examples/bootstrap4/popover.html
[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         isFirefox = ua.indexOf("firefox") > -1,
57         isIE = ua.indexOf("msie") > -1,
58         isIE7 = ua.indexOf("msie 7") > -1,
59         isIE11 = /trident.*rv\:11\./.test(ua),
60         isEdge = ua.indexOf("edge") > -1,
61         isGecko = !isSafari && ua.indexOf("gecko") > -1,
62         isBorderBox = isIE && !isStrict,
63         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
64         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
65         isLinux = (ua.indexOf("linux") != -1),
66         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
67         isIOS = /iphone|ipad/.test(ua),
68         isAndroid = /android/.test(ua),
69         isTouch =  (function() {
70             try {
71                 if (ua.indexOf('chrome') != -1 && ua.indexOf('android') == -1) {
72                     window.addEventListener('touchstart', function __set_has_touch__ () {
73                         Roo.isTouch = true;
74                         window.removeEventListener('touchstart', __set_has_touch__);
75                     });
76                     return false; // no touch on chrome!?
77                 }
78                 document.createEvent("TouchEvent");  
79                 return true;  
80             } catch (e) {  
81                 return false;  
82             } 
83             
84         })();
85     // remove css image flicker
86         if(isIE && !isIE7){
87         try{
88             document.execCommand("BackgroundImageCache", false, true);
89         }catch(e){}
90     }
91     
92     Roo.apply(Roo, {
93         /**
94          * True if the browser is in strict mode
95          * @type Boolean
96          */
97         isStrict : isStrict,
98         /**
99          * True if the page is running over SSL
100          * @type Boolean
101          */
102         isSecure : isSecure,
103         /**
104          * True when the document is fully initialized and ready for action
105          * @type Boolean
106          */
107         isReady : false,
108         /**
109          * Turn on debugging output (currently only the factory uses this)
110          * @type Boolean
111          */
112         
113         debug: false,
114
115         /**
116          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
117          * @type Boolean
118          */
119         enableGarbageCollector : true,
120
121         /**
122          * True to automatically purge event listeners after uncaching an element (defaults to false).
123          * Note: this only happens if enableGarbageCollector is true.
124          * @type Boolean
125          */
126         enableListenerCollection:false,
127
128         /**
129          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
130          * the IE insecure content warning (defaults to javascript:false).
131          * @type String
132          */
133         SSL_SECURE_URL : "javascript:false",
134
135         /**
136          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
137          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
138          * @type String
139          */
140         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
141
142         emptyFn : function(){},
143         
144         /**
145          * Copies all the properties of config to obj if they don't already exist.
146          * @param {Object} obj The receiver of the properties
147          * @param {Object} config The source of the properties
148          * @return {Object} returns obj
149          */
150         applyIf : function(o, c){
151             if(o && c){
152                 for(var p in c){
153                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
154                 }
155             }
156             return o;
157         },
158
159         /**
160          * Applies event listeners to elements by selectors when the document is ready.
161          * The event name is specified with an @ suffix.
162 <pre><code>
163 Roo.addBehaviors({
164    // add a listener for click on all anchors in element with id foo
165    '#foo a@click' : function(e, t){
166        // do something
167    },
168
169    // add the same listener to multiple selectors (separated by comma BEFORE the @)
170    '#foo a, #bar span.some-class@mouseover' : function(){
171        // do something
172    }
173 });
174 </code></pre>
175          * @param {Object} obj The list of behaviors to apply
176          */
177         addBehaviors : function(o){
178             if(!Roo.isReady){
179                 Roo.onReady(function(){
180                     Roo.addBehaviors(o);
181                 });
182                 return;
183             }
184             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
185             for(var b in o){
186                 var parts = b.split('@');
187                 if(parts[1]){ // for Object prototype breakers
188                     var s = parts[0];
189                     if(!cache[s]){
190                         cache[s] = Roo.select(s);
191                     }
192                     cache[s].on(parts[1], o[b]);
193                 }
194             }
195             cache = null;
196         },
197
198         /**
199          * Generates unique ids. If the element already has an id, it is unchanged
200          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
201          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
202          * @return {String} The generated Id.
203          */
204         id : function(el, prefix){
205             prefix = prefix || "roo-gen";
206             el = Roo.getDom(el);
207             var id = prefix + (++idSeed);
208             return el ? (el.id ? el.id : (el.id = id)) : id;
209         },
210          
211        
212         /**
213          * Extends one class with another class and optionally overrides members with the passed literal. This class
214          * also adds the function "override()" to the class that can be used to override
215          * members on an instance.
216          * @param {Object} subclass The class inheriting the functionality
217          * @param {Object} superclass The class being extended
218          * @param {Object} overrides (optional) A literal with members
219          * @method extend
220          */
221         extend : function(){
222             // inline overrides
223             var io = function(o){
224                 for(var m in o){
225                     this[m] = o[m];
226                 }
227             };
228             return function(sb, sp, overrides){
229                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
230                     overrides = sp;
231                     sp = sb;
232                     sb = function(){sp.apply(this, arguments);};
233                 }
234                 var F = function(){}, sbp, spp = sp.prototype;
235                 F.prototype = spp;
236                 sbp = sb.prototype = new F();
237                 sbp.constructor=sb;
238                 sb.superclass=spp;
239                 
240                 if(spp.constructor == Object.prototype.constructor){
241                     spp.constructor=sp;
242                    
243                 }
244                 
245                 sb.override = function(o){
246                     Roo.override(sb, o);
247                 };
248                 sbp.override = io;
249                 Roo.override(sb, overrides);
250                 return sb;
251             };
252         }(),
253
254         /**
255          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
256          * Usage:<pre><code>
257 Roo.override(MyClass, {
258     newMethod1: function(){
259         // etc.
260     },
261     newMethod2: function(foo){
262         // etc.
263     }
264 });
265  </code></pre>
266          * @param {Object} origclass The class to override
267          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
268          * containing one or more methods.
269          * @method override
270          */
271         override : function(origclass, overrides){
272             if(overrides){
273                 var p = origclass.prototype;
274                 for(var method in overrides){
275                     p[method] = overrides[method];
276                 }
277             }
278         },
279         /**
280          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
281          * <pre><code>
282 Roo.namespace('Company', 'Company.data');
283 Company.Widget = function() { ... }
284 Company.data.CustomStore = function(config) { ... }
285 </code></pre>
286          * @param {String} namespace1
287          * @param {String} namespace2
288          * @param {String} etc
289          * @method namespace
290          */
291         namespace : function(){
292             var a=arguments, o=null, i, j, d, rt;
293             for (i=0; i<a.length; ++i) {
294                 d=a[i].split(".");
295                 rt = d[0];
296                 /** eval:var:o */
297                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
298                 for (j=1; j<d.length; ++j) {
299                     o[d[j]]=o[d[j]] || {};
300                     o=o[d[j]];
301                 }
302             }
303         },
304         /**
305          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
306          * <pre><code>
307 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
308 Roo.factory(conf, Roo.data);
309 </code></pre>
310          * @param {String} classname
311          * @param {String} namespace (optional)
312          * @method factory
313          */
314          
315         factory : function(c, ns)
316         {
317             // no xtype, no ns or c.xns - or forced off by c.xns
318             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
319                 return c;
320             }
321             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
322             if (c.constructor == ns[c.xtype]) {// already created...
323                 return c;
324             }
325             if (ns[c.xtype]) {
326                 if (Roo.debug) { Roo.log("Roo.Factory(" + c.xtype + ")"); }
327                 var ret = new ns[c.xtype](c);
328                 ret.xns = false;
329                 return ret;
330             }
331             c.xns = false; // prevent recursion..
332             return c;
333         },
334          /**
335          * Logs to console if it can.
336          *
337          * @param {String|Object} string
338          * @method log
339          */
340         log : function(s)
341         {
342             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
343                 return; // alerT?
344             }
345             
346             console.log(s);
347         },
348         /**
349          * 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.
350          * @param {Object} o
351          * @return {String}
352          */
353         urlEncode : function(o){
354             if(!o){
355                 return "";
356             }
357             var buf = [];
358             for(var key in o){
359                 var ov = o[key], k = Roo.encodeURIComponent(key);
360                 var type = typeof ov;
361                 if(type == 'undefined'){
362                     buf.push(k, "=&");
363                 }else if(type != "function" && type != "object"){
364                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
365                 }else if(ov instanceof Array){
366                     if (ov.length) {
367                             for(var i = 0, len = ov.length; i < len; i++) {
368                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
369                             }
370                         } else {
371                             buf.push(k, "=&");
372                         }
373                 }
374             }
375             buf.pop();
376             return buf.join("");
377         },
378          /**
379          * Safe version of encodeURIComponent
380          * @param {String} data 
381          * @return {String} 
382          */
383         
384         encodeURIComponent : function (data)
385         {
386             try {
387                 return encodeURIComponent(data);
388             } catch(e) {} // should be an uri encode error.
389             
390             if (data == '' || data == null){
391                return '';
392             }
393             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
394             function nibble_to_hex(nibble){
395                 var chars = '0123456789ABCDEF';
396                 return chars.charAt(nibble);
397             }
398             data = data.toString();
399             var buffer = '';
400             for(var i=0; i<data.length; i++){
401                 var c = data.charCodeAt(i);
402                 var bs = new Array();
403                 if (c > 0x10000){
404                         // 4 bytes
405                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
406                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
407                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
408                     bs[3] = 0x80 | (c & 0x3F);
409                 }else if (c > 0x800){
410                          // 3 bytes
411                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
412                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
413                     bs[2] = 0x80 | (c & 0x3F);
414                 }else if (c > 0x80){
415                        // 2 bytes
416                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
417                     bs[1] = 0x80 | (c & 0x3F);
418                 }else{
419                         // 1 byte
420                     bs[0] = c;
421                 }
422                 for(var j=0; j<bs.length; j++){
423                     var b = bs[j];
424                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
425                             + nibble_to_hex(b &0x0F);
426                     buffer += '%'+hex;
427                }
428             }
429             return buffer;    
430              
431         },
432
433         /**
434          * 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]}.
435          * @param {String} string
436          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
437          * @return {Object} A literal with members
438          */
439         urlDecode : function(string, overwrite){
440             if(!string || !string.length){
441                 return {};
442             }
443             var obj = {};
444             var pairs = string.split('&');
445             var pair, name, value;
446             for(var i = 0, len = pairs.length; i < len; i++){
447                 pair = pairs[i].split('=');
448                 name = decodeURIComponent(pair[0]);
449                 value = decodeURIComponent(pair[1]);
450                 if(overwrite !== true){
451                     if(typeof obj[name] == "undefined"){
452                         obj[name] = value;
453                     }else if(typeof obj[name] == "string"){
454                         obj[name] = [obj[name]];
455                         obj[name].push(value);
456                     }else{
457                         obj[name].push(value);
458                     }
459                 }else{
460                     obj[name] = value;
461                 }
462             }
463             return obj;
464         },
465
466         /**
467          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
468          * passed array is not really an array, your function is called once with it.
469          * The supplied function is called with (Object item, Number index, Array allItems).
470          * @param {Array/NodeList/Mixed} array
471          * @param {Function} fn
472          * @param {Object} scope
473          */
474         each : function(array, fn, scope){
475             if(typeof array.length == "undefined" || typeof array == "string"){
476                 array = [array];
477             }
478             for(var i = 0, len = array.length; i < len; i++){
479                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
480             }
481         },
482
483         // deprecated
484         combine : function(){
485             var as = arguments, l = as.length, r = [];
486             for(var i = 0; i < l; i++){
487                 var a = as[i];
488                 if(a instanceof Array){
489                     r = r.concat(a);
490                 }else if(a.length !== undefined && !a.substr){
491                     r = r.concat(Array.prototype.slice.call(a, 0));
492                 }else{
493                     r.push(a);
494                 }
495             }
496             return r;
497         },
498
499         /**
500          * Escapes the passed string for use in a regular expression
501          * @param {String} str
502          * @return {String}
503          */
504         escapeRe : function(s) {
505             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
506         },
507
508         // internal
509         callback : function(cb, scope, args, delay){
510             if(typeof cb == "function"){
511                 if(delay){
512                     cb.defer(delay, scope, args || []);
513                 }else{
514                     cb.apply(scope, args || []);
515                 }
516             }
517         },
518
519         /**
520          * Return the dom node for the passed string (id), dom node, or Roo.Element
521          * @param {String/HTMLElement/Roo.Element} el
522          * @return HTMLElement
523          */
524         getDom : function(el){
525             if(!el){
526                 return null;
527             }
528             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
529         },
530
531         /**
532         * Shorthand for {@link Roo.ComponentMgr#get}
533         * @param {String} id
534         * @return Roo.Component
535         */
536         getCmp : function(id){
537             return Roo.ComponentMgr.get(id);
538         },
539          
540         num : function(v, defaultValue){
541             if(typeof v != 'number'){
542                 return defaultValue;
543             }
544             return v;
545         },
546
547         destroy : function(){
548             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
549                 var as = a[i];
550                 if(as){
551                     if(as.dom){
552                         as.removeAllListeners();
553                         as.remove();
554                         continue;
555                     }
556                     if(typeof as.purgeListeners == 'function'){
557                         as.purgeListeners();
558                     }
559                     if(typeof as.destroy == 'function'){
560                         as.destroy();
561                     }
562                 }
563             }
564         },
565
566         // inpired by a similar function in mootools library
567         /**
568          * Returns the type of object that is passed in. If the object passed in is null or undefined it
569          * return false otherwise it returns one of the following values:<ul>
570          * <li><b>string</b>: If the object passed is a string</li>
571          * <li><b>number</b>: If the object passed is a number</li>
572          * <li><b>boolean</b>: If the object passed is a boolean value</li>
573          * <li><b>function</b>: If the object passed is a function reference</li>
574          * <li><b>object</b>: If the object passed is an object</li>
575          * <li><b>array</b>: If the object passed is an array</li>
576          * <li><b>regexp</b>: If the object passed is a regular expression</li>
577          * <li><b>element</b>: If the object passed is a DOM Element</li>
578          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
579          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
580          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
581          * @param {Mixed} object
582          * @return {String}
583          */
584         type : function(o){
585             if(o === undefined || o === null){
586                 return false;
587             }
588             if(o.htmlElement){
589                 return 'element';
590             }
591             var t = typeof o;
592             if(t == 'object' && o.nodeName) {
593                 switch(o.nodeType) {
594                     case 1: return 'element';
595                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
596                 }
597             }
598             if(t == 'object' || t == 'function') {
599                 switch(o.constructor) {
600                     case Array: return 'array';
601                     case RegExp: return 'regexp';
602                 }
603                 if(typeof o.length == 'number' && typeof o.item == 'function') {
604                     return 'nodelist';
605                 }
606             }
607             return t;
608         },
609
610         /**
611          * Returns true if the passed value is null, undefined or an empty string (optional).
612          * @param {Mixed} value The value to test
613          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
614          * @return {Boolean}
615          */
616         isEmpty : function(v, allowBlank){
617             return v === null || v === undefined || (!allowBlank ? v === '' : false);
618         },
619         
620         /** @type Boolean */
621         isOpera : isOpera,
622         /** @type Boolean */
623         isSafari : isSafari,
624         /** @type Boolean */
625         isFirefox : isFirefox,
626         /** @type Boolean */
627         isIE : isIE,
628         /** @type Boolean */
629         isIE7 : isIE7,
630         /** @type Boolean */
631         isIE11 : isIE11,
632         /** @type Boolean */
633         isEdge : isEdge,
634         /** @type Boolean */
635         isGecko : isGecko,
636         /** @type Boolean */
637         isBorderBox : isBorderBox,
638         /** @type Boolean */
639         isWindows : isWindows,
640         /** @type Boolean */
641         isLinux : isLinux,
642         /** @type Boolean */
643         isMac : isMac,
644         /** @type Boolean */
645         isIOS : isIOS,
646         /** @type Boolean */
647         isAndroid : isAndroid,
648         /** @type Boolean */
649         isTouch : isTouch,
650
651         /**
652          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
653          * you may want to set this to true.
654          * @type Boolean
655          */
656         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
657         
658         
659                 
660         /**
661          * Selects a single element as a Roo Element
662          * This is about as close as you can get to jQuery's $('do crazy stuff')
663          * @param {String} selector The selector/xpath query
664          * @param {Node} root (optional) The start of the query (defaults to document).
665          * @return {Roo.Element}
666          */
667         selectNode : function(selector, root) 
668         {
669             var node = Roo.DomQuery.selectNode(selector,root);
670             return node ? Roo.get(node) : new Roo.Element(false);
671         }
672         
673     });
674
675
676 })();
677
678 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
679                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
680                 "Roo.app", "Roo.ux",
681                 "Roo.bootstrap",
682                 "Roo.bootstrap.dash");
683 /*
684  * Based on:
685  * Ext JS Library 1.1.1
686  * Copyright(c) 2006-2007, Ext JS, LLC.
687  *
688  * Originally Released Under LGPL - original licence link has changed is not relivant.
689  *
690  * Fork - LGPL
691  * <script type="text/javascript">
692  */
693
694 (function() {    
695     // wrappedn so fnCleanup is not in global scope...
696     if(Roo.isIE) {
697         function fnCleanUp() {
698             var p = Function.prototype;
699             delete p.createSequence;
700             delete p.defer;
701             delete p.createDelegate;
702             delete p.createCallback;
703             delete p.createInterceptor;
704
705             window.detachEvent("onunload", fnCleanUp);
706         }
707         window.attachEvent("onunload", fnCleanUp);
708     }
709 })();
710
711
712 /**
713  * @class Function
714  * These functions are available on every Function object (any JavaScript function).
715  */
716 Roo.apply(Function.prototype, {
717      /**
718      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
719      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
720      * Will create a function that is bound to those 2 args.
721      * @return {Function} The new function
722     */
723     createCallback : function(/*args...*/){
724         // make args available, in function below
725         var args = arguments;
726         var method = this;
727         return function() {
728             return method.apply(window, args);
729         };
730     },
731
732     /**
733      * Creates a delegate (callback) that sets the scope to obj.
734      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
735      * Will create a function that is automatically scoped to this.
736      * @param {Object} obj (optional) The object for which the scope is set
737      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
738      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
739      *                                             if a number the args are inserted at the specified position
740      * @return {Function} The new function
741      */
742     createDelegate : function(obj, args, appendArgs){
743         var method = this;
744         return function() {
745             var callArgs = args || arguments;
746             if(appendArgs === true){
747                 callArgs = Array.prototype.slice.call(arguments, 0);
748                 callArgs = callArgs.concat(args);
749             }else if(typeof appendArgs == "number"){
750                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
751                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
752                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
753             }
754             return method.apply(obj || window, callArgs);
755         };
756     },
757
758     /**
759      * Calls this function after the number of millseconds specified.
760      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
761      * @param {Object} obj (optional) The object for which the scope is set
762      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
763      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
764      *                                             if a number the args are inserted at the specified position
765      * @return {Number} The timeout id that can be used with clearTimeout
766      */
767     defer : function(millis, obj, args, appendArgs){
768         var fn = this.createDelegate(obj, args, appendArgs);
769         if(millis){
770             return setTimeout(fn, millis);
771         }
772         fn();
773         return 0;
774     },
775     /**
776      * Create a combined function call sequence of the original function + the passed function.
777      * The resulting function returns the results of the original function.
778      * The passed fcn is called with the parameters of the original function
779      * @param {Function} fcn The function to sequence
780      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
781      * @return {Function} The new function
782      */
783     createSequence : function(fcn, scope){
784         if(typeof fcn != "function"){
785             return this;
786         }
787         var method = this;
788         return function() {
789             var retval = method.apply(this || window, arguments);
790             fcn.apply(scope || this || window, arguments);
791             return retval;
792         };
793     },
794
795     /**
796      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
797      * The resulting function returns the results of the original function.
798      * The passed fcn is called with the parameters of the original function.
799      * @addon
800      * @param {Function} fcn The function to call before the original
801      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
802      * @return {Function} The new function
803      */
804     createInterceptor : function(fcn, scope){
805         if(typeof fcn != "function"){
806             return this;
807         }
808         var method = this;
809         return function() {
810             fcn.target = this;
811             fcn.method = method;
812             if(fcn.apply(scope || this || window, arguments) === false){
813                 return;
814             }
815             return method.apply(this || window, arguments);
816         };
817     }
818 });
819 /*
820  * Based on:
821  * Ext JS Library 1.1.1
822  * Copyright(c) 2006-2007, Ext JS, LLC.
823  *
824  * Originally Released Under LGPL - original licence link has changed is not relivant.
825  *
826  * Fork - LGPL
827  * <script type="text/javascript">
828  */
829
830 Roo.applyIf(String, {
831     
832     /** @scope String */
833     
834     /**
835      * Escapes the passed string for ' and \
836      * @param {String} string The string to escape
837      * @return {String} The escaped string
838      * @static
839      */
840     escape : function(string) {
841         return string.replace(/('|\\)/g, "\\$1");
842     },
843
844     /**
845      * Pads the left side of a string with a specified character.  This is especially useful
846      * for normalizing number and date strings.  Example usage:
847      * <pre><code>
848 var s = String.leftPad('123', 5, '0');
849 // s now contains the string: '00123'
850 </code></pre>
851      * @param {String} string The original string
852      * @param {Number} size The total length of the output string
853      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
854      * @return {String} The padded string
855      * @static
856      */
857     leftPad : function (val, size, ch) {
858         var result = new String(val);
859         if(ch === null || ch === undefined || ch === '') {
860             ch = " ";
861         }
862         while (result.length < size) {
863             result = ch + result;
864         }
865         return result;
866     },
867
868     /**
869      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
870      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
871      * <pre><code>
872 var cls = 'my-class', text = 'Some text';
873 var s = String.format('<div class="{0}">{1}</div>', cls, text);
874 // s now contains the string: '<div class="my-class">Some text</div>'
875 </code></pre>
876      * @param {String} string The tokenized string to be formatted
877      * @param {String} value1 The value to replace token {0}
878      * @param {String} value2 Etc...
879      * @return {String} The formatted string
880      * @static
881      */
882     format : function(format){
883         var args = Array.prototype.slice.call(arguments, 1);
884         return format.replace(/\{(\d+)\}/g, function(m, i){
885             return Roo.util.Format.htmlEncode(args[i]);
886         });
887     }
888   
889     
890 });
891
892 /**
893  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
894  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
895  * they are already different, the first value passed in is returned.  Note that this method returns the new value
896  * but does not change the current string.
897  * <pre><code>
898 // alternate sort directions
899 sort = sort.toggle('ASC', 'DESC');
900
901 // instead of conditional logic:
902 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
903 </code></pre>
904  * @param {String} value The value to compare to the current string
905  * @param {String} other The new value to use if the string already equals the first value passed in
906  * @return {String} The new value
907  */
908  
909 String.prototype.toggle = function(value, other){
910     return this == value ? other : value;
911 };
912
913
914 /**
915   * Remove invalid unicode characters from a string 
916   *
917   * @return {String} The clean string
918   */
919 String.prototype.unicodeClean = function () {
920     return this.replace(/[\s\S]/g,
921         function(character) {
922             if (character.charCodeAt()< 256) {
923               return character;
924            }
925            try {
926                 encodeURIComponent(character);
927            } catch(e) { 
928               return '';
929            }
930            return character;
931         }
932     );
933 };
934   
935 /*
936  * Based on:
937  * Ext JS Library 1.1.1
938  * Copyright(c) 2006-2007, Ext JS, LLC.
939  *
940  * Originally Released Under LGPL - original licence link has changed is not relivant.
941  *
942  * Fork - LGPL
943  * <script type="text/javascript">
944  */
945
946  /**
947  * @class Number
948  */
949 Roo.applyIf(Number.prototype, {
950     /**
951      * Checks whether or not the current number is within a desired range.  If the number is already within the
952      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
953      * exceeded.  Note that this method returns the constrained value but does not change the current number.
954      * @param {Number} min The minimum number in the range
955      * @param {Number} max The maximum number in the range
956      * @return {Number} The constrained value if outside the range, otherwise the current value
957      */
958     constrain : function(min, max){
959         return Math.min(Math.max(this, min), max);
960     }
961 });/*
962  * Based on:
963  * Ext JS Library 1.1.1
964  * Copyright(c) 2006-2007, Ext JS, LLC.
965  *
966  * Originally Released Under LGPL - original licence link has changed is not relivant.
967  *
968  * Fork - LGPL
969  * <script type="text/javascript">
970  */
971  /**
972  * @class Array
973  */
974 Roo.applyIf(Array.prototype, {
975     /**
976      * 
977      * Checks whether or not the specified object exists in the array.
978      * @param {Object} o The object to check for
979      * @return {Number} The index of o in the array (or -1 if it is not found)
980      */
981     indexOf : function(o){
982        for (var i = 0, len = this.length; i < len; i++){
983               if(this[i] == o) { return i; }
984        }
985            return -1;
986     },
987
988     /**
989      * Removes the specified object from the array.  If the object is not found nothing happens.
990      * @param {Object} o The object to remove
991      */
992     remove : function(o){
993        var index = this.indexOf(o);
994        if(index != -1){
995            this.splice(index, 1);
996        }
997     },
998     /**
999      * Map (JS 1.6 compatibility)
1000      * @param {Function} function  to call
1001      */
1002     map : function(fun )
1003     {
1004         var len = this.length >>> 0;
1005         if (typeof fun != "function") {
1006             throw new TypeError();
1007         }
1008         var res = new Array(len);
1009         var thisp = arguments[1];
1010         for (var i = 0; i < len; i++)
1011         {
1012             if (i in this) {
1013                 res[i] = fun.call(thisp, this[i], i, this);
1014             }
1015         }
1016
1017         return res;
1018     },
1019     /**
1020      * equals
1021      * @param {Array} o The array to compare to
1022      * @returns {Boolean} true if the same
1023      */
1024     equals : function(b)
1025     {
1026         // https://stackoverflow.com/questions/3115982/how-to-check-if-two-arrays-are-equal-with-javascript
1027         if (this === b) {
1028             return true;
1029          }
1030         if (b == null) {
1031             return false;
1032         }
1033         if (this.length !== b.length) {
1034             return false;
1035         }
1036       
1037         // sort?? a.sort().equals(b.sort());
1038       
1039         for (var i = 0; i < this.length; ++i) {
1040             if (this[i] !== b[i]) {
1041                 return false;
1042             }
1043         }
1044         return true;
1045     }
1046 });
1047
1048
1049  
1050 /*
1051  * Based on:
1052  * Ext JS Library 1.1.1
1053  * Copyright(c) 2006-2007, Ext JS, LLC.
1054  *
1055  * Originally Released Under LGPL - original licence link has changed is not relivant.
1056  *
1057  * Fork - LGPL
1058  * <script type="text/javascript">
1059  */
1060
1061 /**
1062  * @class Date
1063  *
1064  * The date parsing and format syntax is a subset of
1065  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
1066  * supported will provide results equivalent to their PHP versions.
1067  *
1068  * Following is the list of all currently supported formats:
1069  *<pre>
1070 Sample date:
1071 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1072
1073 Format  Output      Description
1074 ------  ----------  --------------------------------------------------------------
1075   d      10         Day of the month, 2 digits with leading zeros
1076   D      Wed        A textual representation of a day, three letters
1077   j      10         Day of the month without leading zeros
1078   l      Wednesday  A full textual representation of the day of the week
1079   S      th         English ordinal day of month suffix, 2 chars (use with j)
1080   w      3          Numeric representation of the day of the week
1081   z      9          The julian date, or day of the year (0-365)
1082   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1083   F      January    A full textual representation of the month
1084   m      01         Numeric representation of a month, with leading zeros
1085   M      Jan        Month name abbreviation, three letters
1086   n      1          Numeric representation of a month, without leading zeros
1087   t      31         Number of days in the given month
1088   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1089   Y      2007       A full numeric representation of a year, 4 digits
1090   y      07         A two digit representation of a year
1091   a      pm         Lowercase Ante meridiem and Post meridiem
1092   A      PM         Uppercase Ante meridiem and Post meridiem
1093   g      3          12-hour format of an hour without leading zeros
1094   G      15         24-hour format of an hour without leading zeros
1095   h      03         12-hour format of an hour with leading zeros
1096   H      15         24-hour format of an hour with leading zeros
1097   i      05         Minutes with leading zeros
1098   s      01         Seconds, with leading zeros
1099   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1100   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1101   T      CST        Timezone setting of the machine running the code
1102   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1103 </pre>
1104  *
1105  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1106  * <pre><code>
1107 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1108 document.write(dt.format('Y-m-d'));                         //2007-01-10
1109 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1110 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
1111  </code></pre>
1112  *
1113  * Here are some standard date/time patterns that you might find helpful.  They
1114  * are not part of the source of Date.js, but to use them you can simply copy this
1115  * block of code into any script that is included after Date.js and they will also become
1116  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1117  * <pre><code>
1118 Date.patterns = {
1119     ISO8601Long:"Y-m-d H:i:s",
1120     ISO8601Short:"Y-m-d",
1121     ShortDate: "n/j/Y",
1122     LongDate: "l, F d, Y",
1123     FullDateTime: "l, F d, Y g:i:s A",
1124     MonthDay: "F d",
1125     ShortTime: "g:i A",
1126     LongTime: "g:i:s A",
1127     SortableDateTime: "Y-m-d\\TH:i:s",
1128     UniversalSortableDateTime: "Y-m-d H:i:sO",
1129     YearMonth: "F, Y"
1130 };
1131 </code></pre>
1132  *
1133  * Example usage:
1134  * <pre><code>
1135 var dt = new Date();
1136 document.write(dt.format(Date.patterns.ShortDate));
1137  </code></pre>
1138  */
1139
1140 /*
1141  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1142  * They generate precompiled functions from date formats instead of parsing and
1143  * processing the pattern every time you format a date.  These functions are available
1144  * on every Date object (any javascript function).
1145  *
1146  * The original article and download are here:
1147  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1148  *
1149  */
1150  
1151  
1152  // was in core
1153 /**
1154  Returns the number of milliseconds between this date and date
1155  @param {Date} date (optional) Defaults to now
1156  @return {Number} The diff in milliseconds
1157  @member Date getElapsed
1158  */
1159 Date.prototype.getElapsed = function(date) {
1160         return Math.abs((date || new Date()).getTime()-this.getTime());
1161 };
1162 // was in date file..
1163
1164
1165 // private
1166 Date.parseFunctions = {count:0};
1167 // private
1168 Date.parseRegexes = [];
1169 // private
1170 Date.formatFunctions = {count:0};
1171
1172 // private
1173 Date.prototype.dateFormat = function(format) {
1174     if (Date.formatFunctions[format] == null) {
1175         Date.createNewFormat(format);
1176     }
1177     var func = Date.formatFunctions[format];
1178     return this[func]();
1179 };
1180
1181
1182 /**
1183  * Formats a date given the supplied format string
1184  * @param {String} format The format string
1185  * @return {String} The formatted date
1186  * @method
1187  */
1188 Date.prototype.format = Date.prototype.dateFormat;
1189
1190 // private
1191 Date.createNewFormat = function(format) {
1192     var funcName = "format" + Date.formatFunctions.count++;
1193     Date.formatFunctions[format] = funcName;
1194     var code = "Date.prototype." + funcName + " = function(){return ";
1195     var special = false;
1196     var ch = '';
1197     for (var i = 0; i < format.length; ++i) {
1198         ch = format.charAt(i);
1199         if (!special && ch == "\\") {
1200             special = true;
1201         }
1202         else if (special) {
1203             special = false;
1204             code += "'" + String.escape(ch) + "' + ";
1205         }
1206         else {
1207             code += Date.getFormatCode(ch);
1208         }
1209     }
1210     /** eval:var:zzzzzzzzzzzzz */
1211     eval(code.substring(0, code.length - 3) + ";}");
1212 };
1213
1214 // private
1215 Date.getFormatCode = function(character) {
1216     switch (character) {
1217     case "d":
1218         return "String.leftPad(this.getDate(), 2, '0') + ";
1219     case "D":
1220         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1221     case "j":
1222         return "this.getDate() + ";
1223     case "l":
1224         return "Date.dayNames[this.getDay()] + ";
1225     case "S":
1226         return "this.getSuffix() + ";
1227     case "w":
1228         return "this.getDay() + ";
1229     case "z":
1230         return "this.getDayOfYear() + ";
1231     case "W":
1232         return "this.getWeekOfYear() + ";
1233     case "F":
1234         return "Date.monthNames[this.getMonth()] + ";
1235     case "m":
1236         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1237     case "M":
1238         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1239     case "n":
1240         return "(this.getMonth() + 1) + ";
1241     case "t":
1242         return "this.getDaysInMonth() + ";
1243     case "L":
1244         return "(this.isLeapYear() ? 1 : 0) + ";
1245     case "Y":
1246         return "this.getFullYear() + ";
1247     case "y":
1248         return "('' + this.getFullYear()).substring(2, 4) + ";
1249     case "a":
1250         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1251     case "A":
1252         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1253     case "g":
1254         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1255     case "G":
1256         return "this.getHours() + ";
1257     case "h":
1258         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1259     case "H":
1260         return "String.leftPad(this.getHours(), 2, '0') + ";
1261     case "i":
1262         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1263     case "s":
1264         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1265     case "O":
1266         return "this.getGMTOffset() + ";
1267     case "P":
1268         return "this.getGMTColonOffset() + ";
1269     case "T":
1270         return "this.getTimezone() + ";
1271     case "Z":
1272         return "(this.getTimezoneOffset() * -60) + ";
1273     default:
1274         return "'" + String.escape(character) + "' + ";
1275     }
1276 };
1277
1278 /**
1279  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1280  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1281  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1282  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1283  * string or the parse operation will fail.
1284  * Example Usage:
1285 <pre><code>
1286 //dt = Fri May 25 2007 (current date)
1287 var dt = new Date();
1288
1289 //dt = Thu May 25 2006 (today's month/day in 2006)
1290 dt = Date.parseDate("2006", "Y");
1291
1292 //dt = Sun Jan 15 2006 (all date parts specified)
1293 dt = Date.parseDate("2006-1-15", "Y-m-d");
1294
1295 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1296 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1297 </code></pre>
1298  * @param {String} input The unparsed date as a string
1299  * @param {String} format The format the date is in
1300  * @return {Date} The parsed date
1301  * @static
1302  */
1303 Date.parseDate = function(input, format) {
1304     if (Date.parseFunctions[format] == null) {
1305         Date.createParser(format);
1306     }
1307     var func = Date.parseFunctions[format];
1308     return Date[func](input);
1309 };
1310 /**
1311  * @private
1312  */
1313
1314 Date.createParser = function(format) {
1315     var funcName = "parse" + Date.parseFunctions.count++;
1316     var regexNum = Date.parseRegexes.length;
1317     var currentGroup = 1;
1318     Date.parseFunctions[format] = funcName;
1319
1320     var code = "Date." + funcName + " = function(input){\n"
1321         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1322         + "var d = new Date();\n"
1323         + "y = d.getFullYear();\n"
1324         + "m = d.getMonth();\n"
1325         + "d = d.getDate();\n"
1326         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1327         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1328         + "if (results && results.length > 0) {";
1329     var regex = "";
1330
1331     var special = false;
1332     var ch = '';
1333     for (var i = 0; i < format.length; ++i) {
1334         ch = format.charAt(i);
1335         if (!special && ch == "\\") {
1336             special = true;
1337         }
1338         else if (special) {
1339             special = false;
1340             regex += String.escape(ch);
1341         }
1342         else {
1343             var obj = Date.formatCodeToRegex(ch, currentGroup);
1344             currentGroup += obj.g;
1345             regex += obj.s;
1346             if (obj.g && obj.c) {
1347                 code += obj.c;
1348             }
1349         }
1350     }
1351
1352     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1353         + "{v = new Date(y, m, d, h, i, s);}\n"
1354         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1355         + "{v = new Date(y, m, d, h, i);}\n"
1356         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1357         + "{v = new Date(y, m, d, h);}\n"
1358         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1359         + "{v = new Date(y, m, d);}\n"
1360         + "else if (y >= 0 && m >= 0)\n"
1361         + "{v = new Date(y, m);}\n"
1362         + "else if (y >= 0)\n"
1363         + "{v = new Date(y);}\n"
1364         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1365         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1366         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1367         + ";}";
1368
1369     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1370     /** eval:var:zzzzzzzzzzzzz */
1371     eval(code);
1372 };
1373
1374 // private
1375 Date.formatCodeToRegex = function(character, currentGroup) {
1376     switch (character) {
1377     case "D":
1378         return {g:0,
1379         c:null,
1380         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1381     case "j":
1382         return {g:1,
1383             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1384             s:"(\\d{1,2})"}; // day of month without leading zeroes
1385     case "d":
1386         return {g:1,
1387             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1388             s:"(\\d{2})"}; // day of month with leading zeroes
1389     case "l":
1390         return {g:0,
1391             c:null,
1392             s:"(?:" + Date.dayNames.join("|") + ")"};
1393     case "S":
1394         return {g:0,
1395             c:null,
1396             s:"(?:st|nd|rd|th)"};
1397     case "w":
1398         return {g:0,
1399             c:null,
1400             s:"\\d"};
1401     case "z":
1402         return {g:0,
1403             c:null,
1404             s:"(?:\\d{1,3})"};
1405     case "W":
1406         return {g:0,
1407             c:null,
1408             s:"(?:\\d{2})"};
1409     case "F":
1410         return {g:1,
1411             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1412             s:"(" + Date.monthNames.join("|") + ")"};
1413     case "M":
1414         return {g:1,
1415             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1416             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1417     case "n":
1418         return {g:1,
1419             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1420             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1421     case "m":
1422         return {g:1,
1423             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1424             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1425     case "t":
1426         return {g:0,
1427             c:null,
1428             s:"\\d{1,2}"};
1429     case "L":
1430         return {g:0,
1431             c:null,
1432             s:"(?:1|0)"};
1433     case "Y":
1434         return {g:1,
1435             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1436             s:"(\\d{4})"};
1437     case "y":
1438         return {g:1,
1439             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1440                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1441             s:"(\\d{1,2})"};
1442     case "a":
1443         return {g:1,
1444             c:"if (results[" + currentGroup + "] == 'am') {\n"
1445                 + "if (h == 12) { h = 0; }\n"
1446                 + "} else { if (h < 12) { h += 12; }}",
1447             s:"(am|pm)"};
1448     case "A":
1449         return {g:1,
1450             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1451                 + "if (h == 12) { h = 0; }\n"
1452                 + "} else { if (h < 12) { h += 12; }}",
1453             s:"(AM|PM)"};
1454     case "g":
1455     case "G":
1456         return {g:1,
1457             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1458             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1459     case "h":
1460     case "H":
1461         return {g:1,
1462             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1463             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1464     case "i":
1465         return {g:1,
1466             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1467             s:"(\\d{2})"};
1468     case "s":
1469         return {g:1,
1470             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1471             s:"(\\d{2})"};
1472     case "O":
1473         return {g:1,
1474             c:[
1475                 "o = results[", currentGroup, "];\n",
1476                 "var sn = o.substring(0,1);\n", // get + / - sign
1477                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1478                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1479                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1480                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1481             ].join(""),
1482             s:"([+\-]\\d{2,4})"};
1483     
1484     
1485     case "P":
1486         return {g:1,
1487                 c:[
1488                    "o = results[", currentGroup, "];\n",
1489                    "var sn = o.substring(0,1);\n",
1490                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1491                    "var mn = o.substring(4,6) % 60;\n",
1492                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1493                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1494             ].join(""),
1495             s:"([+\-]\\d{4})"};
1496     case "T":
1497         return {g:0,
1498             c:null,
1499             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1500     case "Z":
1501         return {g:1,
1502             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1503                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1504             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1505     default:
1506         return {g:0,
1507             c:null,
1508             s:String.escape(character)};
1509     }
1510 };
1511
1512 /**
1513  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1514  * @return {String} The abbreviated timezone name (e.g. 'CST')
1515  */
1516 Date.prototype.getTimezone = function() {
1517     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1518 };
1519
1520 /**
1521  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1522  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1523  */
1524 Date.prototype.getGMTOffset = function() {
1525     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1526         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1527         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1528 };
1529
1530 /**
1531  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1532  * @return {String} 2-characters representing hours and 2-characters representing minutes
1533  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1534  */
1535 Date.prototype.getGMTColonOffset = function() {
1536         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1537                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1538                 + ":"
1539                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1540 }
1541
1542 /**
1543  * Get the numeric day number of the year, adjusted for leap year.
1544  * @return {Number} 0 through 364 (365 in leap years)
1545  */
1546 Date.prototype.getDayOfYear = function() {
1547     var num = 0;
1548     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1549     for (var i = 0; i < this.getMonth(); ++i) {
1550         num += Date.daysInMonth[i];
1551     }
1552     return num + this.getDate() - 1;
1553 };
1554
1555 /**
1556  * Get the string representation of the numeric week number of the year
1557  * (equivalent to the format specifier 'W').
1558  * @return {String} '00' through '52'
1559  */
1560 Date.prototype.getWeekOfYear = function() {
1561     // Skip to Thursday of this week
1562     var now = this.getDayOfYear() + (4 - this.getDay());
1563     // Find the first Thursday of the year
1564     var jan1 = new Date(this.getFullYear(), 0, 1);
1565     var then = (7 - jan1.getDay() + 4);
1566     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1567 };
1568
1569 /**
1570  * Whether or not the current date is in a leap year.
1571  * @return {Boolean} True if the current date is in a leap year, else false
1572  */
1573 Date.prototype.isLeapYear = function() {
1574     var year = this.getFullYear();
1575     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1576 };
1577
1578 /**
1579  * Get the first day of the current month, adjusted for leap year.  The returned value
1580  * is the numeric day index within the week (0-6) which can be used in conjunction with
1581  * the {@link #monthNames} array to retrieve the textual day name.
1582  * Example:
1583  *<pre><code>
1584 var dt = new Date('1/10/2007');
1585 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1586 </code></pre>
1587  * @return {Number} The day number (0-6)
1588  */
1589 Date.prototype.getFirstDayOfMonth = function() {
1590     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1591     return (day < 0) ? (day + 7) : day;
1592 };
1593
1594 /**
1595  * Get the last day of the current month, adjusted for leap year.  The returned value
1596  * is the numeric day index within the week (0-6) which can be used in conjunction with
1597  * the {@link #monthNames} array to retrieve the textual day name.
1598  * Example:
1599  *<pre><code>
1600 var dt = new Date('1/10/2007');
1601 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1602 </code></pre>
1603  * @return {Number} The day number (0-6)
1604  */
1605 Date.prototype.getLastDayOfMonth = function() {
1606     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1607     return (day < 0) ? (day + 7) : day;
1608 };
1609
1610
1611 /**
1612  * Get the first date of this date's month
1613  * @return {Date}
1614  */
1615 Date.prototype.getFirstDateOfMonth = function() {
1616     return new Date(this.getFullYear(), this.getMonth(), 1);
1617 };
1618
1619 /**
1620  * Get the last date of this date's month
1621  * @return {Date}
1622  */
1623 Date.prototype.getLastDateOfMonth = function() {
1624     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1625 };
1626 /**
1627  * Get the number of days in the current month, adjusted for leap year.
1628  * @return {Number} The number of days in the month
1629  */
1630 Date.prototype.getDaysInMonth = function() {
1631     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1632     return Date.daysInMonth[this.getMonth()];
1633 };
1634
1635 /**
1636  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1637  * @return {String} 'st, 'nd', 'rd' or 'th'
1638  */
1639 Date.prototype.getSuffix = function() {
1640     switch (this.getDate()) {
1641         case 1:
1642         case 21:
1643         case 31:
1644             return "st";
1645         case 2:
1646         case 22:
1647             return "nd";
1648         case 3:
1649         case 23:
1650             return "rd";
1651         default:
1652             return "th";
1653     }
1654 };
1655
1656 // private
1657 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1658
1659 /**
1660  * An array of textual month names.
1661  * Override these values for international dates, for example...
1662  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1663  * @type Array
1664  * @static
1665  */
1666 Date.monthNames =
1667    ["January",
1668     "February",
1669     "March",
1670     "April",
1671     "May",
1672     "June",
1673     "July",
1674     "August",
1675     "September",
1676     "October",
1677     "November",
1678     "December"];
1679
1680 /**
1681  * An array of textual day names.
1682  * Override these values for international dates, for example...
1683  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1684  * @type Array
1685  * @static
1686  */
1687 Date.dayNames =
1688    ["Sunday",
1689     "Monday",
1690     "Tuesday",
1691     "Wednesday",
1692     "Thursday",
1693     "Friday",
1694     "Saturday"];
1695
1696 // private
1697 Date.y2kYear = 50;
1698 // private
1699 Date.monthNumbers = {
1700     Jan:0,
1701     Feb:1,
1702     Mar:2,
1703     Apr:3,
1704     May:4,
1705     Jun:5,
1706     Jul:6,
1707     Aug:7,
1708     Sep:8,
1709     Oct:9,
1710     Nov:10,
1711     Dec:11};
1712
1713 /**
1714  * Creates and returns a new Date instance with the exact same date value as the called instance.
1715  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1716  * variable will also be changed.  When the intention is to create a new variable that will not
1717  * modify the original instance, you should create a clone.
1718  *
1719  * Example of correctly cloning a date:
1720  * <pre><code>
1721 //wrong way:
1722 var orig = new Date('10/1/2006');
1723 var copy = orig;
1724 copy.setDate(5);
1725 document.write(orig);  //returns 'Thu Oct 05 2006'!
1726
1727 //correct way:
1728 var orig = new Date('10/1/2006');
1729 var copy = orig.clone();
1730 copy.setDate(5);
1731 document.write(orig);  //returns 'Thu Oct 01 2006'
1732 </code></pre>
1733  * @return {Date} The new Date instance
1734  */
1735 Date.prototype.clone = function() {
1736         return new Date(this.getTime());
1737 };
1738
1739 /**
1740  * Clears any time information from this date
1741  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1742  @return {Date} this or the clone
1743  */
1744 Date.prototype.clearTime = function(clone){
1745     if(clone){
1746         return this.clone().clearTime();
1747     }
1748     this.setHours(0);
1749     this.setMinutes(0);
1750     this.setSeconds(0);
1751     this.setMilliseconds(0);
1752     return this;
1753 };
1754
1755 // private
1756 // safari setMonth is broken -- check that this is only donw once...
1757 if(Roo.isSafari && typeof(Date.brokenSetMonth) == 'undefined'){
1758     Date.brokenSetMonth = Date.prototype.setMonth;
1759         Date.prototype.setMonth = function(num){
1760                 if(num <= -1){
1761                         var n = Math.ceil(-num);
1762                         var back_year = Math.ceil(n/12);
1763                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1764                         this.setFullYear(this.getFullYear() - back_year);
1765                         return Date.brokenSetMonth.call(this, month);
1766                 } else {
1767                         return Date.brokenSetMonth.apply(this, arguments);
1768                 }
1769         };
1770 }
1771
1772 /** Date interval constant 
1773 * @static 
1774 * @type String */
1775 Date.MILLI = "ms";
1776 /** Date interval constant 
1777 * @static 
1778 * @type String */
1779 Date.SECOND = "s";
1780 /** Date interval constant 
1781 * @static 
1782 * @type String */
1783 Date.MINUTE = "mi";
1784 /** Date interval constant 
1785 * @static 
1786 * @type String */
1787 Date.HOUR = "h";
1788 /** Date interval constant 
1789 * @static 
1790 * @type String */
1791 Date.DAY = "d";
1792 /** Date interval constant 
1793 * @static 
1794 * @type String */
1795 Date.MONTH = "mo";
1796 /** Date interval constant 
1797 * @static 
1798 * @type String */
1799 Date.YEAR = "y";
1800
1801 /**
1802  * Provides a convenient method of performing basic date arithmetic.  This method
1803  * does not modify the Date instance being called - it creates and returns
1804  * a new Date instance containing the resulting date value.
1805  *
1806  * Examples:
1807  * <pre><code>
1808 //Basic usage:
1809 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1810 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1811
1812 //Negative values will subtract correctly:
1813 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1814 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1815
1816 //You can even chain several calls together in one line!
1817 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1818 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1819  </code></pre>
1820  *
1821  * @param {String} interval   A valid date interval enum value
1822  * @param {Number} value      The amount to add to the current date
1823  * @return {Date} The new Date instance
1824  */
1825 Date.prototype.add = function(interval, value){
1826   var d = this.clone();
1827   if (!interval || value === 0) { return d; }
1828   switch(interval.toLowerCase()){
1829     case Date.MILLI:
1830       d.setMilliseconds(this.getMilliseconds() + value);
1831       break;
1832     case Date.SECOND:
1833       d.setSeconds(this.getSeconds() + value);
1834       break;
1835     case Date.MINUTE:
1836       d.setMinutes(this.getMinutes() + value);
1837       break;
1838     case Date.HOUR:
1839       d.setHours(this.getHours() + value);
1840       break;
1841     case Date.DAY:
1842       d.setDate(this.getDate() + value);
1843       break;
1844     case Date.MONTH:
1845       var day = this.getDate();
1846       if(day > 28){
1847           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1848       }
1849       d.setDate(day);
1850       d.setMonth(this.getMonth() + value);
1851       break;
1852     case Date.YEAR:
1853       d.setFullYear(this.getFullYear() + value);
1854       break;
1855   }
1856   return d;
1857 };
1858 /*
1859  * Based on:
1860  * Ext JS Library 1.1.1
1861  * Copyright(c) 2006-2007, Ext JS, LLC.
1862  *
1863  * Originally Released Under LGPL - original licence link has changed is not relivant.
1864  *
1865  * Fork - LGPL
1866  * <script type="text/javascript">
1867  */
1868
1869 /**
1870  * @class Roo.lib.Dom
1871  * @static
1872  * 
1873  * Dom utils (from YIU afaik)
1874  * 
1875  **/
1876 Roo.lib.Dom = {
1877     /**
1878      * Get the view width
1879      * @param {Boolean} full True will get the full document, otherwise it's the view width
1880      * @return {Number} The width
1881      */
1882      
1883     getViewWidth : function(full) {
1884         return full ? this.getDocumentWidth() : this.getViewportWidth();
1885     },
1886     /**
1887      * Get the view height
1888      * @param {Boolean} full True will get the full document, otherwise it's the view height
1889      * @return {Number} The height
1890      */
1891     getViewHeight : function(full) {
1892         return full ? this.getDocumentHeight() : this.getViewportHeight();
1893     },
1894
1895     getDocumentHeight: function() {
1896         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1897         return Math.max(scrollHeight, this.getViewportHeight());
1898     },
1899
1900     getDocumentWidth: function() {
1901         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1902         return Math.max(scrollWidth, this.getViewportWidth());
1903     },
1904
1905     getViewportHeight: function() {
1906         var height = self.innerHeight;
1907         var mode = document.compatMode;
1908
1909         if ((mode || Roo.isIE) && !Roo.isOpera) {
1910             height = (mode == "CSS1Compat") ?
1911                      document.documentElement.clientHeight :
1912                      document.body.clientHeight;
1913         }
1914
1915         return height;
1916     },
1917
1918     getViewportWidth: function() {
1919         var width = self.innerWidth;
1920         var mode = document.compatMode;
1921
1922         if (mode || Roo.isIE) {
1923             width = (mode == "CSS1Compat") ?
1924                     document.documentElement.clientWidth :
1925                     document.body.clientWidth;
1926         }
1927         return width;
1928     },
1929
1930     isAncestor : function(p, c) {
1931         p = Roo.getDom(p);
1932         c = Roo.getDom(c);
1933         if (!p || !c) {
1934             return false;
1935         }
1936
1937         if (p.contains && !Roo.isSafari) {
1938             return p.contains(c);
1939         } else if (p.compareDocumentPosition) {
1940             return !!(p.compareDocumentPosition(c) & 16);
1941         } else {
1942             var parent = c.parentNode;
1943             while (parent) {
1944                 if (parent == p) {
1945                     return true;
1946                 }
1947                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1948                     return false;
1949                 }
1950                 parent = parent.parentNode;
1951             }
1952             return false;
1953         }
1954     },
1955
1956     getRegion : function(el) {
1957         return Roo.lib.Region.getRegion(el);
1958     },
1959
1960     getY : function(el) {
1961         return this.getXY(el)[1];
1962     },
1963
1964     getX : function(el) {
1965         return this.getXY(el)[0];
1966     },
1967
1968     getXY : function(el) {
1969         var p, pe, b, scroll, bd = document.body;
1970         el = Roo.getDom(el);
1971         var fly = Roo.lib.AnimBase.fly;
1972         if (el.getBoundingClientRect) {
1973             b = el.getBoundingClientRect();
1974             scroll = fly(document).getScroll();
1975             return [b.left + scroll.left, b.top + scroll.top];
1976         }
1977         var x = 0, y = 0;
1978
1979         p = el;
1980
1981         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1982
1983         while (p) {
1984
1985             x += p.offsetLeft;
1986             y += p.offsetTop;
1987
1988             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1989                 hasAbsolute = true;
1990             }
1991
1992             if (Roo.isGecko) {
1993                 pe = fly(p);
1994
1995                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1996                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1997
1998
1999                 x += bl;
2000                 y += bt;
2001
2002
2003                 if (p != el && pe.getStyle('overflow') != 'visible') {
2004                     x += bl;
2005                     y += bt;
2006                 }
2007             }
2008             p = p.offsetParent;
2009         }
2010
2011         if (Roo.isSafari && hasAbsolute) {
2012             x -= bd.offsetLeft;
2013             y -= bd.offsetTop;
2014         }
2015
2016         if (Roo.isGecko && !hasAbsolute) {
2017             var dbd = fly(bd);
2018             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
2019             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
2020         }
2021
2022         p = el.parentNode;
2023         while (p && p != bd) {
2024             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
2025                 x -= p.scrollLeft;
2026                 y -= p.scrollTop;
2027             }
2028             p = p.parentNode;
2029         }
2030         return [x, y];
2031     },
2032  
2033   
2034
2035
2036     setXY : function(el, xy) {
2037         el = Roo.fly(el, '_setXY');
2038         el.position();
2039         var pts = el.translatePoints(xy);
2040         if (xy[0] !== false) {
2041             el.dom.style.left = pts.left + "px";
2042         }
2043         if (xy[1] !== false) {
2044             el.dom.style.top = pts.top + "px";
2045         }
2046     },
2047
2048     setX : function(el, x) {
2049         this.setXY(el, [x, false]);
2050     },
2051
2052     setY : function(el, y) {
2053         this.setXY(el, [false, y]);
2054     }
2055 };
2056 /*
2057  * Portions of this file are based on pieces of Yahoo User Interface Library
2058  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2059  * YUI licensed under the BSD License:
2060  * http://developer.yahoo.net/yui/license.txt
2061  * <script type="text/javascript">
2062  *
2063  */
2064
2065 Roo.lib.Event = function() {
2066     var loadComplete = false;
2067     var listeners = [];
2068     var unloadListeners = [];
2069     var retryCount = 0;
2070     var onAvailStack = [];
2071     var counter = 0;
2072     var lastError = null;
2073
2074     return {
2075         POLL_RETRYS: 200,
2076         POLL_INTERVAL: 20,
2077         EL: 0,
2078         TYPE: 1,
2079         FN: 2,
2080         WFN: 3,
2081         OBJ: 3,
2082         ADJ_SCOPE: 4,
2083         _interval: null,
2084
2085         startInterval: function() {
2086             if (!this._interval) {
2087                 var self = this;
2088                 var callback = function() {
2089                     self._tryPreloadAttach();
2090                 };
2091                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2092
2093             }
2094         },
2095
2096         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2097             onAvailStack.push({ id:         p_id,
2098                 fn:         p_fn,
2099                 obj:        p_obj,
2100                 override:   p_override,
2101                 checkReady: false    });
2102
2103             retryCount = this.POLL_RETRYS;
2104             this.startInterval();
2105         },
2106
2107
2108         addListener: function(el, eventName, fn) {
2109             el = Roo.getDom(el);
2110             if (!el || !fn) {
2111                 return false;
2112             }
2113
2114             if ("unload" == eventName) {
2115                 unloadListeners[unloadListeners.length] =
2116                 [el, eventName, fn];
2117                 return true;
2118             }
2119
2120             var wrappedFn = function(e) {
2121                 return fn(Roo.lib.Event.getEvent(e));
2122             };
2123
2124             var li = [el, eventName, fn, wrappedFn];
2125
2126             var index = listeners.length;
2127             listeners[index] = li;
2128
2129             this.doAdd(el, eventName, wrappedFn, false);
2130             return true;
2131
2132         },
2133
2134
2135         removeListener: function(el, eventName, fn) {
2136             var i, len;
2137
2138             el = Roo.getDom(el);
2139
2140             if(!fn) {
2141                 return this.purgeElement(el, false, eventName);
2142             }
2143
2144
2145             if ("unload" == eventName) {
2146
2147                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2148                     var li = unloadListeners[i];
2149                     if (li &&
2150                         li[0] == el &&
2151                         li[1] == eventName &&
2152                         li[2] == fn) {
2153                         unloadListeners.splice(i, 1);
2154                         return true;
2155                     }
2156                 }
2157
2158                 return false;
2159             }
2160
2161             var cacheItem = null;
2162
2163
2164             var index = arguments[3];
2165
2166             if ("undefined" == typeof index) {
2167                 index = this._getCacheIndex(el, eventName, fn);
2168             }
2169
2170             if (index >= 0) {
2171                 cacheItem = listeners[index];
2172             }
2173
2174             if (!el || !cacheItem) {
2175                 return false;
2176             }
2177
2178             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2179
2180             delete listeners[index][this.WFN];
2181             delete listeners[index][this.FN];
2182             listeners.splice(index, 1);
2183
2184             return true;
2185
2186         },
2187
2188
2189         getTarget: function(ev, resolveTextNode) {
2190             ev = ev.browserEvent || ev;
2191             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2192             var t = ev.target || ev.srcElement;
2193             return this.resolveTextNode(t);
2194         },
2195
2196
2197         resolveTextNode: function(node) {
2198             if (Roo.isSafari && node && 3 == node.nodeType) {
2199                 return node.parentNode;
2200             } else {
2201                 return node;
2202             }
2203         },
2204
2205
2206         getPageX: function(ev) {
2207             ev = ev.browserEvent || ev;
2208             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2209             var x = ev.pageX;
2210             if (!x && 0 !== x) {
2211                 x = ev.clientX || 0;
2212
2213                 if (Roo.isIE) {
2214                     x += this.getScroll()[1];
2215                 }
2216             }
2217
2218             return x;
2219         },
2220
2221
2222         getPageY: function(ev) {
2223             ev = ev.browserEvent || ev;
2224             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2225             var y = ev.pageY;
2226             if (!y && 0 !== y) {
2227                 y = ev.clientY || 0;
2228
2229                 if (Roo.isIE) {
2230                     y += this.getScroll()[0];
2231                 }
2232             }
2233
2234
2235             return y;
2236         },
2237
2238
2239         getXY: function(ev) {
2240             ev = ev.browserEvent || ev;
2241             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2242             return [this.getPageX(ev), this.getPageY(ev)];
2243         },
2244
2245
2246         getRelatedTarget: function(ev) {
2247             ev = ev.browserEvent || ev;
2248             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2249             var t = ev.relatedTarget;
2250             if (!t) {
2251                 if (ev.type == "mouseout") {
2252                     t = ev.toElement;
2253                 } else if (ev.type == "mouseover") {
2254                     t = ev.fromElement;
2255                 }
2256             }
2257
2258             return this.resolveTextNode(t);
2259         },
2260
2261
2262         getTime: function(ev) {
2263             ev = ev.browserEvent || ev;
2264             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2265             if (!ev.time) {
2266                 var t = new Date().getTime();
2267                 try {
2268                     ev.time = t;
2269                 } catch(ex) {
2270                     this.lastError = ex;
2271                     return t;
2272                 }
2273             }
2274
2275             return ev.time;
2276         },
2277
2278
2279         stopEvent: function(ev) {
2280             this.stopPropagation(ev);
2281             this.preventDefault(ev);
2282         },
2283
2284
2285         stopPropagation: function(ev) {
2286             ev = ev.browserEvent || ev;
2287             if (ev.stopPropagation) {
2288                 ev.stopPropagation();
2289             } else {
2290                 ev.cancelBubble = true;
2291             }
2292         },
2293
2294
2295         preventDefault: function(ev) {
2296             ev = ev.browserEvent || ev;
2297             if(ev.preventDefault) {
2298                 ev.preventDefault();
2299             } else {
2300                 ev.returnValue = false;
2301             }
2302         },
2303
2304
2305         getEvent: function(e) {
2306             var ev = e || window.event;
2307             if (!ev) {
2308                 var c = this.getEvent.caller;
2309                 while (c) {
2310                     ev = c.arguments[0];
2311                     if (ev && Event == ev.constructor) {
2312                         break;
2313                     }
2314                     c = c.caller;
2315                 }
2316             }
2317             return ev;
2318         },
2319
2320
2321         getCharCode: function(ev) {
2322             ev = ev.browserEvent || ev;
2323             return ev.charCode || ev.keyCode || 0;
2324         },
2325
2326
2327         _getCacheIndex: function(el, eventName, fn) {
2328             for (var i = 0,len = listeners.length; i < len; ++i) {
2329                 var li = listeners[i];
2330                 if (li &&
2331                     li[this.FN] == fn &&
2332                     li[this.EL] == el &&
2333                     li[this.TYPE] == eventName) {
2334                     return i;
2335                 }
2336             }
2337
2338             return -1;
2339         },
2340
2341
2342         elCache: {},
2343
2344
2345         getEl: function(id) {
2346             return document.getElementById(id);
2347         },
2348
2349
2350         clearCache: function() {
2351         },
2352
2353
2354         _load: function(e) {
2355             loadComplete = true;
2356             var EU = Roo.lib.Event;
2357
2358
2359             if (Roo.isIE) {
2360                 EU.doRemove(window, "load", EU._load);
2361             }
2362         },
2363
2364
2365         _tryPreloadAttach: function() {
2366
2367             if (this.locked) {
2368                 return false;
2369             }
2370
2371             this.locked = true;
2372
2373
2374             var tryAgain = !loadComplete;
2375             if (!tryAgain) {
2376                 tryAgain = (retryCount > 0);
2377             }
2378
2379
2380             var notAvail = [];
2381             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2382                 var item = onAvailStack[i];
2383                 if (item) {
2384                     var el = this.getEl(item.id);
2385
2386                     if (el) {
2387                         if (!item.checkReady ||
2388                             loadComplete ||
2389                             el.nextSibling ||
2390                             (document && document.body)) {
2391
2392                             var scope = el;
2393                             if (item.override) {
2394                                 if (item.override === true) {
2395                                     scope = item.obj;
2396                                 } else {
2397                                     scope = item.override;
2398                                 }
2399                             }
2400                             item.fn.call(scope, item.obj);
2401                             onAvailStack[i] = null;
2402                         }
2403                     } else {
2404                         notAvail.push(item);
2405                     }
2406                 }
2407             }
2408
2409             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2410
2411             if (tryAgain) {
2412
2413                 this.startInterval();
2414             } else {
2415                 clearInterval(this._interval);
2416                 this._interval = null;
2417             }
2418
2419             this.locked = false;
2420
2421             return true;
2422
2423         },
2424
2425
2426         purgeElement: function(el, recurse, eventName) {
2427             var elListeners = this.getListeners(el, eventName);
2428             if (elListeners) {
2429                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2430                     var l = elListeners[i];
2431                     this.removeListener(el, l.type, l.fn);
2432                 }
2433             }
2434
2435             if (recurse && el && el.childNodes) {
2436                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2437                     this.purgeElement(el.childNodes[i], recurse, eventName);
2438                 }
2439             }
2440         },
2441
2442
2443         getListeners: function(el, eventName) {
2444             var results = [], searchLists;
2445             if (!eventName) {
2446                 searchLists = [listeners, unloadListeners];
2447             } else if (eventName == "unload") {
2448                 searchLists = [unloadListeners];
2449             } else {
2450                 searchLists = [listeners];
2451             }
2452
2453             for (var j = 0; j < searchLists.length; ++j) {
2454                 var searchList = searchLists[j];
2455                 if (searchList && searchList.length > 0) {
2456                     for (var i = 0,len = searchList.length; i < len; ++i) {
2457                         var l = searchList[i];
2458                         if (l && l[this.EL] === el &&
2459                             (!eventName || eventName === l[this.TYPE])) {
2460                             results.push({
2461                                 type:   l[this.TYPE],
2462                                 fn:     l[this.FN],
2463                                 obj:    l[this.OBJ],
2464                                 adjust: l[this.ADJ_SCOPE],
2465                                 index:  i
2466                             });
2467                         }
2468                     }
2469                 }
2470             }
2471
2472             return (results.length) ? results : null;
2473         },
2474
2475
2476         _unload: function(e) {
2477
2478             var EU = Roo.lib.Event, i, j, l, len, index;
2479
2480             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2481                 l = unloadListeners[i];
2482                 if (l) {
2483                     var scope = window;
2484                     if (l[EU.ADJ_SCOPE]) {
2485                         if (l[EU.ADJ_SCOPE] === true) {
2486                             scope = l[EU.OBJ];
2487                         } else {
2488                             scope = l[EU.ADJ_SCOPE];
2489                         }
2490                     }
2491                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2492                     unloadListeners[i] = null;
2493                     l = null;
2494                     scope = null;
2495                 }
2496             }
2497
2498             unloadListeners = null;
2499
2500             if (listeners && listeners.length > 0) {
2501                 j = listeners.length;
2502                 while (j) {
2503                     index = j - 1;
2504                     l = listeners[index];
2505                     if (l) {
2506                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2507                                 l[EU.FN], index);
2508                     }
2509                     j = j - 1;
2510                 }
2511                 l = null;
2512
2513                 EU.clearCache();
2514             }
2515
2516             EU.doRemove(window, "unload", EU._unload);
2517
2518         },
2519
2520
2521         getScroll: function() {
2522             var dd = document.documentElement, db = document.body;
2523             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2524                 return [dd.scrollTop, dd.scrollLeft];
2525             } else if (db) {
2526                 return [db.scrollTop, db.scrollLeft];
2527             } else {
2528                 return [0, 0];
2529             }
2530         },
2531
2532
2533         doAdd: function () {
2534             if (window.addEventListener) {
2535                 return function(el, eventName, fn, capture) {
2536                     el.addEventListener(eventName, fn, (capture));
2537                 };
2538             } else if (window.attachEvent) {
2539                 return function(el, eventName, fn, capture) {
2540                     el.attachEvent("on" + eventName, fn);
2541                 };
2542             } else {
2543                 return function() {
2544                 };
2545             }
2546         }(),
2547
2548
2549         doRemove: function() {
2550             if (window.removeEventListener) {
2551                 return function (el, eventName, fn, capture) {
2552                     el.removeEventListener(eventName, fn, (capture));
2553                 };
2554             } else if (window.detachEvent) {
2555                 return function (el, eventName, fn) {
2556                     el.detachEvent("on" + eventName, fn);
2557                 };
2558             } else {
2559                 return function() {
2560                 };
2561             }
2562         }()
2563     };
2564     
2565 }();
2566 (function() {     
2567    
2568     var E = Roo.lib.Event;
2569     E.on = E.addListener;
2570     E.un = E.removeListener;
2571
2572     if (document && document.body) {
2573         E._load();
2574     } else {
2575         E.doAdd(window, "load", E._load);
2576     }
2577     E.doAdd(window, "unload", E._unload);
2578     E._tryPreloadAttach();
2579 })();
2580
2581 /*
2582  * Portions of this file are based on pieces of Yahoo User Interface Library
2583  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2584  * YUI licensed under the BSD License:
2585  * http://developer.yahoo.net/yui/license.txt
2586  * <script type="text/javascript">
2587  *
2588  */
2589
2590 (function() {
2591     /**
2592      * @class Roo.lib.Ajax
2593      *
2594      */
2595     Roo.lib.Ajax = {
2596         /**
2597          * @static 
2598          */
2599         request : function(method, uri, cb, data, options) {
2600             if(options){
2601                 var hs = options.headers;
2602                 if(hs){
2603                     for(var h in hs){
2604                         if(hs.hasOwnProperty(h)){
2605                             this.initHeader(h, hs[h], false);
2606                         }
2607                     }
2608                 }
2609                 if(options.xmlData){
2610                     this.initHeader('Content-Type', 'text/xml', false);
2611                     method = 'POST';
2612                     data = options.xmlData;
2613                 }
2614             }
2615
2616             return this.asyncRequest(method, uri, cb, data);
2617         },
2618
2619         serializeForm : function(form) {
2620             if(typeof form == 'string') {
2621                 form = (document.getElementById(form) || document.forms[form]);
2622             }
2623
2624             var el, name, val, disabled, data = '', hasSubmit = false;
2625             for (var i = 0; i < form.elements.length; i++) {
2626                 el = form.elements[i];
2627                 disabled = form.elements[i].disabled;
2628                 name = form.elements[i].name;
2629                 val = form.elements[i].value;
2630
2631                 if (!disabled && name){
2632                     switch (el.type)
2633                             {
2634                         case 'select-one':
2635                         case 'select-multiple':
2636                             for (var j = 0; j < el.options.length; j++) {
2637                                 if (el.options[j].selected) {
2638                                     if (Roo.isIE) {
2639                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2640                                     }
2641                                     else {
2642                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2643                                     }
2644                                 }
2645                             }
2646                             break;
2647                         case 'radio':
2648                         case 'checkbox':
2649                             if (el.checked) {
2650                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2651                             }
2652                             break;
2653                         case 'file':
2654
2655                         case undefined:
2656
2657                         case 'reset':
2658
2659                         case 'button':
2660
2661                             break;
2662                         case 'submit':
2663                             if(hasSubmit == false) {
2664                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2665                                 hasSubmit = true;
2666                             }
2667                             break;
2668                         default:
2669                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2670                             break;
2671                     }
2672                 }
2673             }
2674             data = data.substr(0, data.length - 1);
2675             return data;
2676         },
2677
2678         headers:{},
2679
2680         hasHeaders:false,
2681
2682         useDefaultHeader:true,
2683
2684         defaultPostHeader:'application/x-www-form-urlencoded',
2685
2686         useDefaultXhrHeader:true,
2687
2688         defaultXhrHeader:'XMLHttpRequest',
2689
2690         hasDefaultHeaders:true,
2691
2692         defaultHeaders:{},
2693
2694         poll:{},
2695
2696         timeout:{},
2697
2698         pollInterval:50,
2699
2700         transactionId:0,
2701
2702         setProgId:function(id)
2703         {
2704             this.activeX.unshift(id);
2705         },
2706
2707         setDefaultPostHeader:function(b)
2708         {
2709             this.useDefaultHeader = b;
2710         },
2711
2712         setDefaultXhrHeader:function(b)
2713         {
2714             this.useDefaultXhrHeader = b;
2715         },
2716
2717         setPollingInterval:function(i)
2718         {
2719             if (typeof i == 'number' && isFinite(i)) {
2720                 this.pollInterval = i;
2721             }
2722         },
2723
2724         createXhrObject:function(transactionId)
2725         {
2726             var obj,http;
2727             try
2728             {
2729
2730                 http = new XMLHttpRequest();
2731
2732                 obj = { conn:http, tId:transactionId };
2733             }
2734             catch(e)
2735             {
2736                 for (var i = 0; i < this.activeX.length; ++i) {
2737                     try
2738                     {
2739
2740                         http = new ActiveXObject(this.activeX[i]);
2741
2742                         obj = { conn:http, tId:transactionId };
2743                         break;
2744                     }
2745                     catch(e) {
2746                     }
2747                 }
2748             }
2749             finally
2750             {
2751                 return obj;
2752             }
2753         },
2754
2755         getConnectionObject:function()
2756         {
2757             var o;
2758             var tId = this.transactionId;
2759
2760             try
2761             {
2762                 o = this.createXhrObject(tId);
2763                 if (o) {
2764                     this.transactionId++;
2765                 }
2766             }
2767             catch(e) {
2768             }
2769             finally
2770             {
2771                 return o;
2772             }
2773         },
2774
2775         asyncRequest:function(method, uri, callback, postData)
2776         {
2777             var o = this.getConnectionObject();
2778
2779             if (!o) {
2780                 return null;
2781             }
2782             else {
2783                 o.conn.open(method, uri, true);
2784
2785                 if (this.useDefaultXhrHeader) {
2786                     if (!this.defaultHeaders['X-Requested-With']) {
2787                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2788                     }
2789                 }
2790
2791                 if(postData && this.useDefaultHeader){
2792                     this.initHeader('Content-Type', this.defaultPostHeader);
2793                 }
2794
2795                  if (this.hasDefaultHeaders || this.hasHeaders) {
2796                     this.setHeader(o);
2797                 }
2798
2799                 this.handleReadyState(o, callback);
2800                 o.conn.send(postData || null);
2801
2802                 return o;
2803             }
2804         },
2805
2806         handleReadyState:function(o, callback)
2807         {
2808             var oConn = this;
2809
2810             if (callback && callback.timeout) {
2811                 
2812                 this.timeout[o.tId] = window.setTimeout(function() {
2813                     oConn.abort(o, callback, true);
2814                 }, callback.timeout);
2815             }
2816
2817             this.poll[o.tId] = window.setInterval(
2818                     function() {
2819                         if (o.conn && o.conn.readyState == 4) {
2820                             window.clearInterval(oConn.poll[o.tId]);
2821                             delete oConn.poll[o.tId];
2822
2823                             if(callback && callback.timeout) {
2824                                 window.clearTimeout(oConn.timeout[o.tId]);
2825                                 delete oConn.timeout[o.tId];
2826                             }
2827
2828                             oConn.handleTransactionResponse(o, callback);
2829                         }
2830                     }
2831                     , this.pollInterval);
2832         },
2833
2834         handleTransactionResponse:function(o, callback, isAbort)
2835         {
2836
2837             if (!callback) {
2838                 this.releaseObject(o);
2839                 return;
2840             }
2841
2842             var httpStatus, responseObject;
2843
2844             try
2845             {
2846                 if (o.conn.status !== undefined && o.conn.status != 0) {
2847                     httpStatus = o.conn.status;
2848                 }
2849                 else {
2850                     httpStatus = 13030;
2851                 }
2852             }
2853             catch(e) {
2854
2855
2856                 httpStatus = 13030;
2857             }
2858
2859             if (httpStatus >= 200 && httpStatus < 300) {
2860                 responseObject = this.createResponseObject(o, callback.argument);
2861                 if (callback.success) {
2862                     if (!callback.scope) {
2863                         callback.success(responseObject);
2864                     }
2865                     else {
2866
2867
2868                         callback.success.apply(callback.scope, [responseObject]);
2869                     }
2870                 }
2871             }
2872             else {
2873                 switch (httpStatus) {
2874
2875                     case 12002:
2876                     case 12029:
2877                     case 12030:
2878                     case 12031:
2879                     case 12152:
2880                     case 13030:
2881                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2882                         if (callback.failure) {
2883                             if (!callback.scope) {
2884                                 callback.failure(responseObject);
2885                             }
2886                             else {
2887                                 callback.failure.apply(callback.scope, [responseObject]);
2888                             }
2889                         }
2890                         break;
2891                     default:
2892                         responseObject = this.createResponseObject(o, callback.argument);
2893                         if (callback.failure) {
2894                             if (!callback.scope) {
2895                                 callback.failure(responseObject);
2896                             }
2897                             else {
2898                                 callback.failure.apply(callback.scope, [responseObject]);
2899                             }
2900                         }
2901                 }
2902             }
2903
2904             this.releaseObject(o);
2905             responseObject = null;
2906         },
2907
2908         createResponseObject:function(o, callbackArg)
2909         {
2910             var obj = {};
2911             var headerObj = {};
2912
2913             try
2914             {
2915                 var headerStr = o.conn.getAllResponseHeaders();
2916                 var header = headerStr.split('\n');
2917                 for (var i = 0; i < header.length; i++) {
2918                     var delimitPos = header[i].indexOf(':');
2919                     if (delimitPos != -1) {
2920                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2921                     }
2922                 }
2923             }
2924             catch(e) {
2925             }
2926
2927             obj.tId = o.tId;
2928             obj.status = o.conn.status;
2929             obj.statusText = o.conn.statusText;
2930             obj.getResponseHeader = headerObj;
2931             obj.getAllResponseHeaders = headerStr;
2932             obj.responseText = o.conn.responseText;
2933             obj.responseXML = o.conn.responseXML;
2934
2935             if (typeof callbackArg !== undefined) {
2936                 obj.argument = callbackArg;
2937             }
2938
2939             return obj;
2940         },
2941
2942         createExceptionObject:function(tId, callbackArg, isAbort)
2943         {
2944             var COMM_CODE = 0;
2945             var COMM_ERROR = 'communication failure';
2946             var ABORT_CODE = -1;
2947             var ABORT_ERROR = 'transaction aborted';
2948
2949             var obj = {};
2950
2951             obj.tId = tId;
2952             if (isAbort) {
2953                 obj.status = ABORT_CODE;
2954                 obj.statusText = ABORT_ERROR;
2955             }
2956             else {
2957                 obj.status = COMM_CODE;
2958                 obj.statusText = COMM_ERROR;
2959             }
2960
2961             if (callbackArg) {
2962                 obj.argument = callbackArg;
2963             }
2964
2965             return obj;
2966         },
2967
2968         initHeader:function(label, value, isDefault)
2969         {
2970             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2971
2972             if (headerObj[label] === undefined) {
2973                 headerObj[label] = value;
2974             }
2975             else {
2976
2977
2978                 headerObj[label] = value + "," + headerObj[label];
2979             }
2980
2981             if (isDefault) {
2982                 this.hasDefaultHeaders = true;
2983             }
2984             else {
2985                 this.hasHeaders = true;
2986             }
2987         },
2988
2989
2990         setHeader:function(o)
2991         {
2992             if (this.hasDefaultHeaders) {
2993                 for (var prop in this.defaultHeaders) {
2994                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2995                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2996                     }
2997                 }
2998             }
2999
3000             if (this.hasHeaders) {
3001                 for (var prop in this.headers) {
3002                     if (this.headers.hasOwnProperty(prop)) {
3003                         o.conn.setRequestHeader(prop, this.headers[prop]);
3004                     }
3005                 }
3006                 this.headers = {};
3007                 this.hasHeaders = false;
3008             }
3009         },
3010
3011         resetDefaultHeaders:function() {
3012             delete this.defaultHeaders;
3013             this.defaultHeaders = {};
3014             this.hasDefaultHeaders = false;
3015         },
3016
3017         abort:function(o, callback, isTimeout)
3018         {
3019             if(this.isCallInProgress(o)) {
3020                 o.conn.abort();
3021                 window.clearInterval(this.poll[o.tId]);
3022                 delete this.poll[o.tId];
3023                 if (isTimeout) {
3024                     delete this.timeout[o.tId];
3025                 }
3026
3027                 this.handleTransactionResponse(o, callback, true);
3028
3029                 return true;
3030             }
3031             else {
3032                 return false;
3033             }
3034         },
3035
3036
3037         isCallInProgress:function(o)
3038         {
3039             if (o && o.conn) {
3040                 return o.conn.readyState != 4 && o.conn.readyState != 0;
3041             }
3042             else {
3043
3044                 return false;
3045             }
3046         },
3047
3048
3049         releaseObject:function(o)
3050         {
3051
3052             o.conn = null;
3053
3054             o = null;
3055         },
3056
3057         activeX:[
3058         'MSXML2.XMLHTTP.3.0',
3059         'MSXML2.XMLHTTP',
3060         'Microsoft.XMLHTTP'
3061         ]
3062
3063
3064     };
3065 })();/*
3066  * Portions of this file are based on pieces of Yahoo User Interface Library
3067  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3068  * YUI licensed under the BSD License:
3069  * http://developer.yahoo.net/yui/license.txt
3070  * <script type="text/javascript">
3071  *
3072  */
3073
3074 Roo.lib.Region = function(t, r, b, l) {
3075     this.top = t;
3076     this[1] = t;
3077     this.right = r;
3078     this.bottom = b;
3079     this.left = l;
3080     this[0] = l;
3081 };
3082
3083
3084 Roo.lib.Region.prototype = {
3085     contains : function(region) {
3086         return ( region.left >= this.left &&
3087                  region.right <= this.right &&
3088                  region.top >= this.top &&
3089                  region.bottom <= this.bottom    );
3090
3091     },
3092
3093     getArea : function() {
3094         return ( (this.bottom - this.top) * (this.right - this.left) );
3095     },
3096
3097     intersect : function(region) {
3098         var t = Math.max(this.top, region.top);
3099         var r = Math.min(this.right, region.right);
3100         var b = Math.min(this.bottom, region.bottom);
3101         var l = Math.max(this.left, region.left);
3102
3103         if (b >= t && r >= l) {
3104             return new Roo.lib.Region(t, r, b, l);
3105         } else {
3106             return null;
3107         }
3108     },
3109     union : function(region) {
3110         var t = Math.min(this.top, region.top);
3111         var r = Math.max(this.right, region.right);
3112         var b = Math.max(this.bottom, region.bottom);
3113         var l = Math.min(this.left, region.left);
3114
3115         return new Roo.lib.Region(t, r, b, l);
3116     },
3117
3118     adjust : function(t, l, b, r) {
3119         this.top += t;
3120         this.left += l;
3121         this.right += r;
3122         this.bottom += b;
3123         return this;
3124     }
3125 };
3126
3127 Roo.lib.Region.getRegion = function(el) {
3128     var p = Roo.lib.Dom.getXY(el);
3129
3130     var t = p[1];
3131     var r = p[0] + el.offsetWidth;
3132     var b = p[1] + el.offsetHeight;
3133     var l = p[0];
3134
3135     return new Roo.lib.Region(t, r, b, l);
3136 };
3137 /*
3138  * Portions of this file are based on pieces of Yahoo User Interface Library
3139  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3140  * YUI licensed under the BSD License:
3141  * http://developer.yahoo.net/yui/license.txt
3142  * <script type="text/javascript">
3143  *
3144  */
3145 //@@dep Roo.lib.Region
3146
3147
3148 Roo.lib.Point = function(x, y) {
3149     if (x instanceof Array) {
3150         y = x[1];
3151         x = x[0];
3152     }
3153     this.x = this.right = this.left = this[0] = x;
3154     this.y = this.top = this.bottom = this[1] = y;
3155 };
3156
3157 Roo.lib.Point.prototype = new Roo.lib.Region();
3158 /*
3159  * Portions of this file are based on pieces of Yahoo User Interface Library
3160  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3161  * YUI licensed under the BSD License:
3162  * http://developer.yahoo.net/yui/license.txt
3163  * <script type="text/javascript">
3164  *
3165  */
3166  
3167 (function() {   
3168
3169     Roo.lib.Anim = {
3170         scroll : function(el, args, duration, easing, cb, scope) {
3171             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3172         },
3173
3174         motion : function(el, args, duration, easing, cb, scope) {
3175             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3176         },
3177
3178         color : function(el, args, duration, easing, cb, scope) {
3179             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3180         },
3181
3182         run : function(el, args, duration, easing, cb, scope, type) {
3183             type = type || Roo.lib.AnimBase;
3184             if (typeof easing == "string") {
3185                 easing = Roo.lib.Easing[easing];
3186             }
3187             var anim = new type(el, args, duration, easing);
3188             anim.animateX(function() {
3189                 Roo.callback(cb, scope);
3190             });
3191             return anim;
3192         }
3193     };
3194 })();/*
3195  * Portions of this file are based on pieces of Yahoo User Interface Library
3196  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3197  * YUI licensed under the BSD License:
3198  * http://developer.yahoo.net/yui/license.txt
3199  * <script type="text/javascript">
3200  *
3201  */
3202
3203 (function() {    
3204     var libFlyweight;
3205     
3206     function fly(el) {
3207         if (!libFlyweight) {
3208             libFlyweight = new Roo.Element.Flyweight();
3209         }
3210         libFlyweight.dom = el;
3211         return libFlyweight;
3212     }
3213
3214     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3215     
3216    
3217     
3218     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3219         if (el) {
3220             this.init(el, attributes, duration, method);
3221         }
3222     };
3223
3224     Roo.lib.AnimBase.fly = fly;
3225     
3226     
3227     
3228     Roo.lib.AnimBase.prototype = {
3229
3230         toString: function() {
3231             var el = this.getEl();
3232             var id = el.id || el.tagName;
3233             return ("Anim " + id);
3234         },
3235
3236         patterns: {
3237             noNegatives:        /width|height|opacity|padding/i,
3238             offsetAttribute:  /^((width|height)|(top|left))$/,
3239             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3240             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3241         },
3242
3243
3244         doMethod: function(attr, start, end) {
3245             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3246         },
3247
3248
3249         setAttribute: function(attr, val, unit) {
3250             if (this.patterns.noNegatives.test(attr)) {
3251                 val = (val > 0) ? val : 0;
3252             }
3253
3254             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3255         },
3256
3257
3258         getAttribute: function(attr) {
3259             var el = this.getEl();
3260             var val = fly(el).getStyle(attr);
3261
3262             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3263                 return parseFloat(val);
3264             }
3265
3266             var a = this.patterns.offsetAttribute.exec(attr) || [];
3267             var pos = !!( a[3] );
3268             var box = !!( a[2] );
3269
3270
3271             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3272                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3273             } else {
3274                 val = 0;
3275             }
3276
3277             return val;
3278         },
3279
3280
3281         getDefaultUnit: function(attr) {
3282             if (this.patterns.defaultUnit.test(attr)) {
3283                 return 'px';
3284             }
3285
3286             return '';
3287         },
3288
3289         animateX : function(callback, scope) {
3290             var f = function() {
3291                 this.onComplete.removeListener(f);
3292                 if (typeof callback == "function") {
3293                     callback.call(scope || this, this);
3294                 }
3295             };
3296             this.onComplete.addListener(f, this);
3297             this.animate();
3298         },
3299
3300
3301         setRuntimeAttribute: function(attr) {
3302             var start;
3303             var end;
3304             var attributes = this.attributes;
3305
3306             this.runtimeAttributes[attr] = {};
3307
3308             var isset = function(prop) {
3309                 return (typeof prop !== 'undefined');
3310             };
3311
3312             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3313                 return false;
3314             }
3315
3316             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3317
3318
3319             if (isset(attributes[attr]['to'])) {
3320                 end = attributes[attr]['to'];
3321             } else if (isset(attributes[attr]['by'])) {
3322                 if (start.constructor == Array) {
3323                     end = [];
3324                     for (var i = 0, len = start.length; i < len; ++i) {
3325                         end[i] = start[i] + attributes[attr]['by'][i];
3326                     }
3327                 } else {
3328                     end = start + attributes[attr]['by'];
3329                 }
3330             }
3331
3332             this.runtimeAttributes[attr].start = start;
3333             this.runtimeAttributes[attr].end = end;
3334
3335
3336             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3337         },
3338
3339
3340         init: function(el, attributes, duration, method) {
3341
3342             var isAnimated = false;
3343
3344
3345             var startTime = null;
3346
3347
3348             var actualFrames = 0;
3349
3350
3351             el = Roo.getDom(el);
3352
3353
3354             this.attributes = attributes || {};
3355
3356
3357             this.duration = duration || 1;
3358
3359
3360             this.method = method || Roo.lib.Easing.easeNone;
3361
3362
3363             this.useSeconds = true;
3364
3365
3366             this.currentFrame = 0;
3367
3368
3369             this.totalFrames = Roo.lib.AnimMgr.fps;
3370
3371
3372             this.getEl = function() {
3373                 return el;
3374             };
3375
3376
3377             this.isAnimated = function() {
3378                 return isAnimated;
3379             };
3380
3381
3382             this.getStartTime = function() {
3383                 return startTime;
3384             };
3385
3386             this.runtimeAttributes = {};
3387
3388
3389             this.animate = function() {
3390                 if (this.isAnimated()) {
3391                     return false;
3392                 }
3393
3394                 this.currentFrame = 0;
3395
3396                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3397
3398                 Roo.lib.AnimMgr.registerElement(this);
3399             };
3400
3401
3402             this.stop = function(finish) {
3403                 if (finish) {
3404                     this.currentFrame = this.totalFrames;
3405                     this._onTween.fire();
3406                 }
3407                 Roo.lib.AnimMgr.stop(this);
3408             };
3409
3410             var onStart = function() {
3411                 this.onStart.fire();
3412
3413                 this.runtimeAttributes = {};
3414                 for (var attr in this.attributes) {
3415                     this.setRuntimeAttribute(attr);
3416                 }
3417
3418                 isAnimated = true;
3419                 actualFrames = 0;
3420                 startTime = new Date();
3421             };
3422
3423
3424             var onTween = function() {
3425                 var data = {
3426                     duration: new Date() - this.getStartTime(),
3427                     currentFrame: this.currentFrame
3428                 };
3429
3430                 data.toString = function() {
3431                     return (
3432                             'duration: ' + data.duration +
3433                             ', currentFrame: ' + data.currentFrame
3434                             );
3435                 };
3436
3437                 this.onTween.fire(data);
3438
3439                 var runtimeAttributes = this.runtimeAttributes;
3440
3441                 for (var attr in runtimeAttributes) {
3442                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3443                 }
3444
3445                 actualFrames += 1;
3446             };
3447
3448             var onComplete = function() {
3449                 var actual_duration = (new Date() - startTime) / 1000 ;
3450
3451                 var data = {
3452                     duration: actual_duration,
3453                     frames: actualFrames,
3454                     fps: actualFrames / actual_duration
3455                 };
3456
3457                 data.toString = function() {
3458                     return (
3459                             'duration: ' + data.duration +
3460                             ', frames: ' + data.frames +
3461                             ', fps: ' + data.fps
3462                             );
3463                 };
3464
3465                 isAnimated = false;
3466                 actualFrames = 0;
3467                 this.onComplete.fire(data);
3468             };
3469
3470
3471             this._onStart = new Roo.util.Event(this);
3472             this.onStart = new Roo.util.Event(this);
3473             this.onTween = new Roo.util.Event(this);
3474             this._onTween = new Roo.util.Event(this);
3475             this.onComplete = new Roo.util.Event(this);
3476             this._onComplete = new Roo.util.Event(this);
3477             this._onStart.addListener(onStart);
3478             this._onTween.addListener(onTween);
3479             this._onComplete.addListener(onComplete);
3480         }
3481     };
3482 })();
3483 /*
3484  * Portions of this file are based on pieces of Yahoo User Interface Library
3485  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3486  * YUI licensed under the BSD License:
3487  * http://developer.yahoo.net/yui/license.txt
3488  * <script type="text/javascript">
3489  *
3490  */
3491
3492 Roo.lib.AnimMgr = new function() {
3493
3494     var thread = null;
3495
3496
3497     var queue = [];
3498
3499
3500     var tweenCount = 0;
3501
3502
3503     this.fps = 1000;
3504
3505
3506     this.delay = 1;
3507
3508
3509     this.registerElement = function(tween) {
3510         queue[queue.length] = tween;
3511         tweenCount += 1;
3512         tween._onStart.fire();
3513         this.start();
3514     };
3515
3516
3517     this.unRegister = function(tween, index) {
3518         tween._onComplete.fire();
3519         index = index || getIndex(tween);
3520         if (index != -1) {
3521             queue.splice(index, 1);
3522         }
3523
3524         tweenCount -= 1;
3525         if (tweenCount <= 0) {
3526             this.stop();
3527         }
3528     };
3529
3530
3531     this.start = function() {
3532         if (thread === null) {
3533             thread = setInterval(this.run, this.delay);
3534         }
3535     };
3536
3537
3538     this.stop = function(tween) {
3539         if (!tween) {
3540             clearInterval(thread);
3541
3542             for (var i = 0, len = queue.length; i < len; ++i) {
3543                 if (queue[0].isAnimated()) {
3544                     this.unRegister(queue[0], 0);
3545                 }
3546             }
3547
3548             queue = [];
3549             thread = null;
3550             tweenCount = 0;
3551         }
3552         else {
3553             this.unRegister(tween);
3554         }
3555     };
3556
3557
3558     this.run = function() {
3559         for (var i = 0, len = queue.length; i < len; ++i) {
3560             var tween = queue[i];
3561             if (!tween || !tween.isAnimated()) {
3562                 continue;
3563             }
3564
3565             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3566             {
3567                 tween.currentFrame += 1;
3568
3569                 if (tween.useSeconds) {
3570                     correctFrame(tween);
3571                 }
3572                 tween._onTween.fire();
3573             }
3574             else {
3575                 Roo.lib.AnimMgr.stop(tween, i);
3576             }
3577         }
3578     };
3579
3580     var getIndex = function(anim) {
3581         for (var i = 0, len = queue.length; i < len; ++i) {
3582             if (queue[i] == anim) {
3583                 return i;
3584             }
3585         }
3586         return -1;
3587     };
3588
3589
3590     var correctFrame = function(tween) {
3591         var frames = tween.totalFrames;
3592         var frame = tween.currentFrame;
3593         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3594         var elapsed = (new Date() - tween.getStartTime());
3595         var tweak = 0;
3596
3597         if (elapsed < tween.duration * 1000) {
3598             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3599         } else {
3600             tweak = frames - (frame + 1);
3601         }
3602         if (tweak > 0 && isFinite(tweak)) {
3603             if (tween.currentFrame + tweak >= frames) {
3604                 tweak = frames - (frame + 1);
3605             }
3606
3607             tween.currentFrame += tweak;
3608         }
3609     };
3610 };
3611
3612     /*
3613  * Portions of this file are based on pieces of Yahoo User Interface Library
3614  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3615  * YUI licensed under the BSD License:
3616  * http://developer.yahoo.net/yui/license.txt
3617  * <script type="text/javascript">
3618  *
3619  */
3620 Roo.lib.Bezier = new function() {
3621
3622         this.getPosition = function(points, t) {
3623             var n = points.length;
3624             var tmp = [];
3625
3626             for (var i = 0; i < n; ++i) {
3627                 tmp[i] = [points[i][0], points[i][1]];
3628             }
3629
3630             for (var j = 1; j < n; ++j) {
3631                 for (i = 0; i < n - j; ++i) {
3632                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3633                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3634                 }
3635             }
3636
3637             return [ tmp[0][0], tmp[0][1] ];
3638
3639         };
3640     };/*
3641  * Portions of this file are based on pieces of Yahoo User Interface Library
3642  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3643  * YUI licensed under the BSD License:
3644  * http://developer.yahoo.net/yui/license.txt
3645  * <script type="text/javascript">
3646  *
3647  */
3648 (function() {
3649
3650     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3651         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3652     };
3653
3654     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3655
3656     var fly = Roo.lib.AnimBase.fly;
3657     var Y = Roo.lib;
3658     var superclass = Y.ColorAnim.superclass;
3659     var proto = Y.ColorAnim.prototype;
3660
3661     proto.toString = function() {
3662         var el = this.getEl();
3663         var id = el.id || el.tagName;
3664         return ("ColorAnim " + id);
3665     };
3666
3667     proto.patterns.color = /color$/i;
3668     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3669     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3670     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3671     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3672
3673
3674     proto.parseColor = function(s) {
3675         if (s.length == 3) {
3676             return s;
3677         }
3678
3679         var c = this.patterns.hex.exec(s);
3680         if (c && c.length == 4) {
3681             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3682         }
3683
3684         c = this.patterns.rgb.exec(s);
3685         if (c && c.length == 4) {
3686             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3687         }
3688
3689         c = this.patterns.hex3.exec(s);
3690         if (c && c.length == 4) {
3691             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3692         }
3693
3694         return null;
3695     };
3696     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3697     proto.getAttribute = function(attr) {
3698         var el = this.getEl();
3699         if (this.patterns.color.test(attr)) {
3700             var val = fly(el).getStyle(attr);
3701
3702             if (this.patterns.transparent.test(val)) {
3703                 var parent = el.parentNode;
3704                 val = fly(parent).getStyle(attr);
3705
3706                 while (parent && this.patterns.transparent.test(val)) {
3707                     parent = parent.parentNode;
3708                     val = fly(parent).getStyle(attr);
3709                     if (parent.tagName.toUpperCase() == 'HTML') {
3710                         val = '#fff';
3711                     }
3712                 }
3713             }
3714         } else {
3715             val = superclass.getAttribute.call(this, attr);
3716         }
3717
3718         return val;
3719     };
3720     proto.getAttribute = function(attr) {
3721         var el = this.getEl();
3722         if (this.patterns.color.test(attr)) {
3723             var val = fly(el).getStyle(attr);
3724
3725             if (this.patterns.transparent.test(val)) {
3726                 var parent = el.parentNode;
3727                 val = fly(parent).getStyle(attr);
3728
3729                 while (parent && this.patterns.transparent.test(val)) {
3730                     parent = parent.parentNode;
3731                     val = fly(parent).getStyle(attr);
3732                     if (parent.tagName.toUpperCase() == 'HTML') {
3733                         val = '#fff';
3734                     }
3735                 }
3736             }
3737         } else {
3738             val = superclass.getAttribute.call(this, attr);
3739         }
3740
3741         return val;
3742     };
3743
3744     proto.doMethod = function(attr, start, end) {
3745         var val;
3746
3747         if (this.patterns.color.test(attr)) {
3748             val = [];
3749             for (var i = 0, len = start.length; i < len; ++i) {
3750                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3751             }
3752
3753             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3754         }
3755         else {
3756             val = superclass.doMethod.call(this, attr, start, end);
3757         }
3758
3759         return val;
3760     };
3761
3762     proto.setRuntimeAttribute = function(attr) {
3763         superclass.setRuntimeAttribute.call(this, attr);
3764
3765         if (this.patterns.color.test(attr)) {
3766             var attributes = this.attributes;
3767             var start = this.parseColor(this.runtimeAttributes[attr].start);
3768             var end = this.parseColor(this.runtimeAttributes[attr].end);
3769
3770             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3771                 end = this.parseColor(attributes[attr].by);
3772
3773                 for (var i = 0, len = start.length; i < len; ++i) {
3774                     end[i] = start[i] + end[i];
3775                 }
3776             }
3777
3778             this.runtimeAttributes[attr].start = start;
3779             this.runtimeAttributes[attr].end = end;
3780         }
3781     };
3782 })();
3783
3784 /*
3785  * Portions of this file are based on pieces of Yahoo User Interface Library
3786  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3787  * YUI licensed under the BSD License:
3788  * http://developer.yahoo.net/yui/license.txt
3789  * <script type="text/javascript">
3790  *
3791  */
3792 Roo.lib.Easing = {
3793
3794
3795     easeNone: function (t, b, c, d) {
3796         return c * t / d + b;
3797     },
3798
3799
3800     easeIn: function (t, b, c, d) {
3801         return c * (t /= d) * t + b;
3802     },
3803
3804
3805     easeOut: function (t, b, c, d) {
3806         return -c * (t /= d) * (t - 2) + b;
3807     },
3808
3809
3810     easeBoth: function (t, b, c, d) {
3811         if ((t /= d / 2) < 1) {
3812             return c / 2 * t * t + b;
3813         }
3814
3815         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3816     },
3817
3818
3819     easeInStrong: function (t, b, c, d) {
3820         return c * (t /= d) * t * t * t + b;
3821     },
3822
3823
3824     easeOutStrong: function (t, b, c, d) {
3825         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3826     },
3827
3828
3829     easeBothStrong: function (t, b, c, d) {
3830         if ((t /= d / 2) < 1) {
3831             return c / 2 * t * t * t * t + b;
3832         }
3833
3834         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3835     },
3836
3837
3838
3839     elasticIn: function (t, b, c, d, a, p) {
3840         if (t == 0) {
3841             return b;
3842         }
3843         if ((t /= d) == 1) {
3844             return b + c;
3845         }
3846         if (!p) {
3847             p = d * .3;
3848         }
3849
3850         if (!a || a < Math.abs(c)) {
3851             a = c;
3852             var s = p / 4;
3853         }
3854         else {
3855             var s = p / (2 * Math.PI) * Math.asin(c / a);
3856         }
3857
3858         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3859     },
3860
3861
3862     elasticOut: function (t, b, c, d, a, p) {
3863         if (t == 0) {
3864             return b;
3865         }
3866         if ((t /= d) == 1) {
3867             return b + c;
3868         }
3869         if (!p) {
3870             p = d * .3;
3871         }
3872
3873         if (!a || a < Math.abs(c)) {
3874             a = c;
3875             var s = p / 4;
3876         }
3877         else {
3878             var s = p / (2 * Math.PI) * Math.asin(c / a);
3879         }
3880
3881         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3882     },
3883
3884
3885     elasticBoth: function (t, b, c, d, a, p) {
3886         if (t == 0) {
3887             return b;
3888         }
3889
3890         if ((t /= d / 2) == 2) {
3891             return b + c;
3892         }
3893
3894         if (!p) {
3895             p = d * (.3 * 1.5);
3896         }
3897
3898         if (!a || a < Math.abs(c)) {
3899             a = c;
3900             var s = p / 4;
3901         }
3902         else {
3903             var s = p / (2 * Math.PI) * Math.asin(c / a);
3904         }
3905
3906         if (t < 1) {
3907             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3908                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3909         }
3910         return a * Math.pow(2, -10 * (t -= 1)) *
3911                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3912     },
3913
3914
3915
3916     backIn: function (t, b, c, d, s) {
3917         if (typeof s == 'undefined') {
3918             s = 1.70158;
3919         }
3920         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3921     },
3922
3923
3924     backOut: function (t, b, c, d, s) {
3925         if (typeof s == 'undefined') {
3926             s = 1.70158;
3927         }
3928         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3929     },
3930
3931
3932     backBoth: function (t, b, c, d, s) {
3933         if (typeof s == 'undefined') {
3934             s = 1.70158;
3935         }
3936
3937         if ((t /= d / 2 ) < 1) {
3938             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3939         }
3940         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3941     },
3942
3943
3944     bounceIn: function (t, b, c, d) {
3945         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3946     },
3947
3948
3949     bounceOut: function (t, b, c, d) {
3950         if ((t /= d) < (1 / 2.75)) {
3951             return c * (7.5625 * t * t) + b;
3952         } else if (t < (2 / 2.75)) {
3953             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3954         } else if (t < (2.5 / 2.75)) {
3955             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3956         }
3957         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3958     },
3959
3960
3961     bounceBoth: function (t, b, c, d) {
3962         if (t < d / 2) {
3963             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3964         }
3965         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3966     }
3967 };/*
3968  * Portions of this file are based on pieces of Yahoo User Interface Library
3969  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3970  * YUI licensed under the BSD License:
3971  * http://developer.yahoo.net/yui/license.txt
3972  * <script type="text/javascript">
3973  *
3974  */
3975     (function() {
3976         Roo.lib.Motion = function(el, attributes, duration, method) {
3977             if (el) {
3978                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3979             }
3980         };
3981
3982         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3983
3984
3985         var Y = Roo.lib;
3986         var superclass = Y.Motion.superclass;
3987         var proto = Y.Motion.prototype;
3988
3989         proto.toString = function() {
3990             var el = this.getEl();
3991             var id = el.id || el.tagName;
3992             return ("Motion " + id);
3993         };
3994
3995         proto.patterns.points = /^points$/i;
3996
3997         proto.setAttribute = function(attr, val, unit) {
3998             if (this.patterns.points.test(attr)) {
3999                 unit = unit || 'px';
4000                 superclass.setAttribute.call(this, 'left', val[0], unit);
4001                 superclass.setAttribute.call(this, 'top', val[1], unit);
4002             } else {
4003                 superclass.setAttribute.call(this, attr, val, unit);
4004             }
4005         };
4006
4007         proto.getAttribute = function(attr) {
4008             if (this.patterns.points.test(attr)) {
4009                 var val = [
4010                         superclass.getAttribute.call(this, 'left'),
4011                         superclass.getAttribute.call(this, 'top')
4012                         ];
4013             } else {
4014                 val = superclass.getAttribute.call(this, attr);
4015             }
4016
4017             return val;
4018         };
4019
4020         proto.doMethod = function(attr, start, end) {
4021             var val = null;
4022
4023             if (this.patterns.points.test(attr)) {
4024                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
4025                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
4026             } else {
4027                 val = superclass.doMethod.call(this, attr, start, end);
4028             }
4029             return val;
4030         };
4031
4032         proto.setRuntimeAttribute = function(attr) {
4033             if (this.patterns.points.test(attr)) {
4034                 var el = this.getEl();
4035                 var attributes = this.attributes;
4036                 var start;
4037                 var control = attributes['points']['control'] || [];
4038                 var end;
4039                 var i, len;
4040
4041                 if (control.length > 0 && !(control[0] instanceof Array)) {
4042                     control = [control];
4043                 } else {
4044                     var tmp = [];
4045                     for (i = 0,len = control.length; i < len; ++i) {
4046                         tmp[i] = control[i];
4047                     }
4048                     control = tmp;
4049                 }
4050
4051                 Roo.fly(el).position();
4052
4053                 if (isset(attributes['points']['from'])) {
4054                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
4055                 }
4056                 else {
4057                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
4058                 }
4059
4060                 start = this.getAttribute('points');
4061
4062
4063                 if (isset(attributes['points']['to'])) {
4064                     end = translateValues.call(this, attributes['points']['to'], start);
4065
4066                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4067                     for (i = 0,len = control.length; i < len; ++i) {
4068                         control[i] = translateValues.call(this, control[i], start);
4069                     }
4070
4071
4072                 } else if (isset(attributes['points']['by'])) {
4073                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4074
4075                     for (i = 0,len = control.length; i < len; ++i) {
4076                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4077                     }
4078                 }
4079
4080                 this.runtimeAttributes[attr] = [start];
4081
4082                 if (control.length > 0) {
4083                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4084                 }
4085
4086                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4087             }
4088             else {
4089                 superclass.setRuntimeAttribute.call(this, attr);
4090             }
4091         };
4092
4093         var translateValues = function(val, start) {
4094             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4095             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4096
4097             return val;
4098         };
4099
4100         var isset = function(prop) {
4101             return (typeof prop !== 'undefined');
4102         };
4103     })();
4104 /*
4105  * Portions of this file are based on pieces of Yahoo User Interface Library
4106  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4107  * YUI licensed under the BSD License:
4108  * http://developer.yahoo.net/yui/license.txt
4109  * <script type="text/javascript">
4110  *
4111  */
4112     (function() {
4113         Roo.lib.Scroll = function(el, attributes, duration, method) {
4114             if (el) {
4115                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4116             }
4117         };
4118
4119         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4120
4121
4122         var Y = Roo.lib;
4123         var superclass = Y.Scroll.superclass;
4124         var proto = Y.Scroll.prototype;
4125
4126         proto.toString = function() {
4127             var el = this.getEl();
4128             var id = el.id || el.tagName;
4129             return ("Scroll " + id);
4130         };
4131
4132         proto.doMethod = function(attr, start, end) {
4133             var val = null;
4134
4135             if (attr == 'scroll') {
4136                 val = [
4137                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4138                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4139                         ];
4140
4141             } else {
4142                 val = superclass.doMethod.call(this, attr, start, end);
4143             }
4144             return val;
4145         };
4146
4147         proto.getAttribute = function(attr) {
4148             var val = null;
4149             var el = this.getEl();
4150
4151             if (attr == 'scroll') {
4152                 val = [ el.scrollLeft, el.scrollTop ];
4153             } else {
4154                 val = superclass.getAttribute.call(this, attr);
4155             }
4156
4157             return val;
4158         };
4159
4160         proto.setAttribute = function(attr, val, unit) {
4161             var el = this.getEl();
4162
4163             if (attr == 'scroll') {
4164                 el.scrollLeft = val[0];
4165                 el.scrollTop = val[1];
4166             } else {
4167                 superclass.setAttribute.call(this, attr, val, unit);
4168             }
4169         };
4170     })();
4171 /*
4172  * Based on:
4173  * Ext JS Library 1.1.1
4174  * Copyright(c) 2006-2007, Ext JS, LLC.
4175  *
4176  * Originally Released Under LGPL - original licence link has changed is not relivant.
4177  *
4178  * Fork - LGPL
4179  * <script type="text/javascript">
4180  */
4181
4182
4183 // nasty IE9 hack - what a pile of crap that is..
4184
4185  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4186     Range.prototype.createContextualFragment = function (html) {
4187         var doc = window.document;
4188         var container = doc.createElement("div");
4189         container.innerHTML = html;
4190         var frag = doc.createDocumentFragment(), n;
4191         while ((n = container.firstChild)) {
4192             frag.appendChild(n);
4193         }
4194         return frag;
4195     };
4196 }
4197
4198 /**
4199  * @class Roo.DomHelper
4200  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4201  * For more information see <a href="http://web.archive.org/web/20071221063734/http://www.jackslocum.com/blog/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
4202  * @singleton
4203  */
4204 Roo.DomHelper = function(){
4205     var tempTableEl = null;
4206     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4207     var tableRe = /^table|tbody|tr|td$/i;
4208     var xmlns = {};
4209     // build as innerHTML where available
4210     /** @ignore */
4211     var createHtml = function(o){
4212         if(typeof o == 'string'){
4213             return o;
4214         }
4215         var b = "";
4216         if(!o.tag){
4217             o.tag = "div";
4218         }
4219         b += "<" + o.tag;
4220         for(var attr in o){
4221             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
4222             if(attr == "style"){
4223                 var s = o["style"];
4224                 if(typeof s == "function"){
4225                     s = s.call();
4226                 }
4227                 if(typeof s == "string"){
4228                     b += ' style="' + s + '"';
4229                 }else if(typeof s == "object"){
4230                     b += ' style="';
4231                     for(var key in s){
4232                         if(typeof s[key] != "function"){
4233                             b += key + ":" + s[key] + ";";
4234                         }
4235                     }
4236                     b += '"';
4237                 }
4238             }else{
4239                 if(attr == "cls"){
4240                     b += ' class="' + o["cls"] + '"';
4241                 }else if(attr == "htmlFor"){
4242                     b += ' for="' + o["htmlFor"] + '"';
4243                 }else{
4244                     b += " " + attr + '="' + o[attr] + '"';
4245                 }
4246             }
4247         }
4248         if(emptyTags.test(o.tag)){
4249             b += "/>";
4250         }else{
4251             b += ">";
4252             var cn = o.children || o.cn;
4253             if(cn){
4254                 //http://bugs.kde.org/show_bug.cgi?id=71506
4255                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4256                     for(var i = 0, len = cn.length; i < len; i++) {
4257                         b += createHtml(cn[i], b);
4258                     }
4259                 }else{
4260                     b += createHtml(cn, b);
4261                 }
4262             }
4263             if(o.html){
4264                 b += o.html;
4265             }
4266             b += "</" + o.tag + ">";
4267         }
4268         return b;
4269     };
4270
4271     // build as dom
4272     /** @ignore */
4273     var createDom = function(o, parentNode){
4274          
4275         // defininition craeted..
4276         var ns = false;
4277         if (o.ns && o.ns != 'html') {
4278                
4279             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4280                 xmlns[o.ns] = o.xmlns;
4281                 ns = o.xmlns;
4282             }
4283             if (typeof(xmlns[o.ns]) == 'undefined') {
4284                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4285             }
4286             ns = xmlns[o.ns];
4287         }
4288         
4289         
4290         if (typeof(o) == 'string') {
4291             return parentNode.appendChild(document.createTextNode(o));
4292         }
4293         o.tag = o.tag || div;
4294         if (o.ns && Roo.isIE) {
4295             ns = false;
4296             o.tag = o.ns + ':' + o.tag;
4297             
4298         }
4299         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4300         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4301         for(var attr in o){
4302             
4303             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4304                     attr == "style" || typeof o[attr] == "function") { continue; }
4305                     
4306             if(attr=="cls" && Roo.isIE){
4307                 el.className = o["cls"];
4308             }else{
4309                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
4310                 else { 
4311                     el[attr] = o[attr];
4312                 }
4313             }
4314         }
4315         Roo.DomHelper.applyStyles(el, o.style);
4316         var cn = o.children || o.cn;
4317         if(cn){
4318             //http://bugs.kde.org/show_bug.cgi?id=71506
4319              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4320                 for(var i = 0, len = cn.length; i < len; i++) {
4321                     createDom(cn[i], el);
4322                 }
4323             }else{
4324                 createDom(cn, el);
4325             }
4326         }
4327         if(o.html){
4328             el.innerHTML = o.html;
4329         }
4330         if(parentNode){
4331            parentNode.appendChild(el);
4332         }
4333         return el;
4334     };
4335
4336     var ieTable = function(depth, s, h, e){
4337         tempTableEl.innerHTML = [s, h, e].join('');
4338         var i = -1, el = tempTableEl;
4339         while(++i < depth){
4340             el = el.firstChild;
4341         }
4342         return el;
4343     };
4344
4345     // kill repeat to save bytes
4346     var ts = '<table>',
4347         te = '</table>',
4348         tbs = ts+'<tbody>',
4349         tbe = '</tbody>'+te,
4350         trs = tbs + '<tr>',
4351         tre = '</tr>'+tbe;
4352
4353     /**
4354      * @ignore
4355      * Nasty code for IE's broken table implementation
4356      */
4357     var insertIntoTable = function(tag, where, el, html){
4358         if(!tempTableEl){
4359             tempTableEl = document.createElement('div');
4360         }
4361         var node;
4362         var before = null;
4363         if(tag == 'td'){
4364             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4365                 return;
4366             }
4367             if(where == 'beforebegin'){
4368                 before = el;
4369                 el = el.parentNode;
4370             } else{
4371                 before = el.nextSibling;
4372                 el = el.parentNode;
4373             }
4374             node = ieTable(4, trs, html, tre);
4375         }
4376         else if(tag == 'tr'){
4377             if(where == 'beforebegin'){
4378                 before = el;
4379                 el = el.parentNode;
4380                 node = ieTable(3, tbs, html, tbe);
4381             } else if(where == 'afterend'){
4382                 before = el.nextSibling;
4383                 el = el.parentNode;
4384                 node = ieTable(3, tbs, html, tbe);
4385             } else{ // INTO a TR
4386                 if(where == 'afterbegin'){
4387                     before = el.firstChild;
4388                 }
4389                 node = ieTable(4, trs, html, tre);
4390             }
4391         } else if(tag == 'tbody'){
4392             if(where == 'beforebegin'){
4393                 before = el;
4394                 el = el.parentNode;
4395                 node = ieTable(2, ts, html, te);
4396             } else if(where == 'afterend'){
4397                 before = el.nextSibling;
4398                 el = el.parentNode;
4399                 node = ieTable(2, ts, html, te);
4400             } else{
4401                 if(where == 'afterbegin'){
4402                     before = el.firstChild;
4403                 }
4404                 node = ieTable(3, tbs, html, tbe);
4405             }
4406         } else{ // TABLE
4407             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4408                 return;
4409             }
4410             if(where == 'afterbegin'){
4411                 before = el.firstChild;
4412             }
4413             node = ieTable(2, ts, html, te);
4414         }
4415         el.insertBefore(node, before);
4416         return node;
4417     };
4418
4419     return {
4420     /** True to force the use of DOM instead of html fragments @type Boolean */
4421     useDom : false,
4422
4423     /**
4424      * Returns the markup for the passed Element(s) config
4425      * @param {Object} o The Dom object spec (and children)
4426      * @return {String}
4427      */
4428     markup : function(o){
4429         return createHtml(o);
4430     },
4431
4432     /**
4433      * Applies a style specification to an element
4434      * @param {String/HTMLElement} el The element to apply styles to
4435      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4436      * a function which returns such a specification.
4437      */
4438     applyStyles : function(el, styles){
4439         if(styles){
4440            el = Roo.fly(el);
4441            if(typeof styles == "string"){
4442                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4443                var matches;
4444                while ((matches = re.exec(styles)) != null){
4445                    el.setStyle(matches[1], matches[2]);
4446                }
4447            }else if (typeof styles == "object"){
4448                for (var style in styles){
4449                   el.setStyle(style, styles[style]);
4450                }
4451            }else if (typeof styles == "function"){
4452                 Roo.DomHelper.applyStyles(el, styles.call());
4453            }
4454         }
4455     },
4456
4457     /**
4458      * Inserts an HTML fragment into the Dom
4459      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4460      * @param {HTMLElement} el The context element
4461      * @param {String} html The HTML fragmenet
4462      * @return {HTMLElement} The new node
4463      */
4464     insertHtml : function(where, el, html){
4465         where = where.toLowerCase();
4466         if(el.insertAdjacentHTML){
4467             if(tableRe.test(el.tagName)){
4468                 var rs;
4469                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4470                     return rs;
4471                 }
4472             }
4473             switch(where){
4474                 case "beforebegin":
4475                     el.insertAdjacentHTML('BeforeBegin', html);
4476                     return el.previousSibling;
4477                 case "afterbegin":
4478                     el.insertAdjacentHTML('AfterBegin', html);
4479                     return el.firstChild;
4480                 case "beforeend":
4481                     el.insertAdjacentHTML('BeforeEnd', html);
4482                     return el.lastChild;
4483                 case "afterend":
4484                     el.insertAdjacentHTML('AfterEnd', html);
4485                     return el.nextSibling;
4486             }
4487             throw 'Illegal insertion point -> "' + where + '"';
4488         }
4489         var range = el.ownerDocument.createRange();
4490         var frag;
4491         switch(where){
4492              case "beforebegin":
4493                 range.setStartBefore(el);
4494                 frag = range.createContextualFragment(html);
4495                 el.parentNode.insertBefore(frag, el);
4496                 return el.previousSibling;
4497              case "afterbegin":
4498                 if(el.firstChild){
4499                     range.setStartBefore(el.firstChild);
4500                     frag = range.createContextualFragment(html);
4501                     el.insertBefore(frag, el.firstChild);
4502                     return el.firstChild;
4503                 }else{
4504                     el.innerHTML = html;
4505                     return el.firstChild;
4506                 }
4507             case "beforeend":
4508                 if(el.lastChild){
4509                     range.setStartAfter(el.lastChild);
4510                     frag = range.createContextualFragment(html);
4511                     el.appendChild(frag);
4512                     return el.lastChild;
4513                 }else{
4514                     el.innerHTML = html;
4515                     return el.lastChild;
4516                 }
4517             case "afterend":
4518                 range.setStartAfter(el);
4519                 frag = range.createContextualFragment(html);
4520                 el.parentNode.insertBefore(frag, el.nextSibling);
4521                 return el.nextSibling;
4522             }
4523             throw 'Illegal insertion point -> "' + where + '"';
4524     },
4525
4526     /**
4527      * Creates new Dom element(s) and inserts them before el
4528      * @param {String/HTMLElement/Element} el The context element
4529      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4530      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4531      * @return {HTMLElement/Roo.Element} The new node
4532      */
4533     insertBefore : function(el, o, returnElement){
4534         return this.doInsert(el, o, returnElement, "beforeBegin");
4535     },
4536
4537     /**
4538      * Creates new Dom element(s) and inserts them after el
4539      * @param {String/HTMLElement/Element} el The context element
4540      * @param {Object} o The Dom object spec (and children)
4541      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4542      * @return {HTMLElement/Roo.Element} The new node
4543      */
4544     insertAfter : function(el, o, returnElement){
4545         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4546     },
4547
4548     /**
4549      * Creates new Dom element(s) and inserts them as the first child of el
4550      * @param {String/HTMLElement/Element} el The context element
4551      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4552      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4553      * @return {HTMLElement/Roo.Element} The new node
4554      */
4555     insertFirst : function(el, o, returnElement){
4556         return this.doInsert(el, o, returnElement, "afterBegin");
4557     },
4558
4559     // private
4560     doInsert : function(el, o, returnElement, pos, sibling){
4561         el = Roo.getDom(el);
4562         var newNode;
4563         if(this.useDom || o.ns){
4564             newNode = createDom(o, null);
4565             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4566         }else{
4567             var html = createHtml(o);
4568             newNode = this.insertHtml(pos, el, html);
4569         }
4570         return returnElement ? Roo.get(newNode, true) : newNode;
4571     },
4572
4573     /**
4574      * Creates new Dom element(s) and appends them to el
4575      * @param {String/HTMLElement/Element} el The context element
4576      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4577      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4578      * @return {HTMLElement/Roo.Element} The new node
4579      */
4580     append : function(el, o, returnElement){
4581         el = Roo.getDom(el);
4582         var newNode;
4583         if(this.useDom || o.ns){
4584             newNode = createDom(o, null);
4585             el.appendChild(newNode);
4586         }else{
4587             var html = createHtml(o);
4588             newNode = this.insertHtml("beforeEnd", el, html);
4589         }
4590         return returnElement ? Roo.get(newNode, true) : newNode;
4591     },
4592
4593     /**
4594      * Creates new Dom element(s) and overwrites the contents of el with them
4595      * @param {String/HTMLElement/Element} el The context element
4596      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4597      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4598      * @return {HTMLElement/Roo.Element} The new node
4599      */
4600     overwrite : function(el, o, returnElement){
4601         el = Roo.getDom(el);
4602         if (o.ns) {
4603           
4604             while (el.childNodes.length) {
4605                 el.removeChild(el.firstChild);
4606             }
4607             createDom(o, el);
4608         } else {
4609             el.innerHTML = createHtml(o);   
4610         }
4611         
4612         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4613     },
4614
4615     /**
4616      * Creates a new Roo.DomHelper.Template from the Dom object spec
4617      * @param {Object} o The Dom object spec (and children)
4618      * @return {Roo.DomHelper.Template} The new template
4619      */
4620     createTemplate : function(o){
4621         var html = createHtml(o);
4622         return new Roo.Template(html);
4623     }
4624     };
4625 }();
4626 /*
4627  * Based on:
4628  * Ext JS Library 1.1.1
4629  * Copyright(c) 2006-2007, Ext JS, LLC.
4630  *
4631  * Originally Released Under LGPL - original licence link has changed is not relivant.
4632  *
4633  * Fork - LGPL
4634  * <script type="text/javascript">
4635  */
4636  
4637 /**
4638 * @class Roo.Template
4639 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4640 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4641 * Usage:
4642 <pre><code>
4643 var t = new Roo.Template({
4644     html :  '&lt;div name="{id}"&gt;' + 
4645         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4646         '&lt;/div&gt;',
4647     myformat: function (value, allValues) {
4648         return 'XX' + value;
4649     }
4650 });
4651 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4652 </code></pre>
4653 * For more information see this blog post with examples:
4654 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4655      - Create Elements using DOM, HTML fragments and Templates</a>. 
4656 * @constructor
4657 * @param {Object} cfg - Configuration object.
4658 */
4659 Roo.Template = function(cfg){
4660     // BC!
4661     if(cfg instanceof Array){
4662         cfg = cfg.join("");
4663     }else if(arguments.length > 1){
4664         cfg = Array.prototype.join.call(arguments, "");
4665     }
4666     
4667     
4668     if (typeof(cfg) == 'object') {
4669         Roo.apply(this,cfg)
4670     } else {
4671         // bc
4672         this.html = cfg;
4673     }
4674     if (this.url) {
4675         this.load();
4676     }
4677     
4678 };
4679 Roo.Template.prototype = {
4680     
4681     /**
4682      * @cfg {String} url  The Url to load the template from. beware if you are loading from a url, the data may not be ready if you use it instantly..
4683      *                    it should be fixed so that template is observable...
4684      */
4685     url : false,
4686     /**
4687      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4688      */
4689     html : '',
4690     /**
4691      * Returns an HTML fragment of this template with the specified values applied.
4692      * @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'})
4693      * @return {String} The HTML fragment
4694      */
4695     applyTemplate : function(values){
4696         //Roo.log(["applyTemplate", values]);
4697         try {
4698            
4699             if(this.compiled){
4700                 return this.compiled(values);
4701             }
4702             var useF = this.disableFormats !== true;
4703             var fm = Roo.util.Format, tpl = this;
4704             var fn = function(m, name, format, args){
4705                 if(format && useF){
4706                     if(format.substr(0, 5) == "this."){
4707                         return tpl.call(format.substr(5), values[name], values);
4708                     }else{
4709                         if(args){
4710                             // quoted values are required for strings in compiled templates, 
4711                             // but for non compiled we need to strip them
4712                             // quoted reversed for jsmin
4713                             var re = /^\s*['"](.*)["']\s*$/;
4714                             args = args.split(',');
4715                             for(var i = 0, len = args.length; i < len; i++){
4716                                 args[i] = args[i].replace(re, "$1");
4717                             }
4718                             args = [values[name]].concat(args);
4719                         }else{
4720                             args = [values[name]];
4721                         }
4722                         return fm[format].apply(fm, args);
4723                     }
4724                 }else{
4725                     return values[name] !== undefined ? values[name] : "";
4726                 }
4727             };
4728             return this.html.replace(this.re, fn);
4729         } catch (e) {
4730             Roo.log(e);
4731             throw e;
4732         }
4733          
4734     },
4735     
4736     loading : false,
4737       
4738     load : function ()
4739     {
4740          
4741         if (this.loading) {
4742             return;
4743         }
4744         var _t = this;
4745         
4746         this.loading = true;
4747         this.compiled = false;
4748         
4749         var cx = new Roo.data.Connection();
4750         cx.request({
4751             url : this.url,
4752             method : 'GET',
4753             success : function (response) {
4754                 _t.loading = false;
4755                 _t.html = response.responseText;
4756                 _t.url = false;
4757                 _t.compile();
4758              },
4759             failure : function(response) {
4760                 Roo.log("Template failed to load from " + _t.url);
4761                 _t.loading = false;
4762             }
4763         });
4764     },
4765
4766     /**
4767      * Sets the HTML used as the template and optionally compiles it.
4768      * @param {String} html
4769      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4770      * @return {Roo.Template} this
4771      */
4772     set : function(html, compile){
4773         this.html = html;
4774         this.compiled = null;
4775         if(compile){
4776             this.compile();
4777         }
4778         return this;
4779     },
4780     
4781     /**
4782      * True to disable format functions (defaults to false)
4783      * @type Boolean
4784      */
4785     disableFormats : false,
4786     
4787     /**
4788     * The regular expression used to match template variables 
4789     * @type RegExp
4790     * @property 
4791     */
4792     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4793     
4794     /**
4795      * Compiles the template into an internal function, eliminating the RegEx overhead.
4796      * @return {Roo.Template} this
4797      */
4798     compile : function(){
4799         var fm = Roo.util.Format;
4800         var useF = this.disableFormats !== true;
4801         var sep = Roo.isGecko ? "+" : ",";
4802         var fn = function(m, name, format, args){
4803             if(format && useF){
4804                 args = args ? ',' + args : "";
4805                 if(format.substr(0, 5) != "this."){
4806                     format = "fm." + format + '(';
4807                 }else{
4808                     format = 'this.call("'+ format.substr(5) + '", ';
4809                     args = ", values";
4810                 }
4811             }else{
4812                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4813             }
4814             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4815         };
4816         var body;
4817         // branched to use + in gecko and [].join() in others
4818         if(Roo.isGecko){
4819             body = "this.compiled = function(values){ return '" +
4820                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4821                     "';};";
4822         }else{
4823             body = ["this.compiled = function(values){ return ['"];
4824             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4825             body.push("'].join('');};");
4826             body = body.join('');
4827         }
4828         /**
4829          * eval:var:values
4830          * eval:var:fm
4831          */
4832         eval(body);
4833         return this;
4834     },
4835     
4836     // private function used to call members
4837     call : function(fnName, value, allValues){
4838         return this[fnName](value, allValues);
4839     },
4840     
4841     /**
4842      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4843      * @param {String/HTMLElement/Roo.Element} el The context element
4844      * @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'})
4845      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4846      * @return {HTMLElement/Roo.Element} The new node or Element
4847      */
4848     insertFirst: function(el, values, returnElement){
4849         return this.doInsert('afterBegin', el, values, returnElement);
4850     },
4851
4852     /**
4853      * Applies the supplied values to the template and inserts the new node(s) before el.
4854      * @param {String/HTMLElement/Roo.Element} el The context element
4855      * @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'})
4856      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4857      * @return {HTMLElement/Roo.Element} The new node or Element
4858      */
4859     insertBefore: function(el, values, returnElement){
4860         return this.doInsert('beforeBegin', el, values, returnElement);
4861     },
4862
4863     /**
4864      * Applies the supplied values to the template and inserts the new node(s) after el.
4865      * @param {String/HTMLElement/Roo.Element} el The context element
4866      * @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'})
4867      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4868      * @return {HTMLElement/Roo.Element} The new node or Element
4869      */
4870     insertAfter : function(el, values, returnElement){
4871         return this.doInsert('afterEnd', el, values, returnElement);
4872     },
4873     
4874     /**
4875      * Applies the supplied values to the template and appends the new node(s) to el.
4876      * @param {String/HTMLElement/Roo.Element} el The context element
4877      * @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'})
4878      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4879      * @return {HTMLElement/Roo.Element} The new node or Element
4880      */
4881     append : function(el, values, returnElement){
4882         return this.doInsert('beforeEnd', el, values, returnElement);
4883     },
4884
4885     doInsert : function(where, el, values, returnEl){
4886         el = Roo.getDom(el);
4887         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4888         return returnEl ? Roo.get(newNode, true) : newNode;
4889     },
4890
4891     /**
4892      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4893      * @param {String/HTMLElement/Roo.Element} el The context element
4894      * @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'})
4895      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4896      * @return {HTMLElement/Roo.Element} The new node or Element
4897      */
4898     overwrite : function(el, values, returnElement){
4899         el = Roo.getDom(el);
4900         el.innerHTML = this.applyTemplate(values);
4901         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4902     }
4903 };
4904 /**
4905  * Alias for {@link #applyTemplate}
4906  * @method
4907  */
4908 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4909
4910 // backwards compat
4911 Roo.DomHelper.Template = Roo.Template;
4912
4913 /**
4914  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4915  * @param {String/HTMLElement} el A DOM element or its id
4916  * @returns {Roo.Template} The created template
4917  * @static
4918  */
4919 Roo.Template.from = function(el){
4920     el = Roo.getDom(el);
4921     return new Roo.Template(el.value || el.innerHTML);
4922 };/*
4923  * Based on:
4924  * Ext JS Library 1.1.1
4925  * Copyright(c) 2006-2007, Ext JS, LLC.
4926  *
4927  * Originally Released Under LGPL - original licence link has changed is not relivant.
4928  *
4929  * Fork - LGPL
4930  * <script type="text/javascript">
4931  */
4932  
4933
4934 /*
4935  * This is code is also distributed under MIT license for use
4936  * with jQuery and prototype JavaScript libraries.
4937  */
4938 /**
4939  * @class Roo.DomQuery
4940 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).
4941 <p>
4942 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>
4943
4944 <p>
4945 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.
4946 </p>
4947 <h4>Element Selectors:</h4>
4948 <ul class="list">
4949     <li> <b>*</b> any element</li>
4950     <li> <b>E</b> an element with the tag E</li>
4951     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4952     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4953     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4954     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4955 </ul>
4956 <h4>Attribute Selectors:</h4>
4957 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4958 <ul class="list">
4959     <li> <b>E[foo]</b> has an attribute "foo"</li>
4960     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4961     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4962     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4963     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4964     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4965     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4966 </ul>
4967 <h4>Pseudo Classes:</h4>
4968 <ul class="list">
4969     <li> <b>E:first-child</b> E is the first child of its parent</li>
4970     <li> <b>E:last-child</b> E is the last child of its parent</li>
4971     <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>
4972     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4973     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4974     <li> <b>E:only-child</b> E is the only child of its parent</li>
4975     <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>
4976     <li> <b>E:first</b> the first E in the resultset</li>
4977     <li> <b>E:last</b> the last E in the resultset</li>
4978     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4979     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4980     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4981     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4982     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4983     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4984     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4985     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4986     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4987 </ul>
4988 <h4>CSS Value Selectors:</h4>
4989 <ul class="list">
4990     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4991     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4992     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4993     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4994     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4995     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4996 </ul>
4997  * @singleton
4998  */
4999 Roo.DomQuery = function(){
5000     var cache = {}, simpleCache = {}, valueCache = {};
5001     var nonSpace = /\S/;
5002     var trimRe = /^\s+|\s+$/g;
5003     var tplRe = /\{(\d+)\}/g;
5004     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
5005     var tagTokenRe = /^(#)?([\w-\*]+)/;
5006     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
5007
5008     function child(p, index){
5009         var i = 0;
5010         var n = p.firstChild;
5011         while(n){
5012             if(n.nodeType == 1){
5013                if(++i == index){
5014                    return n;
5015                }
5016             }
5017             n = n.nextSibling;
5018         }
5019         return null;
5020     };
5021
5022     function next(n){
5023         while((n = n.nextSibling) && n.nodeType != 1);
5024         return n;
5025     };
5026
5027     function prev(n){
5028         while((n = n.previousSibling) && n.nodeType != 1);
5029         return n;
5030     };
5031
5032     function children(d){
5033         var n = d.firstChild, ni = -1;
5034             while(n){
5035                 var nx = n.nextSibling;
5036                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
5037                     d.removeChild(n);
5038                 }else{
5039                     n.nodeIndex = ++ni;
5040                 }
5041                 n = nx;
5042             }
5043             return this;
5044         };
5045
5046     function byClassName(c, a, v){
5047         if(!v){
5048             return c;
5049         }
5050         var r = [], ri = -1, cn;
5051         for(var i = 0, ci; ci = c[i]; i++){
5052             if((' '+ci.className+' ').indexOf(v) != -1){
5053                 r[++ri] = ci;
5054             }
5055         }
5056         return r;
5057     };
5058
5059     function attrValue(n, attr){
5060         if(!n.tagName && typeof n.length != "undefined"){
5061             n = n[0];
5062         }
5063         if(!n){
5064             return null;
5065         }
5066         if(attr == "for"){
5067             return n.htmlFor;
5068         }
5069         if(attr == "class" || attr == "className"){
5070             return n.className;
5071         }
5072         return n.getAttribute(attr) || n[attr];
5073
5074     };
5075
5076     function getNodes(ns, mode, tagName){
5077         var result = [], ri = -1, cs;
5078         if(!ns){
5079             return result;
5080         }
5081         tagName = tagName || "*";
5082         if(typeof ns.getElementsByTagName != "undefined"){
5083             ns = [ns];
5084         }
5085         if(!mode){
5086             for(var i = 0, ni; ni = ns[i]; i++){
5087                 cs = ni.getElementsByTagName(tagName);
5088                 for(var j = 0, ci; ci = cs[j]; j++){
5089                     result[++ri] = ci;
5090                 }
5091             }
5092         }else if(mode == "/" || mode == ">"){
5093             var utag = tagName.toUpperCase();
5094             for(var i = 0, ni, cn; ni = ns[i]; i++){
5095                 cn = ni.children || ni.childNodes;
5096                 for(var j = 0, cj; cj = cn[j]; j++){
5097                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5098                         result[++ri] = cj;
5099                     }
5100                 }
5101             }
5102         }else if(mode == "+"){
5103             var utag = tagName.toUpperCase();
5104             for(var i = 0, n; n = ns[i]; i++){
5105                 while((n = n.nextSibling) && n.nodeType != 1);
5106                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5107                     result[++ri] = n;
5108                 }
5109             }
5110         }else if(mode == "~"){
5111             for(var i = 0, n; n = ns[i]; i++){
5112                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5113                 if(n){
5114                     result[++ri] = n;
5115                 }
5116             }
5117         }
5118         return result;
5119     };
5120
5121     function concat(a, b){
5122         if(b.slice){
5123             return a.concat(b);
5124         }
5125         for(var i = 0, l = b.length; i < l; i++){
5126             a[a.length] = b[i];
5127         }
5128         return a;
5129     }
5130
5131     function byTag(cs, tagName){
5132         if(cs.tagName || cs == document){
5133             cs = [cs];
5134         }
5135         if(!tagName){
5136             return cs;
5137         }
5138         var r = [], ri = -1;
5139         tagName = tagName.toLowerCase();
5140         for(var i = 0, ci; ci = cs[i]; i++){
5141             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5142                 r[++ri] = ci;
5143             }
5144         }
5145         return r;
5146     };
5147
5148     function byId(cs, attr, id){
5149         if(cs.tagName || cs == document){
5150             cs = [cs];
5151         }
5152         if(!id){
5153             return cs;
5154         }
5155         var r = [], ri = -1;
5156         for(var i = 0,ci; ci = cs[i]; i++){
5157             if(ci && ci.id == id){
5158                 r[++ri] = ci;
5159                 return r;
5160             }
5161         }
5162         return r;
5163     };
5164
5165     function byAttribute(cs, attr, value, op, custom){
5166         var r = [], ri = -1, st = custom=="{";
5167         var f = Roo.DomQuery.operators[op];
5168         for(var i = 0, ci; ci = cs[i]; i++){
5169             var a;
5170             if(st){
5171                 a = Roo.DomQuery.getStyle(ci, attr);
5172             }
5173             else if(attr == "class" || attr == "className"){
5174                 a = ci.className;
5175             }else if(attr == "for"){
5176                 a = ci.htmlFor;
5177             }else if(attr == "href"){
5178                 a = ci.getAttribute("href", 2);
5179             }else{
5180                 a = ci.getAttribute(attr);
5181             }
5182             if((f && f(a, value)) || (!f && a)){
5183                 r[++ri] = ci;
5184             }
5185         }
5186         return r;
5187     };
5188
5189     function byPseudo(cs, name, value){
5190         return Roo.DomQuery.pseudos[name](cs, value);
5191     };
5192
5193     // This is for IE MSXML which does not support expandos.
5194     // IE runs the same speed using setAttribute, however FF slows way down
5195     // and Safari completely fails so they need to continue to use expandos.
5196     var isIE = window.ActiveXObject ? true : false;
5197
5198     // this eval is stop the compressor from
5199     // renaming the variable to something shorter
5200     
5201     /** eval:var:batch */
5202     var batch = 30803; 
5203
5204     var key = 30803;
5205
5206     function nodupIEXml(cs){
5207         var d = ++key;
5208         cs[0].setAttribute("_nodup", d);
5209         var r = [cs[0]];
5210         for(var i = 1, len = cs.length; i < len; i++){
5211             var c = cs[i];
5212             if(!c.getAttribute("_nodup") != d){
5213                 c.setAttribute("_nodup", d);
5214                 r[r.length] = c;
5215             }
5216         }
5217         for(var i = 0, len = cs.length; i < len; i++){
5218             cs[i].removeAttribute("_nodup");
5219         }
5220         return r;
5221     }
5222
5223     function nodup(cs){
5224         if(!cs){
5225             return [];
5226         }
5227         var len = cs.length, c, i, r = cs, cj, ri = -1;
5228         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5229             return cs;
5230         }
5231         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5232             return nodupIEXml(cs);
5233         }
5234         var d = ++key;
5235         cs[0]._nodup = d;
5236         for(i = 1; c = cs[i]; i++){
5237             if(c._nodup != d){
5238                 c._nodup = d;
5239             }else{
5240                 r = [];
5241                 for(var j = 0; j < i; j++){
5242                     r[++ri] = cs[j];
5243                 }
5244                 for(j = i+1; cj = cs[j]; j++){
5245                     if(cj._nodup != d){
5246                         cj._nodup = d;
5247                         r[++ri] = cj;
5248                     }
5249                 }
5250                 return r;
5251             }
5252         }
5253         return r;
5254     }
5255
5256     function quickDiffIEXml(c1, c2){
5257         var d = ++key;
5258         for(var i = 0, len = c1.length; i < len; i++){
5259             c1[i].setAttribute("_qdiff", d);
5260         }
5261         var r = [];
5262         for(var i = 0, len = c2.length; i < len; i++){
5263             if(c2[i].getAttribute("_qdiff") != d){
5264                 r[r.length] = c2[i];
5265             }
5266         }
5267         for(var i = 0, len = c1.length; i < len; i++){
5268            c1[i].removeAttribute("_qdiff");
5269         }
5270         return r;
5271     }
5272
5273     function quickDiff(c1, c2){
5274         var len1 = c1.length;
5275         if(!len1){
5276             return c2;
5277         }
5278         if(isIE && c1[0].selectSingleNode){
5279             return quickDiffIEXml(c1, c2);
5280         }
5281         var d = ++key;
5282         for(var i = 0; i < len1; i++){
5283             c1[i]._qdiff = d;
5284         }
5285         var r = [];
5286         for(var i = 0, len = c2.length; i < len; i++){
5287             if(c2[i]._qdiff != d){
5288                 r[r.length] = c2[i];
5289             }
5290         }
5291         return r;
5292     }
5293
5294     function quickId(ns, mode, root, id){
5295         if(ns == root){
5296            var d = root.ownerDocument || root;
5297            return d.getElementById(id);
5298         }
5299         ns = getNodes(ns, mode, "*");
5300         return byId(ns, null, id);
5301     }
5302
5303     return {
5304         getStyle : function(el, name){
5305             return Roo.fly(el).getStyle(name);
5306         },
5307         /**
5308          * Compiles a selector/xpath query into a reusable function. The returned function
5309          * takes one parameter "root" (optional), which is the context node from where the query should start.
5310          * @param {String} selector The selector/xpath query
5311          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5312          * @return {Function}
5313          */
5314         compile : function(path, type){
5315             type = type || "select";
5316             
5317             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5318             var q = path, mode, lq;
5319             var tk = Roo.DomQuery.matchers;
5320             var tklen = tk.length;
5321             var mm;
5322
5323             // accept leading mode switch
5324             var lmode = q.match(modeRe);
5325             if(lmode && lmode[1]){
5326                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5327                 q = q.replace(lmode[1], "");
5328             }
5329             // strip leading slashes
5330             while(path.substr(0, 1)=="/"){
5331                 path = path.substr(1);
5332             }
5333
5334             while(q && lq != q){
5335                 lq = q;
5336                 var tm = q.match(tagTokenRe);
5337                 if(type == "select"){
5338                     if(tm){
5339                         if(tm[1] == "#"){
5340                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5341                         }else{
5342                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5343                         }
5344                         q = q.replace(tm[0], "");
5345                     }else if(q.substr(0, 1) != '@'){
5346                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5347                     }
5348                 }else{
5349                     if(tm){
5350                         if(tm[1] == "#"){
5351                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5352                         }else{
5353                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5354                         }
5355                         q = q.replace(tm[0], "");
5356                     }
5357                 }
5358                 while(!(mm = q.match(modeRe))){
5359                     var matched = false;
5360                     for(var j = 0; j < tklen; j++){
5361                         var t = tk[j];
5362                         var m = q.match(t.re);
5363                         if(m){
5364                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5365                                                     return m[i];
5366                                                 });
5367                             q = q.replace(m[0], "");
5368                             matched = true;
5369                             break;
5370                         }
5371                     }
5372                     // prevent infinite loop on bad selector
5373                     if(!matched){
5374                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5375                     }
5376                 }
5377                 if(mm[1]){
5378                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5379                     q = q.replace(mm[1], "");
5380                 }
5381             }
5382             fn[fn.length] = "return nodup(n);\n}";
5383             
5384              /** 
5385               * list of variables that need from compression as they are used by eval.
5386              *  eval:var:batch 
5387              *  eval:var:nodup
5388              *  eval:var:byTag
5389              *  eval:var:ById
5390              *  eval:var:getNodes
5391              *  eval:var:quickId
5392              *  eval:var:mode
5393              *  eval:var:root
5394              *  eval:var:n
5395              *  eval:var:byClassName
5396              *  eval:var:byPseudo
5397              *  eval:var:byAttribute
5398              *  eval:var:attrValue
5399              * 
5400              **/ 
5401             eval(fn.join(""));
5402             return f;
5403         },
5404
5405         /**
5406          * Selects a group of elements.
5407          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5408          * @param {Node} root (optional) The start of the query (defaults to document).
5409          * @return {Array}
5410          */
5411         select : function(path, root, type){
5412             if(!root || root == document){
5413                 root = document;
5414             }
5415             if(typeof root == "string"){
5416                 root = document.getElementById(root);
5417             }
5418             var paths = path.split(",");
5419             var results = [];
5420             for(var i = 0, len = paths.length; i < len; i++){
5421                 var p = paths[i].replace(trimRe, "");
5422                 if(!cache[p]){
5423                     cache[p] = Roo.DomQuery.compile(p);
5424                     if(!cache[p]){
5425                         throw p + " is not a valid selector";
5426                     }
5427                 }
5428                 var result = cache[p](root);
5429                 if(result && result != document){
5430                     results = results.concat(result);
5431                 }
5432             }
5433             if(paths.length > 1){
5434                 return nodup(results);
5435             }
5436             return results;
5437         },
5438
5439         /**
5440          * Selects a single element.
5441          * @param {String} selector The selector/xpath query
5442          * @param {Node} root (optional) The start of the query (defaults to document).
5443          * @return {Element}
5444          */
5445         selectNode : function(path, root){
5446             return Roo.DomQuery.select(path, root)[0];
5447         },
5448
5449         /**
5450          * Selects the value of a node, optionally replacing null with the defaultValue.
5451          * @param {String} selector The selector/xpath query
5452          * @param {Node} root (optional) The start of the query (defaults to document).
5453          * @param {String} defaultValue
5454          */
5455         selectValue : function(path, root, defaultValue){
5456             path = path.replace(trimRe, "");
5457             if(!valueCache[path]){
5458                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5459             }
5460             var n = valueCache[path](root);
5461             n = n[0] ? n[0] : n;
5462             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5463             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5464         },
5465
5466         /**
5467          * Selects the value of a node, parsing integers and floats.
5468          * @param {String} selector The selector/xpath query
5469          * @param {Node} root (optional) The start of the query (defaults to document).
5470          * @param {Number} defaultValue
5471          * @return {Number}
5472          */
5473         selectNumber : function(path, root, defaultValue){
5474             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5475             return parseFloat(v);
5476         },
5477
5478         /**
5479          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5480          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5481          * @param {String} selector The simple selector to test
5482          * @return {Boolean}
5483          */
5484         is : function(el, ss){
5485             if(typeof el == "string"){
5486                 el = document.getElementById(el);
5487             }
5488             var isArray = (el instanceof Array);
5489             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5490             return isArray ? (result.length == el.length) : (result.length > 0);
5491         },
5492
5493         /**
5494          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5495          * @param {Array} el An array of elements to filter
5496          * @param {String} selector The simple selector to test
5497          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5498          * the selector instead of the ones that match
5499          * @return {Array}
5500          */
5501         filter : function(els, ss, nonMatches){
5502             ss = ss.replace(trimRe, "");
5503             if(!simpleCache[ss]){
5504                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5505             }
5506             var result = simpleCache[ss](els);
5507             return nonMatches ? quickDiff(result, els) : result;
5508         },
5509
5510         /**
5511          * Collection of matching regular expressions and code snippets.
5512          */
5513         matchers : [{
5514                 re: /^\.([\w-]+)/,
5515                 select: 'n = byClassName(n, null, " {1} ");'
5516             }, {
5517                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5518                 select: 'n = byPseudo(n, "{1}", "{2}");'
5519             },{
5520                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5521                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5522             }, {
5523                 re: /^#([\w-]+)/,
5524                 select: 'n = byId(n, null, "{1}");'
5525             },{
5526                 re: /^@([\w-]+)/,
5527                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5528             }
5529         ],
5530
5531         /**
5532          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5533          * 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;.
5534          */
5535         operators : {
5536             "=" : function(a, v){
5537                 return a == v;
5538             },
5539             "!=" : function(a, v){
5540                 return a != v;
5541             },
5542             "^=" : function(a, v){
5543                 return a && a.substr(0, v.length) == v;
5544             },
5545             "$=" : function(a, v){
5546                 return a && a.substr(a.length-v.length) == v;
5547             },
5548             "*=" : function(a, v){
5549                 return a && a.indexOf(v) !== -1;
5550             },
5551             "%=" : function(a, v){
5552                 return (a % v) == 0;
5553             },
5554             "|=" : function(a, v){
5555                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5556             },
5557             "~=" : function(a, v){
5558                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5559             }
5560         },
5561
5562         /**
5563          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5564          * and the argument (if any) supplied in the selector.
5565          */
5566         pseudos : {
5567             "first-child" : function(c){
5568                 var r = [], ri = -1, n;
5569                 for(var i = 0, ci; ci = n = c[i]; i++){
5570                     while((n = n.previousSibling) && n.nodeType != 1);
5571                     if(!n){
5572                         r[++ri] = ci;
5573                     }
5574                 }
5575                 return r;
5576             },
5577
5578             "last-child" : function(c){
5579                 var r = [], ri = -1, n;
5580                 for(var i = 0, ci; ci = n = c[i]; i++){
5581                     while((n = n.nextSibling) && n.nodeType != 1);
5582                     if(!n){
5583                         r[++ri] = ci;
5584                     }
5585                 }
5586                 return r;
5587             },
5588
5589             "nth-child" : function(c, a) {
5590                 var r = [], ri = -1;
5591                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5592                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5593                 for(var i = 0, n; n = c[i]; i++){
5594                     var pn = n.parentNode;
5595                     if (batch != pn._batch) {
5596                         var j = 0;
5597                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5598                             if(cn.nodeType == 1){
5599                                cn.nodeIndex = ++j;
5600                             }
5601                         }
5602                         pn._batch = batch;
5603                     }
5604                     if (f == 1) {
5605                         if (l == 0 || n.nodeIndex == l){
5606                             r[++ri] = n;
5607                         }
5608                     } else if ((n.nodeIndex + l) % f == 0){
5609                         r[++ri] = n;
5610                     }
5611                 }
5612
5613                 return r;
5614             },
5615
5616             "only-child" : function(c){
5617                 var r = [], ri = -1;;
5618                 for(var i = 0, ci; ci = c[i]; i++){
5619                     if(!prev(ci) && !next(ci)){
5620                         r[++ri] = ci;
5621                     }
5622                 }
5623                 return r;
5624             },
5625
5626             "empty" : function(c){
5627                 var r = [], ri = -1;
5628                 for(var i = 0, ci; ci = c[i]; i++){
5629                     var cns = ci.childNodes, j = 0, cn, empty = true;
5630                     while(cn = cns[j]){
5631                         ++j;
5632                         if(cn.nodeType == 1 || cn.nodeType == 3){
5633                             empty = false;
5634                             break;
5635                         }
5636                     }
5637                     if(empty){
5638                         r[++ri] = ci;
5639                     }
5640                 }
5641                 return r;
5642             },
5643
5644             "contains" : function(c, v){
5645                 var r = [], ri = -1;
5646                 for(var i = 0, ci; ci = c[i]; i++){
5647                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5648                         r[++ri] = ci;
5649                     }
5650                 }
5651                 return r;
5652             },
5653
5654             "nodeValue" : function(c, v){
5655                 var r = [], ri = -1;
5656                 for(var i = 0, ci; ci = c[i]; i++){
5657                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5658                         r[++ri] = ci;
5659                     }
5660                 }
5661                 return r;
5662             },
5663
5664             "checked" : function(c){
5665                 var r = [], ri = -1;
5666                 for(var i = 0, ci; ci = c[i]; i++){
5667                     if(ci.checked == true){
5668                         r[++ri] = ci;
5669                     }
5670                 }
5671                 return r;
5672             },
5673
5674             "not" : function(c, ss){
5675                 return Roo.DomQuery.filter(c, ss, true);
5676             },
5677
5678             "odd" : function(c){
5679                 return this["nth-child"](c, "odd");
5680             },
5681
5682             "even" : function(c){
5683                 return this["nth-child"](c, "even");
5684             },
5685
5686             "nth" : function(c, a){
5687                 return c[a-1] || [];
5688             },
5689
5690             "first" : function(c){
5691                 return c[0] || [];
5692             },
5693
5694             "last" : function(c){
5695                 return c[c.length-1] || [];
5696             },
5697
5698             "has" : function(c, ss){
5699                 var s = Roo.DomQuery.select;
5700                 var r = [], ri = -1;
5701                 for(var i = 0, ci; ci = c[i]; i++){
5702                     if(s(ss, ci).length > 0){
5703                         r[++ri] = ci;
5704                     }
5705                 }
5706                 return r;
5707             },
5708
5709             "next" : function(c, ss){
5710                 var is = Roo.DomQuery.is;
5711                 var r = [], ri = -1;
5712                 for(var i = 0, ci; ci = c[i]; i++){
5713                     var n = next(ci);
5714                     if(n && is(n, ss)){
5715                         r[++ri] = ci;
5716                     }
5717                 }
5718                 return r;
5719             },
5720
5721             "prev" : function(c, ss){
5722                 var is = Roo.DomQuery.is;
5723                 var r = [], ri = -1;
5724                 for(var i = 0, ci; ci = c[i]; i++){
5725                     var n = prev(ci);
5726                     if(n && is(n, ss)){
5727                         r[++ri] = ci;
5728                     }
5729                 }
5730                 return r;
5731             }
5732         }
5733     };
5734 }();
5735
5736 /**
5737  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5738  * @param {String} path The selector/xpath query
5739  * @param {Node} root (optional) The start of the query (defaults to document).
5740  * @return {Array}
5741  * @member Roo
5742  * @method query
5743  */
5744 Roo.query = Roo.DomQuery.select;
5745 /*
5746  * Based on:
5747  * Ext JS Library 1.1.1
5748  * Copyright(c) 2006-2007, Ext JS, LLC.
5749  *
5750  * Originally Released Under LGPL - original licence link has changed is not relivant.
5751  *
5752  * Fork - LGPL
5753  * <script type="text/javascript">
5754  */
5755
5756 /**
5757  * @class Roo.util.Observable
5758  * Base class that provides a common interface for publishing events. Subclasses are expected to
5759  * to have a property "events" with all the events defined.<br>
5760  * For example:
5761  * <pre><code>
5762  Employee = function(name){
5763     this.name = name;
5764     this.addEvents({
5765         "fired" : true,
5766         "quit" : true
5767     });
5768  }
5769  Roo.extend(Employee, Roo.util.Observable);
5770 </code></pre>
5771  * @param {Object} config properties to use (incuding events / listeners)
5772  */
5773
5774 Roo.util.Observable = function(cfg){
5775     
5776     cfg = cfg|| {};
5777     this.addEvents(cfg.events || {});
5778     if (cfg.events) {
5779         delete cfg.events; // make sure
5780     }
5781      
5782     Roo.apply(this, cfg);
5783     
5784     if(this.listeners){
5785         this.on(this.listeners);
5786         delete this.listeners;
5787     }
5788 };
5789 Roo.util.Observable.prototype = {
5790     /** 
5791  * @cfg {Object} listeners  list of events and functions to call for this object, 
5792  * For example :
5793  * <pre><code>
5794     listeners :  { 
5795        'click' : function(e) {
5796            ..... 
5797         } ,
5798         .... 
5799     } 
5800   </code></pre>
5801  */
5802     
5803     
5804     /**
5805      * Fires the specified event with the passed parameters (minus the event name).
5806      * @param {String} eventName
5807      * @param {Object...} args Variable number of parameters are passed to handlers
5808      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5809      */
5810     fireEvent : function(){
5811         var ce = this.events[arguments[0].toLowerCase()];
5812         if(typeof ce == "object"){
5813             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5814         }else{
5815             return true;
5816         }
5817     },
5818
5819     // private
5820     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5821
5822     /**
5823      * Appends an event handler to this component
5824      * @param {String}   eventName The type of event to listen for
5825      * @param {Function} handler The method the event invokes
5826      * @param {Object}   scope (optional) The scope in which to execute the handler
5827      * function. The handler function's "this" context.
5828      * @param {Object}   options (optional) An object containing handler configuration
5829      * properties. This may contain any of the following properties:<ul>
5830      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5831      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5832      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5833      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5834      * by the specified number of milliseconds. If the event fires again within that time, the original
5835      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5836      * </ul><br>
5837      * <p>
5838      * <b>Combining Options</b><br>
5839      * Using the options argument, it is possible to combine different types of listeners:<br>
5840      * <br>
5841      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5842                 <pre><code>
5843                 el.on('click', this.onClick, this, {
5844                         single: true,
5845                 delay: 100,
5846                 forumId: 4
5847                 });
5848                 </code></pre>
5849      * <p>
5850      * <b>Attaching multiple handlers in 1 call</b><br>
5851      * The method also allows for a single argument to be passed which is a config object containing properties
5852      * which specify multiple handlers.
5853      * <pre><code>
5854                 el.on({
5855                         'click': {
5856                         fn: this.onClick,
5857                         scope: this,
5858                         delay: 100
5859                 }, 
5860                 'mouseover': {
5861                         fn: this.onMouseOver,
5862                         scope: this
5863                 },
5864                 'mouseout': {
5865                         fn: this.onMouseOut,
5866                         scope: this
5867                 }
5868                 });
5869                 </code></pre>
5870      * <p>
5871      * Or a shorthand syntax which passes the same scope object to all handlers:
5872         <pre><code>
5873                 el.on({
5874                         'click': this.onClick,
5875                 'mouseover': this.onMouseOver,
5876                 'mouseout': this.onMouseOut,
5877                 scope: this
5878                 });
5879                 </code></pre>
5880      */
5881     addListener : function(eventName, fn, scope, o){
5882         if(typeof eventName == "object"){
5883             o = eventName;
5884             for(var e in o){
5885                 if(this.filterOptRe.test(e)){
5886                     continue;
5887                 }
5888                 if(typeof o[e] == "function"){
5889                     // shared options
5890                     this.addListener(e, o[e], o.scope,  o);
5891                 }else{
5892                     // individual options
5893                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5894                 }
5895             }
5896             return;
5897         }
5898         o = (!o || typeof o == "boolean") ? {} : o;
5899         eventName = eventName.toLowerCase();
5900         var ce = this.events[eventName] || true;
5901         if(typeof ce == "boolean"){
5902             ce = new Roo.util.Event(this, eventName);
5903             this.events[eventName] = ce;
5904         }
5905         ce.addListener(fn, scope, o);
5906     },
5907
5908     /**
5909      * Removes a listener
5910      * @param {String}   eventName     The type of event to listen for
5911      * @param {Function} handler        The handler to remove
5912      * @param {Object}   scope  (optional) The scope (this object) for the handler
5913      */
5914     removeListener : function(eventName, fn, scope){
5915         var ce = this.events[eventName.toLowerCase()];
5916         if(typeof ce == "object"){
5917             ce.removeListener(fn, scope);
5918         }
5919     },
5920
5921     /**
5922      * Removes all listeners for this object
5923      */
5924     purgeListeners : function(){
5925         for(var evt in this.events){
5926             if(typeof this.events[evt] == "object"){
5927                  this.events[evt].clearListeners();
5928             }
5929         }
5930     },
5931
5932     relayEvents : function(o, events){
5933         var createHandler = function(ename){
5934             return function(){
5935                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5936             };
5937         };
5938         for(var i = 0, len = events.length; i < len; i++){
5939             var ename = events[i];
5940             if(!this.events[ename]){ this.events[ename] = true; };
5941             o.on(ename, createHandler(ename), this);
5942         }
5943     },
5944
5945     /**
5946      * Used to define events on this Observable
5947      * @param {Object} object The object with the events defined
5948      */
5949     addEvents : function(o){
5950         if(!this.events){
5951             this.events = {};
5952         }
5953         Roo.applyIf(this.events, o);
5954     },
5955
5956     /**
5957      * Checks to see if this object has any listeners for a specified event
5958      * @param {String} eventName The name of the event to check for
5959      * @return {Boolean} True if the event is being listened for, else false
5960      */
5961     hasListener : function(eventName){
5962         var e = this.events[eventName];
5963         return typeof e == "object" && e.listeners.length > 0;
5964     }
5965 };
5966 /**
5967  * Appends an event handler to this element (shorthand for addListener)
5968  * @param {String}   eventName     The type of event to listen for
5969  * @param {Function} handler        The method the event invokes
5970  * @param {Object}   scope (optional) The scope in which to execute the handler
5971  * function. The handler function's "this" context.
5972  * @param {Object}   options  (optional)
5973  * @method
5974  */
5975 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5976 /**
5977  * Removes a listener (shorthand for removeListener)
5978  * @param {String}   eventName     The type of event to listen for
5979  * @param {Function} handler        The handler to remove
5980  * @param {Object}   scope  (optional) The scope (this object) for the handler
5981  * @method
5982  */
5983 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5984
5985 /**
5986  * Starts capture on the specified Observable. All events will be passed
5987  * to the supplied function with the event name + standard signature of the event
5988  * <b>before</b> the event is fired. If the supplied function returns false,
5989  * the event will not fire.
5990  * @param {Observable} o The Observable to capture
5991  * @param {Function} fn The function to call
5992  * @param {Object} scope (optional) The scope (this object) for the fn
5993  * @static
5994  */
5995 Roo.util.Observable.capture = function(o, fn, scope){
5996     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5997 };
5998
5999 /**
6000  * Removes <b>all</b> added captures from the Observable.
6001  * @param {Observable} o The Observable to release
6002  * @static
6003  */
6004 Roo.util.Observable.releaseCapture = function(o){
6005     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
6006 };
6007
6008 (function(){
6009
6010     var createBuffered = function(h, o, scope){
6011         var task = new Roo.util.DelayedTask();
6012         return function(){
6013             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
6014         };
6015     };
6016
6017     var createSingle = function(h, e, fn, scope){
6018         return function(){
6019             e.removeListener(fn, scope);
6020             return h.apply(scope, arguments);
6021         };
6022     };
6023
6024     var createDelayed = function(h, o, scope){
6025         return function(){
6026             var args = Array.prototype.slice.call(arguments, 0);
6027             setTimeout(function(){
6028                 h.apply(scope, args);
6029             }, o.delay || 10);
6030         };
6031     };
6032
6033     Roo.util.Event = function(obj, name){
6034         this.name = name;
6035         this.obj = obj;
6036         this.listeners = [];
6037     };
6038
6039     Roo.util.Event.prototype = {
6040         addListener : function(fn, scope, options){
6041             var o = options || {};
6042             scope = scope || this.obj;
6043             if(!this.isListening(fn, scope)){
6044                 var l = {fn: fn, scope: scope, options: o};
6045                 var h = fn;
6046                 if(o.delay){
6047                     h = createDelayed(h, o, scope);
6048                 }
6049                 if(o.single){
6050                     h = createSingle(h, this, fn, scope);
6051                 }
6052                 if(o.buffer){
6053                     h = createBuffered(h, o, scope);
6054                 }
6055                 l.fireFn = h;
6056                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
6057                     this.listeners.push(l);
6058                 }else{
6059                     this.listeners = this.listeners.slice(0);
6060                     this.listeners.push(l);
6061                 }
6062             }
6063         },
6064
6065         findListener : function(fn, scope){
6066             scope = scope || this.obj;
6067             var ls = this.listeners;
6068             for(var i = 0, len = ls.length; i < len; i++){
6069                 var l = ls[i];
6070                 if(l.fn == fn && l.scope == scope){
6071                     return i;
6072                 }
6073             }
6074             return -1;
6075         },
6076
6077         isListening : function(fn, scope){
6078             return this.findListener(fn, scope) != -1;
6079         },
6080
6081         removeListener : function(fn, scope){
6082             var index;
6083             if((index = this.findListener(fn, scope)) != -1){
6084                 if(!this.firing){
6085                     this.listeners.splice(index, 1);
6086                 }else{
6087                     this.listeners = this.listeners.slice(0);
6088                     this.listeners.splice(index, 1);
6089                 }
6090                 return true;
6091             }
6092             return false;
6093         },
6094
6095         clearListeners : function(){
6096             this.listeners = [];
6097         },
6098
6099         fire : function(){
6100             var ls = this.listeners, scope, len = ls.length;
6101             if(len > 0){
6102                 this.firing = true;
6103                 var args = Array.prototype.slice.call(arguments, 0);                
6104                 for(var i = 0; i < len; i++){
6105                     var l = ls[i];
6106                     if(l.fireFn.apply(l.scope||this.obj||window, args) === false){
6107                         this.firing = false;
6108                         return false;
6109                     }
6110                 }
6111                 this.firing = false;
6112             }
6113             return true;
6114         }
6115     };
6116 })();/*
6117  * RooJS Library 
6118  * Copyright(c) 2007-2017, Roo J Solutions Ltd
6119  *
6120  * Licence LGPL 
6121  *
6122  */
6123  
6124 /**
6125  * @class Roo.Document
6126  * @extends Roo.util.Observable
6127  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
6128  * 
6129  * @param {Object} config the methods and properties of the 'base' class for the application.
6130  * 
6131  *  Generic Page handler - implement this to start your app..
6132  * 
6133  * eg.
6134  *  MyProject = new Roo.Document({
6135         events : {
6136             'load' : true // your events..
6137         },
6138         listeners : {
6139             'ready' : function() {
6140                 // fired on Roo.onReady()
6141             }
6142         }
6143  * 
6144  */
6145 Roo.Document = function(cfg) {
6146      
6147     this.addEvents({ 
6148         'ready' : true
6149     });
6150     Roo.util.Observable.call(this,cfg);
6151     
6152     var _this = this;
6153     
6154     Roo.onReady(function() {
6155         _this.fireEvent('ready');
6156     },null,false);
6157     
6158     
6159 }
6160
6161 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
6162  * Based on:
6163  * Ext JS Library 1.1.1
6164  * Copyright(c) 2006-2007, Ext JS, LLC.
6165  *
6166  * Originally Released Under LGPL - original licence link has changed is not relivant.
6167  *
6168  * Fork - LGPL
6169  * <script type="text/javascript">
6170  */
6171
6172 /**
6173  * @class Roo.EventManager
6174  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6175  * several useful events directly.
6176  * See {@link Roo.EventObject} for more details on normalized event objects.
6177  * @singleton
6178  */
6179 Roo.EventManager = function(){
6180     var docReadyEvent, docReadyProcId, docReadyState = false;
6181     var resizeEvent, resizeTask, textEvent, textSize;
6182     var E = Roo.lib.Event;
6183     var D = Roo.lib.Dom;
6184
6185     
6186     
6187
6188     var fireDocReady = function(){
6189         if(!docReadyState){
6190             docReadyState = true;
6191             Roo.isReady = true;
6192             if(docReadyProcId){
6193                 clearInterval(docReadyProcId);
6194             }
6195             if(Roo.isGecko || Roo.isOpera) {
6196                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6197             }
6198             if(Roo.isIE){
6199                 var defer = document.getElementById("ie-deferred-loader");
6200                 if(defer){
6201                     defer.onreadystatechange = null;
6202                     defer.parentNode.removeChild(defer);
6203                 }
6204             }
6205             if(docReadyEvent){
6206                 docReadyEvent.fire();
6207                 docReadyEvent.clearListeners();
6208             }
6209         }
6210     };
6211     
6212     var initDocReady = function(){
6213         docReadyEvent = new Roo.util.Event();
6214         if(Roo.isGecko || Roo.isOpera) {
6215             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6216         }else if(Roo.isIE){
6217             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6218             var defer = document.getElementById("ie-deferred-loader");
6219             defer.onreadystatechange = function(){
6220                 if(this.readyState == "complete"){
6221                     fireDocReady();
6222                 }
6223             };
6224         }else if(Roo.isSafari){ 
6225             docReadyProcId = setInterval(function(){
6226                 var rs = document.readyState;
6227                 if(rs == "complete") {
6228                     fireDocReady();     
6229                  }
6230             }, 10);
6231         }
6232         // no matter what, make sure it fires on load
6233         E.on(window, "load", fireDocReady);
6234     };
6235
6236     var createBuffered = function(h, o){
6237         var task = new Roo.util.DelayedTask(h);
6238         return function(e){
6239             // create new event object impl so new events don't wipe out properties
6240             e = new Roo.EventObjectImpl(e);
6241             task.delay(o.buffer, h, null, [e]);
6242         };
6243     };
6244
6245     var createSingle = function(h, el, ename, fn){
6246         return function(e){
6247             Roo.EventManager.removeListener(el, ename, fn);
6248             h(e);
6249         };
6250     };
6251
6252     var createDelayed = function(h, o){
6253         return function(e){
6254             // create new event object impl so new events don't wipe out properties
6255             e = new Roo.EventObjectImpl(e);
6256             setTimeout(function(){
6257                 h(e);
6258             }, o.delay || 10);
6259         };
6260     };
6261     var transitionEndVal = false;
6262     
6263     var transitionEnd = function()
6264     {
6265         if (transitionEndVal) {
6266             return transitionEndVal;
6267         }
6268         var el = document.createElement('div');
6269
6270         var transEndEventNames = {
6271             WebkitTransition : 'webkitTransitionEnd',
6272             MozTransition    : 'transitionend',
6273             OTransition      : 'oTransitionEnd otransitionend',
6274             transition       : 'transitionend'
6275         };
6276     
6277         for (var name in transEndEventNames) {
6278             if (el.style[name] !== undefined) {
6279                 transitionEndVal = transEndEventNames[name];
6280                 return  transitionEndVal ;
6281             }
6282         }
6283     }
6284     
6285
6286     var listen = function(element, ename, opt, fn, scope){
6287         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6288         fn = fn || o.fn; scope = scope || o.scope;
6289         var el = Roo.getDom(element);
6290         
6291         
6292         if(!el){
6293             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6294         }
6295         
6296         if (ename == 'transitionend') {
6297             ename = transitionEnd();
6298         }
6299         var h = function(e){
6300             e = Roo.EventObject.setEvent(e);
6301             var t;
6302             if(o.delegate){
6303                 t = e.getTarget(o.delegate, el);
6304                 if(!t){
6305                     return;
6306                 }
6307             }else{
6308                 t = e.target;
6309             }
6310             if(o.stopEvent === true){
6311                 e.stopEvent();
6312             }
6313             if(o.preventDefault === true){
6314                e.preventDefault();
6315             }
6316             if(o.stopPropagation === true){
6317                 e.stopPropagation();
6318             }
6319
6320             if(o.normalized === false){
6321                 e = e.browserEvent;
6322             }
6323
6324             fn.call(scope || el, e, t, o);
6325         };
6326         if(o.delay){
6327             h = createDelayed(h, o);
6328         }
6329         if(o.single){
6330             h = createSingle(h, el, ename, fn);
6331         }
6332         if(o.buffer){
6333             h = createBuffered(h, o);
6334         }
6335         
6336         fn._handlers = fn._handlers || [];
6337         
6338         
6339         fn._handlers.push([Roo.id(el), ename, h]);
6340         
6341         
6342          
6343         E.on(el, ename, h);
6344         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6345             el.addEventListener("DOMMouseScroll", h, false);
6346             E.on(window, 'unload', function(){
6347                 el.removeEventListener("DOMMouseScroll", h, false);
6348             });
6349         }
6350         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6351             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6352         }
6353         return h;
6354     };
6355
6356     var stopListening = function(el, ename, fn){
6357         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6358         if(hds){
6359             for(var i = 0, len = hds.length; i < len; i++){
6360                 var h = hds[i];
6361                 if(h[0] == id && h[1] == ename){
6362                     hd = h[2];
6363                     hds.splice(i, 1);
6364                     break;
6365                 }
6366             }
6367         }
6368         E.un(el, ename, hd);
6369         el = Roo.getDom(el);
6370         if(ename == "mousewheel" && el.addEventListener){
6371             el.removeEventListener("DOMMouseScroll", hd, false);
6372         }
6373         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6374             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6375         }
6376     };
6377
6378     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6379     
6380     var pub = {
6381         
6382         
6383         /** 
6384          * Fix for doc tools
6385          * @scope Roo.EventManager
6386          */
6387         
6388         
6389         /** 
6390          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6391          * object with a Roo.EventObject
6392          * @param {Function} fn        The method the event invokes
6393          * @param {Object}   scope    An object that becomes the scope of the handler
6394          * @param {boolean}  override If true, the obj passed in becomes
6395          *                             the execution scope of the listener
6396          * @return {Function} The wrapped function
6397          * @deprecated
6398          */
6399         wrap : function(fn, scope, override){
6400             return function(e){
6401                 Roo.EventObject.setEvent(e);
6402                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6403             };
6404         },
6405         
6406         /**
6407      * Appends an event handler to an element (shorthand for addListener)
6408      * @param {String/HTMLElement}   element        The html element or id to assign the
6409      * @param {String}   eventName The type of event to listen for
6410      * @param {Function} handler The method the event invokes
6411      * @param {Object}   scope (optional) The scope in which to execute the handler
6412      * function. The handler function's "this" context.
6413      * @param {Object}   options (optional) An object containing handler configuration
6414      * properties. This may contain any of the following properties:<ul>
6415      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6416      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6417      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6418      * <li>preventDefault {Boolean} True to prevent the default action</li>
6419      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6420      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6421      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6422      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6423      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6424      * by the specified number of milliseconds. If the event fires again within that time, the original
6425      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6426      * </ul><br>
6427      * <p>
6428      * <b>Combining Options</b><br>
6429      * Using the options argument, it is possible to combine different types of listeners:<br>
6430      * <br>
6431      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6432      * Code:<pre><code>
6433 el.on('click', this.onClick, this, {
6434     single: true,
6435     delay: 100,
6436     stopEvent : true,
6437     forumId: 4
6438 });</code></pre>
6439      * <p>
6440      * <b>Attaching multiple handlers in 1 call</b><br>
6441       * The method also allows for a single argument to be passed which is a config object containing properties
6442      * which specify multiple handlers.
6443      * <p>
6444      * Code:<pre><code>
6445 el.on({
6446     'click' : {
6447         fn: this.onClick
6448         scope: this,
6449         delay: 100
6450     },
6451     'mouseover' : {
6452         fn: this.onMouseOver
6453         scope: this
6454     },
6455     'mouseout' : {
6456         fn: this.onMouseOut
6457         scope: this
6458     }
6459 });</code></pre>
6460      * <p>
6461      * Or a shorthand syntax:<br>
6462      * Code:<pre><code>
6463 el.on({
6464     'click' : this.onClick,
6465     'mouseover' : this.onMouseOver,
6466     'mouseout' : this.onMouseOut
6467     scope: this
6468 });</code></pre>
6469      */
6470         addListener : function(element, eventName, fn, scope, options){
6471             if(typeof eventName == "object"){
6472                 var o = eventName;
6473                 for(var e in o){
6474                     if(propRe.test(e)){
6475                         continue;
6476                     }
6477                     if(typeof o[e] == "function"){
6478                         // shared options
6479                         listen(element, e, o, o[e], o.scope);
6480                     }else{
6481                         // individual options
6482                         listen(element, e, o[e]);
6483                     }
6484                 }
6485                 return;
6486             }
6487             return listen(element, eventName, options, fn, scope);
6488         },
6489         
6490         /**
6491          * Removes an event handler
6492          *
6493          * @param {String/HTMLElement}   element        The id or html element to remove the 
6494          *                             event from
6495          * @param {String}   eventName     The type of event
6496          * @param {Function} fn
6497          * @return {Boolean} True if a listener was actually removed
6498          */
6499         removeListener : function(element, eventName, fn){
6500             return stopListening(element, eventName, fn);
6501         },
6502         
6503         /**
6504          * Fires when the document is ready (before onload and before images are loaded). Can be 
6505          * accessed shorthanded Roo.onReady().
6506          * @param {Function} fn        The method the event invokes
6507          * @param {Object}   scope    An  object that becomes the scope of the handler
6508          * @param {boolean}  options
6509          */
6510         onDocumentReady : function(fn, scope, options){
6511             if(docReadyState){ // if it already fired
6512                 docReadyEvent.addListener(fn, scope, options);
6513                 docReadyEvent.fire();
6514                 docReadyEvent.clearListeners();
6515                 return;
6516             }
6517             if(!docReadyEvent){
6518                 initDocReady();
6519             }
6520             docReadyEvent.addListener(fn, scope, options);
6521         },
6522         
6523         /**
6524          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6525          * @param {Function} fn        The method the event invokes
6526          * @param {Object}   scope    An object that becomes the scope of the handler
6527          * @param {boolean}  options
6528          */
6529         onWindowResize : function(fn, scope, options){
6530             if(!resizeEvent){
6531                 resizeEvent = new Roo.util.Event();
6532                 resizeTask = new Roo.util.DelayedTask(function(){
6533                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6534                 });
6535                 E.on(window, "resize", function(){
6536                     if(Roo.isIE){
6537                         resizeTask.delay(50);
6538                     }else{
6539                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6540                     }
6541                 });
6542             }
6543             resizeEvent.addListener(fn, scope, options);
6544         },
6545
6546         /**
6547          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6548          * @param {Function} fn        The method the event invokes
6549          * @param {Object}   scope    An object that becomes the scope of the handler
6550          * @param {boolean}  options
6551          */
6552         onTextResize : function(fn, scope, options){
6553             if(!textEvent){
6554                 textEvent = new Roo.util.Event();
6555                 var textEl = new Roo.Element(document.createElement('div'));
6556                 textEl.dom.className = 'x-text-resize';
6557                 textEl.dom.innerHTML = 'X';
6558                 textEl.appendTo(document.body);
6559                 textSize = textEl.dom.offsetHeight;
6560                 setInterval(function(){
6561                     if(textEl.dom.offsetHeight != textSize){
6562                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6563                     }
6564                 }, this.textResizeInterval);
6565             }
6566             textEvent.addListener(fn, scope, options);
6567         },
6568
6569         /**
6570          * Removes the passed window resize listener.
6571          * @param {Function} fn        The method the event invokes
6572          * @param {Object}   scope    The scope of handler
6573          */
6574         removeResizeListener : function(fn, scope){
6575             if(resizeEvent){
6576                 resizeEvent.removeListener(fn, scope);
6577             }
6578         },
6579
6580         // private
6581         fireResize : function(){
6582             if(resizeEvent){
6583                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6584             }   
6585         },
6586         /**
6587          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6588          */
6589         ieDeferSrc : false,
6590         /**
6591          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6592          */
6593         textResizeInterval : 50
6594     };
6595     
6596     /**
6597      * Fix for doc tools
6598      * @scopeAlias pub=Roo.EventManager
6599      */
6600     
6601      /**
6602      * Appends an event handler to an element (shorthand for addListener)
6603      * @param {String/HTMLElement}   element        The html element or id to assign the
6604      * @param {String}   eventName The type of event to listen for
6605      * @param {Function} handler The method the event invokes
6606      * @param {Object}   scope (optional) The scope in which to execute the handler
6607      * function. The handler function's "this" context.
6608      * @param {Object}   options (optional) An object containing handler configuration
6609      * properties. This may contain any of the following properties:<ul>
6610      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6611      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6612      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6613      * <li>preventDefault {Boolean} True to prevent the default action</li>
6614      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6615      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6616      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6617      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6618      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6619      * by the specified number of milliseconds. If the event fires again within that time, the original
6620      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6621      * </ul><br>
6622      * <p>
6623      * <b>Combining Options</b><br>
6624      * Using the options argument, it is possible to combine different types of listeners:<br>
6625      * <br>
6626      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6627      * Code:<pre><code>
6628 el.on('click', this.onClick, this, {
6629     single: true,
6630     delay: 100,
6631     stopEvent : true,
6632     forumId: 4
6633 });</code></pre>
6634      * <p>
6635      * <b>Attaching multiple handlers in 1 call</b><br>
6636       * The method also allows for a single argument to be passed which is a config object containing properties
6637      * which specify multiple handlers.
6638      * <p>
6639      * Code:<pre><code>
6640 el.on({
6641     'click' : {
6642         fn: this.onClick
6643         scope: this,
6644         delay: 100
6645     },
6646     'mouseover' : {
6647         fn: this.onMouseOver
6648         scope: this
6649     },
6650     'mouseout' : {
6651         fn: this.onMouseOut
6652         scope: this
6653     }
6654 });</code></pre>
6655      * <p>
6656      * Or a shorthand syntax:<br>
6657      * Code:<pre><code>
6658 el.on({
6659     'click' : this.onClick,
6660     'mouseover' : this.onMouseOver,
6661     'mouseout' : this.onMouseOut
6662     scope: this
6663 });</code></pre>
6664      */
6665     pub.on = pub.addListener;
6666     pub.un = pub.removeListener;
6667
6668     pub.stoppedMouseDownEvent = new Roo.util.Event();
6669     return pub;
6670 }();
6671 /**
6672   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6673   * @param {Function} fn        The method the event invokes
6674   * @param {Object}   scope    An  object that becomes the scope of the handler
6675   * @param {boolean}  override If true, the obj passed in becomes
6676   *                             the execution scope of the listener
6677   * @member Roo
6678   * @method onReady
6679  */
6680 Roo.onReady = Roo.EventManager.onDocumentReady;
6681
6682 Roo.onReady(function(){
6683     var bd = Roo.get(document.body);
6684     if(!bd){ return; }
6685
6686     var cls = [
6687             Roo.isIE ? "roo-ie"
6688             : Roo.isIE11 ? "roo-ie11"
6689             : Roo.isEdge ? "roo-edge"
6690             : Roo.isGecko ? "roo-gecko"
6691             : Roo.isOpera ? "roo-opera"
6692             : Roo.isSafari ? "roo-safari" : ""];
6693
6694     if(Roo.isMac){
6695         cls.push("roo-mac");
6696     }
6697     if(Roo.isLinux){
6698         cls.push("roo-linux");
6699     }
6700     if(Roo.isIOS){
6701         cls.push("roo-ios");
6702     }
6703     if(Roo.isTouch){
6704         cls.push("roo-touch");
6705     }
6706     if(Roo.isBorderBox){
6707         cls.push('roo-border-box');
6708     }
6709     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6710         var p = bd.dom.parentNode;
6711         if(p){
6712             p.className += ' roo-strict';
6713         }
6714     }
6715     bd.addClass(cls.join(' '));
6716 });
6717
6718 /**
6719  * @class Roo.EventObject
6720  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6721  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6722  * Example:
6723  * <pre><code>
6724  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6725     e.preventDefault();
6726     var target = e.getTarget();
6727     ...
6728  }
6729  var myDiv = Roo.get("myDiv");
6730  myDiv.on("click", handleClick);
6731  //or
6732  Roo.EventManager.on("myDiv", 'click', handleClick);
6733  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6734  </code></pre>
6735  * @singleton
6736  */
6737 Roo.EventObject = function(){
6738     
6739     var E = Roo.lib.Event;
6740     
6741     // safari keypress events for special keys return bad keycodes
6742     var safariKeys = {
6743         63234 : 37, // left
6744         63235 : 39, // right
6745         63232 : 38, // up
6746         63233 : 40, // down
6747         63276 : 33, // page up
6748         63277 : 34, // page down
6749         63272 : 46, // delete
6750         63273 : 36, // home
6751         63275 : 35  // end
6752     };
6753
6754     // normalize button clicks
6755     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6756                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6757
6758     Roo.EventObjectImpl = function(e){
6759         if(e){
6760             this.setEvent(e.browserEvent || e);
6761         }
6762     };
6763     Roo.EventObjectImpl.prototype = {
6764         /**
6765          * Used to fix doc tools.
6766          * @scope Roo.EventObject.prototype
6767          */
6768             
6769
6770         
6771         
6772         /** The normal browser event */
6773         browserEvent : null,
6774         /** The button pressed in a mouse event */
6775         button : -1,
6776         /** True if the shift key was down during the event */
6777         shiftKey : false,
6778         /** True if the control key was down during the event */
6779         ctrlKey : false,
6780         /** True if the alt key was down during the event */
6781         altKey : false,
6782
6783         /** Key constant 
6784         * @type Number */
6785         BACKSPACE : 8,
6786         /** Key constant 
6787         * @type Number */
6788         TAB : 9,
6789         /** Key constant 
6790         * @type Number */
6791         RETURN : 13,
6792         /** Key constant 
6793         * @type Number */
6794         ENTER : 13,
6795         /** Key constant 
6796         * @type Number */
6797         SHIFT : 16,
6798         /** Key constant 
6799         * @type Number */
6800         CONTROL : 17,
6801         /** Key constant 
6802         * @type Number */
6803         ESC : 27,
6804         /** Key constant 
6805         * @type Number */
6806         SPACE : 32,
6807         /** Key constant 
6808         * @type Number */
6809         PAGEUP : 33,
6810         /** Key constant 
6811         * @type Number */
6812         PAGEDOWN : 34,
6813         /** Key constant 
6814         * @type Number */
6815         END : 35,
6816         /** Key constant 
6817         * @type Number */
6818         HOME : 36,
6819         /** Key constant 
6820         * @type Number */
6821         LEFT : 37,
6822         /** Key constant 
6823         * @type Number */
6824         UP : 38,
6825         /** Key constant 
6826         * @type Number */
6827         RIGHT : 39,
6828         /** Key constant 
6829         * @type Number */
6830         DOWN : 40,
6831         /** Key constant 
6832         * @type Number */
6833         DELETE : 46,
6834         /** Key constant 
6835         * @type Number */
6836         F5 : 116,
6837
6838            /** @private */
6839         setEvent : function(e){
6840             if(e == this || (e && e.browserEvent)){ // already wrapped
6841                 return e;
6842             }
6843             this.browserEvent = e;
6844             if(e){
6845                 // normalize buttons
6846                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6847                 if(e.type == 'click' && this.button == -1){
6848                     this.button = 0;
6849                 }
6850                 this.type = e.type;
6851                 this.shiftKey = e.shiftKey;
6852                 // mac metaKey behaves like ctrlKey
6853                 this.ctrlKey = e.ctrlKey || e.metaKey;
6854                 this.altKey = e.altKey;
6855                 // in getKey these will be normalized for the mac
6856                 this.keyCode = e.keyCode;
6857                 // keyup warnings on firefox.
6858                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6859                 // cache the target for the delayed and or buffered events
6860                 this.target = E.getTarget(e);
6861                 // same for XY
6862                 this.xy = E.getXY(e);
6863             }else{
6864                 this.button = -1;
6865                 this.shiftKey = false;
6866                 this.ctrlKey = false;
6867                 this.altKey = false;
6868                 this.keyCode = 0;
6869                 this.charCode =0;
6870                 this.target = null;
6871                 this.xy = [0, 0];
6872             }
6873             return this;
6874         },
6875
6876         /**
6877          * Stop the event (preventDefault and stopPropagation)
6878          */
6879         stopEvent : function(){
6880             if(this.browserEvent){
6881                 if(this.browserEvent.type == 'mousedown'){
6882                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6883                 }
6884                 E.stopEvent(this.browserEvent);
6885             }
6886         },
6887
6888         /**
6889          * Prevents the browsers default handling of the event.
6890          */
6891         preventDefault : function(){
6892             if(this.browserEvent){
6893                 E.preventDefault(this.browserEvent);
6894             }
6895         },
6896
6897         /** @private */
6898         isNavKeyPress : function(){
6899             var k = this.keyCode;
6900             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6901             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6902         },
6903
6904         isSpecialKey : function(){
6905             var k = this.keyCode;
6906             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6907             (k == 16) || (k == 17) ||
6908             (k >= 18 && k <= 20) ||
6909             (k >= 33 && k <= 35) ||
6910             (k >= 36 && k <= 39) ||
6911             (k >= 44 && k <= 45);
6912         },
6913         /**
6914          * Cancels bubbling of the event.
6915          */
6916         stopPropagation : function(){
6917             if(this.browserEvent){
6918                 if(this.type == 'mousedown'){
6919                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6920                 }
6921                 E.stopPropagation(this.browserEvent);
6922             }
6923         },
6924
6925         /**
6926          * Gets the key code for the event.
6927          * @return {Number}
6928          */
6929         getCharCode : function(){
6930             return this.charCode || this.keyCode;
6931         },
6932
6933         /**
6934          * Returns a normalized keyCode for the event.
6935          * @return {Number} The key code
6936          */
6937         getKey : function(){
6938             var k = this.keyCode || this.charCode;
6939             return Roo.isSafari ? (safariKeys[k] || k) : k;
6940         },
6941
6942         /**
6943          * Gets the x coordinate of the event.
6944          * @return {Number}
6945          */
6946         getPageX : function(){
6947             return this.xy[0];
6948         },
6949
6950         /**
6951          * Gets the y coordinate of the event.
6952          * @return {Number}
6953          */
6954         getPageY : function(){
6955             return this.xy[1];
6956         },
6957
6958         /**
6959          * Gets the time of the event.
6960          * @return {Number}
6961          */
6962         getTime : function(){
6963             if(this.browserEvent){
6964                 return E.getTime(this.browserEvent);
6965             }
6966             return null;
6967         },
6968
6969         /**
6970          * Gets the page coordinates of the event.
6971          * @return {Array} The xy values like [x, y]
6972          */
6973         getXY : function(){
6974             return this.xy;
6975         },
6976
6977         /**
6978          * Gets the target for the event.
6979          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6980          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6981                 search as a number or element (defaults to 10 || document.body)
6982          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6983          * @return {HTMLelement}
6984          */
6985         getTarget : function(selector, maxDepth, returnEl){
6986             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6987         },
6988         /**
6989          * Gets the related target.
6990          * @return {HTMLElement}
6991          */
6992         getRelatedTarget : function(){
6993             if(this.browserEvent){
6994                 return E.getRelatedTarget(this.browserEvent);
6995             }
6996             return null;
6997         },
6998
6999         /**
7000          * Normalizes mouse wheel delta across browsers
7001          * @return {Number} The delta
7002          */
7003         getWheelDelta : function(){
7004             var e = this.browserEvent;
7005             var delta = 0;
7006             if(e.wheelDelta){ /* IE/Opera. */
7007                 delta = e.wheelDelta/120;
7008             }else if(e.detail){ /* Mozilla case. */
7009                 delta = -e.detail/3;
7010             }
7011             return delta;
7012         },
7013
7014         /**
7015          * Returns true if the control, meta, shift or alt key was pressed during this event.
7016          * @return {Boolean}
7017          */
7018         hasModifier : function(){
7019             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
7020         },
7021
7022         /**
7023          * Returns true if the target of this event equals el or is a child of el
7024          * @param {String/HTMLElement/Element} el
7025          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
7026          * @return {Boolean}
7027          */
7028         within : function(el, related){
7029             var t = this[related ? "getRelatedTarget" : "getTarget"]();
7030             return t && Roo.fly(el).contains(t);
7031         },
7032
7033         getPoint : function(){
7034             return new Roo.lib.Point(this.xy[0], this.xy[1]);
7035         }
7036     };
7037
7038     return new Roo.EventObjectImpl();
7039 }();
7040             
7041     /*
7042  * Based on:
7043  * Ext JS Library 1.1.1
7044  * Copyright(c) 2006-2007, Ext JS, LLC.
7045  *
7046  * Originally Released Under LGPL - original licence link has changed is not relivant.
7047  *
7048  * Fork - LGPL
7049  * <script type="text/javascript">
7050  */
7051
7052  
7053 // was in Composite Element!??!?!
7054  
7055 (function(){
7056     var D = Roo.lib.Dom;
7057     var E = Roo.lib.Event;
7058     var A = Roo.lib.Anim;
7059
7060     // local style camelizing for speed
7061     var propCache = {};
7062     var camelRe = /(-[a-z])/gi;
7063     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
7064     var view = document.defaultView;
7065
7066 /**
7067  * @class Roo.Element
7068  * Represents an Element in the DOM.<br><br>
7069  * Usage:<br>
7070 <pre><code>
7071 var el = Roo.get("my-div");
7072
7073 // or with getEl
7074 var el = getEl("my-div");
7075
7076 // or with a DOM element
7077 var el = Roo.get(myDivElement);
7078 </code></pre>
7079  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
7080  * each call instead of constructing a new one.<br><br>
7081  * <b>Animations</b><br />
7082  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
7083  * should either be a boolean (true) or an object literal with animation options. The animation options are:
7084 <pre>
7085 Option    Default   Description
7086 --------- --------  ---------------------------------------------
7087 duration  .35       The duration of the animation in seconds
7088 easing    easeOut   The YUI easing method
7089 callback  none      A function to execute when the anim completes
7090 scope     this      The scope (this) of the callback function
7091 </pre>
7092 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
7093 * manipulate the animation. Here's an example:
7094 <pre><code>
7095 var el = Roo.get("my-div");
7096
7097 // no animation
7098 el.setWidth(100);
7099
7100 // default animation
7101 el.setWidth(100, true);
7102
7103 // animation with some options set
7104 el.setWidth(100, {
7105     duration: 1,
7106     callback: this.foo,
7107     scope: this
7108 });
7109
7110 // using the "anim" property to get the Anim object
7111 var opt = {
7112     duration: 1,
7113     callback: this.foo,
7114     scope: this
7115 };
7116 el.setWidth(100, opt);
7117 ...
7118 if(opt.anim.isAnimated()){
7119     opt.anim.stop();
7120 }
7121 </code></pre>
7122 * <b> Composite (Collections of) Elements</b><br />
7123  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7124  * @constructor Create a new Element directly.
7125  * @param {String/HTMLElement} element
7126  * @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).
7127  */
7128     Roo.Element = function(element, forceNew){
7129         var dom = typeof element == "string" ?
7130                 document.getElementById(element) : element;
7131         if(!dom){ // invalid id/element
7132             return null;
7133         }
7134         var id = dom.id;
7135         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7136             return Roo.Element.cache[id];
7137         }
7138
7139         /**
7140          * The DOM element
7141          * @type HTMLElement
7142          */
7143         this.dom = dom;
7144
7145         /**
7146          * The DOM element ID
7147          * @type String
7148          */
7149         this.id = id || Roo.id(dom);
7150     };
7151
7152     var El = Roo.Element;
7153
7154     El.prototype = {
7155         /**
7156          * The element's default display mode  (defaults to "") 
7157          * @type String
7158          */
7159         originalDisplay : "",
7160
7161         
7162         // note this is overridden in BS version..
7163         visibilityMode : 1, 
7164         /**
7165          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7166          * @type String
7167          */
7168         defaultUnit : "px",
7169         
7170         /**
7171          * Sets the element's visibility mode. When setVisible() is called it
7172          * will use this to determine whether to set the visibility or the display property.
7173          * @param visMode Element.VISIBILITY or Element.DISPLAY
7174          * @return {Roo.Element} this
7175          */
7176         setVisibilityMode : function(visMode){
7177             this.visibilityMode = visMode;
7178             return this;
7179         },
7180         /**
7181          * Convenience method for setVisibilityMode(Element.DISPLAY)
7182          * @param {String} display (optional) What to set display to when visible
7183          * @return {Roo.Element} this
7184          */
7185         enableDisplayMode : function(display){
7186             this.setVisibilityMode(El.DISPLAY);
7187             if(typeof display != "undefined") { this.originalDisplay = display; }
7188             return this;
7189         },
7190
7191         /**
7192          * 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)
7193          * @param {String} selector The simple selector to test
7194          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7195                 search as a number or element (defaults to 10 || document.body)
7196          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7197          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7198          */
7199         findParent : function(simpleSelector, maxDepth, returnEl){
7200             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7201             maxDepth = maxDepth || 50;
7202             if(typeof maxDepth != "number"){
7203                 stopEl = Roo.getDom(maxDepth);
7204                 maxDepth = 10;
7205             }
7206             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7207                 if(dq.is(p, simpleSelector)){
7208                     return returnEl ? Roo.get(p) : p;
7209                 }
7210                 depth++;
7211                 p = p.parentNode;
7212             }
7213             return null;
7214         },
7215
7216
7217         /**
7218          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7219          * @param {String} selector The simple selector to test
7220          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7221                 search as a number or element (defaults to 10 || document.body)
7222          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7223          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7224          */
7225         findParentNode : function(simpleSelector, maxDepth, returnEl){
7226             var p = Roo.fly(this.dom.parentNode, '_internal');
7227             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7228         },
7229         
7230         /**
7231          * Looks at  the scrollable parent element
7232          */
7233         findScrollableParent : function()
7234         {
7235             var overflowRegex = /(auto|scroll)/;
7236             
7237             if(this.getStyle('position') === 'fixed'){
7238                 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7239             }
7240             
7241             var excludeStaticParent = this.getStyle('position') === "absolute";
7242             
7243             for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
7244                 
7245                 if (excludeStaticParent && parent.getStyle('position') === "static") {
7246                     continue;
7247                 }
7248                 
7249                 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
7250                     return parent;
7251                 }
7252                 
7253                 if(parent.dom.nodeName.toLowerCase() == 'body'){
7254                     return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7255                 }
7256             }
7257             
7258             return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7259         },
7260
7261         /**
7262          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7263          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7264          * @param {String} selector The simple selector to test
7265          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7266                 search as a number or element (defaults to 10 || document.body)
7267          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7268          */
7269         up : function(simpleSelector, maxDepth){
7270             return this.findParentNode(simpleSelector, maxDepth, true);
7271         },
7272
7273
7274
7275         /**
7276          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7277          * @param {String} selector The simple selector to test
7278          * @return {Boolean} True if this element matches the selector, else false
7279          */
7280         is : function(simpleSelector){
7281             return Roo.DomQuery.is(this.dom, simpleSelector);
7282         },
7283
7284         /**
7285          * Perform animation on this element.
7286          * @param {Object} args The YUI animation control args
7287          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7288          * @param {Function} onComplete (optional) Function to call when animation completes
7289          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7290          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7291          * @return {Roo.Element} this
7292          */
7293         animate : function(args, duration, onComplete, easing, animType){
7294             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7295             return this;
7296         },
7297
7298         /*
7299          * @private Internal animation call
7300          */
7301         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7302             animType = animType || 'run';
7303             opt = opt || {};
7304             var anim = Roo.lib.Anim[animType](
7305                 this.dom, args,
7306                 (opt.duration || defaultDur) || .35,
7307                 (opt.easing || defaultEase) || 'easeOut',
7308                 function(){
7309                     Roo.callback(cb, this);
7310                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7311                 },
7312                 this
7313             );
7314             opt.anim = anim;
7315             return anim;
7316         },
7317
7318         // private legacy anim prep
7319         preanim : function(a, i){
7320             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7321         },
7322
7323         /**
7324          * Removes worthless text nodes
7325          * @param {Boolean} forceReclean (optional) By default the element
7326          * keeps track if it has been cleaned already so
7327          * you can call this over and over. However, if you update the element and
7328          * need to force a reclean, you can pass true.
7329          */
7330         clean : function(forceReclean){
7331             if(this.isCleaned && forceReclean !== true){
7332                 return this;
7333             }
7334             var ns = /\S/;
7335             var d = this.dom, n = d.firstChild, ni = -1;
7336             while(n){
7337                 var nx = n.nextSibling;
7338                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7339                     d.removeChild(n);
7340                 }else{
7341                     n.nodeIndex = ++ni;
7342                 }
7343                 n = nx;
7344             }
7345             this.isCleaned = true;
7346             return this;
7347         },
7348
7349         // private
7350         calcOffsetsTo : function(el){
7351             el = Roo.get(el);
7352             var d = el.dom;
7353             var restorePos = false;
7354             if(el.getStyle('position') == 'static'){
7355                 el.position('relative');
7356                 restorePos = true;
7357             }
7358             var x = 0, y =0;
7359             var op = this.dom;
7360             while(op && op != d && op.tagName != 'HTML'){
7361                 x+= op.offsetLeft;
7362                 y+= op.offsetTop;
7363                 op = op.offsetParent;
7364             }
7365             if(restorePos){
7366                 el.position('static');
7367             }
7368             return [x, y];
7369         },
7370
7371         /**
7372          * Scrolls this element into view within the passed container.
7373          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7374          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7375          * @return {Roo.Element} this
7376          */
7377         scrollIntoView : function(container, hscroll){
7378             var c = Roo.getDom(container) || document.body;
7379             var el = this.dom;
7380
7381             var o = this.calcOffsetsTo(c),
7382                 l = o[0],
7383                 t = o[1],
7384                 b = t+el.offsetHeight,
7385                 r = l+el.offsetWidth;
7386
7387             var ch = c.clientHeight;
7388             var ct = parseInt(c.scrollTop, 10);
7389             var cl = parseInt(c.scrollLeft, 10);
7390             var cb = ct + ch;
7391             var cr = cl + c.clientWidth;
7392
7393             if(t < ct){
7394                 c.scrollTop = t;
7395             }else if(b > cb){
7396                 c.scrollTop = b-ch;
7397             }
7398
7399             if(hscroll !== false){
7400                 if(l < cl){
7401                     c.scrollLeft = l;
7402                 }else if(r > cr){
7403                     c.scrollLeft = r-c.clientWidth;
7404                 }
7405             }
7406             return this;
7407         },
7408
7409         // private
7410         scrollChildIntoView : function(child, hscroll){
7411             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7412         },
7413
7414         /**
7415          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7416          * the new height may not be available immediately.
7417          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7418          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7419          * @param {Function} onComplete (optional) Function to call when animation completes
7420          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7421          * @return {Roo.Element} this
7422          */
7423         autoHeight : function(animate, duration, onComplete, easing){
7424             var oldHeight = this.getHeight();
7425             this.clip();
7426             this.setHeight(1); // force clipping
7427             setTimeout(function(){
7428                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7429                 if(!animate){
7430                     this.setHeight(height);
7431                     this.unclip();
7432                     if(typeof onComplete == "function"){
7433                         onComplete();
7434                     }
7435                 }else{
7436                     this.setHeight(oldHeight); // restore original height
7437                     this.setHeight(height, animate, duration, function(){
7438                         this.unclip();
7439                         if(typeof onComplete == "function") { onComplete(); }
7440                     }.createDelegate(this), easing);
7441                 }
7442             }.createDelegate(this), 0);
7443             return this;
7444         },
7445
7446         /**
7447          * Returns true if this element is an ancestor of the passed element
7448          * @param {HTMLElement/String} el The element to check
7449          * @return {Boolean} True if this element is an ancestor of el, else false
7450          */
7451         contains : function(el){
7452             if(!el){return false;}
7453             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7454         },
7455
7456         /**
7457          * Checks whether the element is currently visible using both visibility and display properties.
7458          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7459          * @return {Boolean} True if the element is currently visible, else false
7460          */
7461         isVisible : function(deep) {
7462             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7463             if(deep !== true || !vis){
7464                 return vis;
7465             }
7466             var p = this.dom.parentNode;
7467             while(p && p.tagName.toLowerCase() != "body"){
7468                 if(!Roo.fly(p, '_isVisible').isVisible()){
7469                     return false;
7470                 }
7471                 p = p.parentNode;
7472             }
7473             return true;
7474         },
7475
7476         /**
7477          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7478          * @param {String} selector The CSS selector
7479          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7480          * @return {CompositeElement/CompositeElementLite} The composite element
7481          */
7482         select : function(selector, unique){
7483             return El.select(selector, unique, this.dom);
7484         },
7485
7486         /**
7487          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7488          * @param {String} selector The CSS selector
7489          * @return {Array} An array of the matched nodes
7490          */
7491         query : function(selector, unique){
7492             return Roo.DomQuery.select(selector, this.dom);
7493         },
7494
7495         /**
7496          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7497          * @param {String} selector The CSS selector
7498          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7499          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7500          */
7501         child : function(selector, returnDom){
7502             var n = Roo.DomQuery.selectNode(selector, this.dom);
7503             return returnDom ? n : Roo.get(n);
7504         },
7505
7506         /**
7507          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7508          * @param {String} selector The CSS selector
7509          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7510          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7511          */
7512         down : function(selector, returnDom){
7513             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7514             return returnDom ? n : Roo.get(n);
7515         },
7516
7517         /**
7518          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7519          * @param {String} group The group the DD object is member of
7520          * @param {Object} config The DD config object
7521          * @param {Object} overrides An object containing methods to override/implement on the DD object
7522          * @return {Roo.dd.DD} The DD object
7523          */
7524         initDD : function(group, config, overrides){
7525             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7526             return Roo.apply(dd, overrides);
7527         },
7528
7529         /**
7530          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7531          * @param {String} group The group the DDProxy object is member of
7532          * @param {Object} config The DDProxy config object
7533          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7534          * @return {Roo.dd.DDProxy} The DDProxy object
7535          */
7536         initDDProxy : function(group, config, overrides){
7537             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7538             return Roo.apply(dd, overrides);
7539         },
7540
7541         /**
7542          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7543          * @param {String} group The group the DDTarget object is member of
7544          * @param {Object} config The DDTarget config object
7545          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7546          * @return {Roo.dd.DDTarget} The DDTarget object
7547          */
7548         initDDTarget : function(group, config, overrides){
7549             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7550             return Roo.apply(dd, overrides);
7551         },
7552
7553         /**
7554          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7555          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7556          * @param {Boolean} visible Whether the element is visible
7557          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7558          * @return {Roo.Element} this
7559          */
7560          setVisible : function(visible, animate){
7561             if(!animate || !A){
7562                 if(this.visibilityMode == El.DISPLAY){
7563                     this.setDisplayed(visible);
7564                 }else{
7565                     this.fixDisplay();
7566                     this.dom.style.visibility = visible ? "visible" : "hidden";
7567                 }
7568             }else{
7569                 // closure for composites
7570                 var dom = this.dom;
7571                 var visMode = this.visibilityMode;
7572                 if(visible){
7573                     this.setOpacity(.01);
7574                     this.setVisible(true);
7575                 }
7576                 this.anim({opacity: { to: (visible?1:0) }},
7577                       this.preanim(arguments, 1),
7578                       null, .35, 'easeIn', function(){
7579                          if(!visible){
7580                              if(visMode == El.DISPLAY){
7581                                  dom.style.display = "none";
7582                              }else{
7583                                  dom.style.visibility = "hidden";
7584                              }
7585                              Roo.get(dom).setOpacity(1);
7586                          }
7587                      });
7588             }
7589             return this;
7590         },
7591
7592         /**
7593          * Returns true if display is not "none"
7594          * @return {Boolean}
7595          */
7596         isDisplayed : function() {
7597             return this.getStyle("display") != "none";
7598         },
7599
7600         /**
7601          * Toggles the element's visibility or display, depending on visibility mode.
7602          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7603          * @return {Roo.Element} this
7604          */
7605         toggle : function(animate){
7606             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7607             return this;
7608         },
7609
7610         /**
7611          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7612          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7613          * @return {Roo.Element} this
7614          */
7615         setDisplayed : function(value) {
7616             if(typeof value == "boolean"){
7617                value = value ? this.originalDisplay : "none";
7618             }
7619             this.setStyle("display", value);
7620             return this;
7621         },
7622
7623         /**
7624          * Tries to focus the element. Any exceptions are caught and ignored.
7625          * @return {Roo.Element} this
7626          */
7627         focus : function() {
7628             try{
7629                 this.dom.focus();
7630             }catch(e){}
7631             return this;
7632         },
7633
7634         /**
7635          * Tries to blur the element. Any exceptions are caught and ignored.
7636          * @return {Roo.Element} this
7637          */
7638         blur : function() {
7639             try{
7640                 this.dom.blur();
7641             }catch(e){}
7642             return this;
7643         },
7644
7645         /**
7646          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7647          * @param {String/Array} className The CSS class to add, or an array of classes
7648          * @return {Roo.Element} this
7649          */
7650         addClass : function(className){
7651             if(className instanceof Array){
7652                 for(var i = 0, len = className.length; i < len; i++) {
7653                     this.addClass(className[i]);
7654                 }
7655             }else{
7656                 if(className && !this.hasClass(className)){
7657                     this.dom.className = this.dom.className + " " + className;
7658                 }
7659             }
7660             return this;
7661         },
7662
7663         /**
7664          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7665          * @param {String/Array} className The CSS class to add, or an array of classes
7666          * @return {Roo.Element} this
7667          */
7668         radioClass : function(className){
7669             var siblings = this.dom.parentNode.childNodes;
7670             for(var i = 0; i < siblings.length; i++) {
7671                 var s = siblings[i];
7672                 if(s.nodeType == 1){
7673                     Roo.get(s).removeClass(className);
7674                 }
7675             }
7676             this.addClass(className);
7677             return this;
7678         },
7679
7680         /**
7681          * Removes one or more CSS classes from the element.
7682          * @param {String/Array} className The CSS class to remove, or an array of classes
7683          * @return {Roo.Element} this
7684          */
7685         removeClass : function(className){
7686             if(!className || !this.dom.className){
7687                 return this;
7688             }
7689             if(className instanceof Array){
7690                 for(var i = 0, len = className.length; i < len; i++) {
7691                     this.removeClass(className[i]);
7692                 }
7693             }else{
7694                 if(this.hasClass(className)){
7695                     var re = this.classReCache[className];
7696                     if (!re) {
7697                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7698                        this.classReCache[className] = re;
7699                     }
7700                     this.dom.className =
7701                         this.dom.className.replace(re, " ");
7702                 }
7703             }
7704             return this;
7705         },
7706
7707         // private
7708         classReCache: {},
7709
7710         /**
7711          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7712          * @param {String} className The CSS class to toggle
7713          * @return {Roo.Element} this
7714          */
7715         toggleClass : function(className){
7716             if(this.hasClass(className)){
7717                 this.removeClass(className);
7718             }else{
7719                 this.addClass(className);
7720             }
7721             return this;
7722         },
7723
7724         /**
7725          * Checks if the specified CSS class exists on this element's DOM node.
7726          * @param {String} className The CSS class to check for
7727          * @return {Boolean} True if the class exists, else false
7728          */
7729         hasClass : function(className){
7730             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7731         },
7732
7733         /**
7734          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7735          * @param {String} oldClassName The CSS class to replace
7736          * @param {String} newClassName The replacement CSS class
7737          * @return {Roo.Element} this
7738          */
7739         replaceClass : function(oldClassName, newClassName){
7740             this.removeClass(oldClassName);
7741             this.addClass(newClassName);
7742             return this;
7743         },
7744
7745         /**
7746          * Returns an object with properties matching the styles requested.
7747          * For example, el.getStyles('color', 'font-size', 'width') might return
7748          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7749          * @param {String} style1 A style name
7750          * @param {String} style2 A style name
7751          * @param {String} etc.
7752          * @return {Object} The style object
7753          */
7754         getStyles : function(){
7755             var a = arguments, len = a.length, r = {};
7756             for(var i = 0; i < len; i++){
7757                 r[a[i]] = this.getStyle(a[i]);
7758             }
7759             return r;
7760         },
7761
7762         /**
7763          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7764          * @param {String} property The style property whose value is returned.
7765          * @return {String} The current value of the style property for this element.
7766          */
7767         getStyle : function(){
7768             return view && view.getComputedStyle ?
7769                 function(prop){
7770                     var el = this.dom, v, cs, camel;
7771                     if(prop == 'float'){
7772                         prop = "cssFloat";
7773                     }
7774                     if(el.style && (v = el.style[prop])){
7775                         return v;
7776                     }
7777                     if(cs = view.getComputedStyle(el, "")){
7778                         if(!(camel = propCache[prop])){
7779                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7780                         }
7781                         return cs[camel];
7782                     }
7783                     return null;
7784                 } :
7785                 function(prop){
7786                     var el = this.dom, v, cs, camel;
7787                     if(prop == 'opacity'){
7788                         if(typeof el.style.filter == 'string'){
7789                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7790                             if(m){
7791                                 var fv = parseFloat(m[1]);
7792                                 if(!isNaN(fv)){
7793                                     return fv ? fv / 100 : 0;
7794                                 }
7795                             }
7796                         }
7797                         return 1;
7798                     }else if(prop == 'float'){
7799                         prop = "styleFloat";
7800                     }
7801                     if(!(camel = propCache[prop])){
7802                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7803                     }
7804                     if(v = el.style[camel]){
7805                         return v;
7806                     }
7807                     if(cs = el.currentStyle){
7808                         return cs[camel];
7809                     }
7810                     return null;
7811                 };
7812         }(),
7813
7814         /**
7815          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7816          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7817          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7818          * @return {Roo.Element} this
7819          */
7820         setStyle : function(prop, value){
7821             if(typeof prop == "string"){
7822                 
7823                 if (prop == 'float') {
7824                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7825                     return this;
7826                 }
7827                 
7828                 var camel;
7829                 if(!(camel = propCache[prop])){
7830                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7831                 }
7832                 
7833                 if(camel == 'opacity') {
7834                     this.setOpacity(value);
7835                 }else{
7836                     this.dom.style[camel] = value;
7837                 }
7838             }else{
7839                 for(var style in prop){
7840                     if(typeof prop[style] != "function"){
7841                        this.setStyle(style, prop[style]);
7842                     }
7843                 }
7844             }
7845             return this;
7846         },
7847
7848         /**
7849          * More flexible version of {@link #setStyle} for setting style properties.
7850          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7851          * a function which returns such a specification.
7852          * @return {Roo.Element} this
7853          */
7854         applyStyles : function(style){
7855             Roo.DomHelper.applyStyles(this.dom, style);
7856             return this;
7857         },
7858
7859         /**
7860           * 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).
7861           * @return {Number} The X position of the element
7862           */
7863         getX : function(){
7864             return D.getX(this.dom);
7865         },
7866
7867         /**
7868           * 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).
7869           * @return {Number} The Y position of the element
7870           */
7871         getY : function(){
7872             return D.getY(this.dom);
7873         },
7874
7875         /**
7876           * 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).
7877           * @return {Array} The XY position of the element
7878           */
7879         getXY : function(){
7880             return D.getXY(this.dom);
7881         },
7882
7883         /**
7884          * 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).
7885          * @param {Number} The X position of the element
7886          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7887          * @return {Roo.Element} this
7888          */
7889         setX : function(x, animate){
7890             if(!animate || !A){
7891                 D.setX(this.dom, x);
7892             }else{
7893                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7894             }
7895             return this;
7896         },
7897
7898         /**
7899          * 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).
7900          * @param {Number} The Y position of the element
7901          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7902          * @return {Roo.Element} this
7903          */
7904         setY : function(y, animate){
7905             if(!animate || !A){
7906                 D.setY(this.dom, y);
7907             }else{
7908                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7909             }
7910             return this;
7911         },
7912
7913         /**
7914          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7915          * @param {String} left The left CSS property value
7916          * @return {Roo.Element} this
7917          */
7918         setLeft : function(left){
7919             this.setStyle("left", this.addUnits(left));
7920             return this;
7921         },
7922
7923         /**
7924          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7925          * @param {String} top The top CSS property value
7926          * @return {Roo.Element} this
7927          */
7928         setTop : function(top){
7929             this.setStyle("top", this.addUnits(top));
7930             return this;
7931         },
7932
7933         /**
7934          * Sets the element's CSS right style.
7935          * @param {String} right The right CSS property value
7936          * @return {Roo.Element} this
7937          */
7938         setRight : function(right){
7939             this.setStyle("right", this.addUnits(right));
7940             return this;
7941         },
7942
7943         /**
7944          * Sets the element's CSS bottom style.
7945          * @param {String} bottom The bottom CSS property value
7946          * @return {Roo.Element} this
7947          */
7948         setBottom : function(bottom){
7949             this.setStyle("bottom", this.addUnits(bottom));
7950             return this;
7951         },
7952
7953         /**
7954          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7955          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7956          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7957          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7958          * @return {Roo.Element} this
7959          */
7960         setXY : function(pos, animate){
7961             if(!animate || !A){
7962                 D.setXY(this.dom, pos);
7963             }else{
7964                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7965             }
7966             return this;
7967         },
7968
7969         /**
7970          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7971          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7972          * @param {Number} x X value for new position (coordinates are page-based)
7973          * @param {Number} y Y value for new position (coordinates are page-based)
7974          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7975          * @return {Roo.Element} this
7976          */
7977         setLocation : function(x, y, animate){
7978             this.setXY([x, y], this.preanim(arguments, 2));
7979             return this;
7980         },
7981
7982         /**
7983          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7984          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7985          * @param {Number} x X value for new position (coordinates are page-based)
7986          * @param {Number} y Y value for new position (coordinates are page-based)
7987          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7988          * @return {Roo.Element} this
7989          */
7990         moveTo : function(x, y, animate){
7991             this.setXY([x, y], this.preanim(arguments, 2));
7992             return this;
7993         },
7994
7995         /**
7996          * Returns the region of the given element.
7997          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7998          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7999          */
8000         getRegion : function(){
8001             return D.getRegion(this.dom);
8002         },
8003
8004         /**
8005          * Returns the offset height of the element
8006          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
8007          * @return {Number} The element's height
8008          */
8009         getHeight : function(contentHeight){
8010             var h = this.dom.offsetHeight || 0;
8011             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
8012         },
8013
8014         /**
8015          * Returns the offset width of the element
8016          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
8017          * @return {Number} The element's width
8018          */
8019         getWidth : function(contentWidth){
8020             var w = this.dom.offsetWidth || 0;
8021             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
8022         },
8023
8024         /**
8025          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
8026          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
8027          * if a height has not been set using CSS.
8028          * @return {Number}
8029          */
8030         getComputedHeight : function(){
8031             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
8032             if(!h){
8033                 h = parseInt(this.getStyle('height'), 10) || 0;
8034                 if(!this.isBorderBox()){
8035                     h += this.getFrameWidth('tb');
8036                 }
8037             }
8038             return h;
8039         },
8040
8041         /**
8042          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
8043          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
8044          * if a width has not been set using CSS.
8045          * @return {Number}
8046          */
8047         getComputedWidth : function(){
8048             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
8049             if(!w){
8050                 w = parseInt(this.getStyle('width'), 10) || 0;
8051                 if(!this.isBorderBox()){
8052                     w += this.getFrameWidth('lr');
8053                 }
8054             }
8055             return w;
8056         },
8057
8058         /**
8059          * Returns the size of the element.
8060          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
8061          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8062          */
8063         getSize : function(contentSize){
8064             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
8065         },
8066
8067         /**
8068          * Returns the width and height of the viewport.
8069          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
8070          */
8071         getViewSize : function(){
8072             var d = this.dom, doc = document, aw = 0, ah = 0;
8073             if(d == doc || d == doc.body){
8074                 return {width : D.getViewWidth(), height: D.getViewHeight()};
8075             }else{
8076                 return {
8077                     width : d.clientWidth,
8078                     height: d.clientHeight
8079                 };
8080             }
8081         },
8082
8083         /**
8084          * Returns the value of the "value" attribute
8085          * @param {Boolean} asNumber true to parse the value as a number
8086          * @return {String/Number}
8087          */
8088         getValue : function(asNumber){
8089             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
8090         },
8091
8092         // private
8093         adjustWidth : function(width){
8094             if(typeof width == "number"){
8095                 if(this.autoBoxAdjust && !this.isBorderBox()){
8096                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8097                 }
8098                 if(width < 0){
8099                     width = 0;
8100                 }
8101             }
8102             return width;
8103         },
8104
8105         // private
8106         adjustHeight : function(height){
8107             if(typeof height == "number"){
8108                if(this.autoBoxAdjust && !this.isBorderBox()){
8109                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8110                }
8111                if(height < 0){
8112                    height = 0;
8113                }
8114             }
8115             return height;
8116         },
8117
8118         /**
8119          * Set the width of the element
8120          * @param {Number} width The new width
8121          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8122          * @return {Roo.Element} this
8123          */
8124         setWidth : function(width, animate){
8125             width = this.adjustWidth(width);
8126             if(!animate || !A){
8127                 this.dom.style.width = this.addUnits(width);
8128             }else{
8129                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
8130             }
8131             return this;
8132         },
8133
8134         /**
8135          * Set the height of the element
8136          * @param {Number} height The new height
8137          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8138          * @return {Roo.Element} this
8139          */
8140          setHeight : function(height, animate){
8141             height = this.adjustHeight(height);
8142             if(!animate || !A){
8143                 this.dom.style.height = this.addUnits(height);
8144             }else{
8145                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
8146             }
8147             return this;
8148         },
8149
8150         /**
8151          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
8152          * @param {Number} width The new width
8153          * @param {Number} height The new height
8154          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8155          * @return {Roo.Element} this
8156          */
8157          setSize : function(width, height, animate){
8158             if(typeof width == "object"){ // in case of object from getSize()
8159                 height = width.height; width = width.width;
8160             }
8161             width = this.adjustWidth(width); height = this.adjustHeight(height);
8162             if(!animate || !A){
8163                 this.dom.style.width = this.addUnits(width);
8164                 this.dom.style.height = this.addUnits(height);
8165             }else{
8166                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8167             }
8168             return this;
8169         },
8170
8171         /**
8172          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8173          * @param {Number} x X value for new position (coordinates are page-based)
8174          * @param {Number} y Y value for new position (coordinates are page-based)
8175          * @param {Number} width The new width
8176          * @param {Number} height The new height
8177          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8178          * @return {Roo.Element} this
8179          */
8180         setBounds : function(x, y, width, height, animate){
8181             if(!animate || !A){
8182                 this.setSize(width, height);
8183                 this.setLocation(x, y);
8184             }else{
8185                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8186                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8187                               this.preanim(arguments, 4), 'motion');
8188             }
8189             return this;
8190         },
8191
8192         /**
8193          * 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.
8194          * @param {Roo.lib.Region} region The region to fill
8195          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8196          * @return {Roo.Element} this
8197          */
8198         setRegion : function(region, animate){
8199             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8200             return this;
8201         },
8202
8203         /**
8204          * Appends an event handler
8205          *
8206          * @param {String}   eventName     The type of event to append
8207          * @param {Function} fn        The method the event invokes
8208          * @param {Object} scope       (optional) The scope (this object) of the fn
8209          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8210          */
8211         addListener : function(eventName, fn, scope, options){
8212             if (this.dom) {
8213                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8214             }
8215         },
8216
8217         /**
8218          * Removes an event handler from this element
8219          * @param {String} eventName the type of event to remove
8220          * @param {Function} fn the method the event invokes
8221          * @return {Roo.Element} this
8222          */
8223         removeListener : function(eventName, fn){
8224             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8225             return this;
8226         },
8227
8228         /**
8229          * Removes all previous added listeners from this element
8230          * @return {Roo.Element} this
8231          */
8232         removeAllListeners : function(){
8233             E.purgeElement(this.dom);
8234             return this;
8235         },
8236
8237         relayEvent : function(eventName, observable){
8238             this.on(eventName, function(e){
8239                 observable.fireEvent(eventName, e);
8240             });
8241         },
8242
8243         /**
8244          * Set the opacity of the element
8245          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8246          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8247          * @return {Roo.Element} this
8248          */
8249          setOpacity : function(opacity, animate){
8250             if(!animate || !A){
8251                 var s = this.dom.style;
8252                 if(Roo.isIE){
8253                     s.zoom = 1;
8254                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8255                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8256                 }else{
8257                     s.opacity = opacity;
8258                 }
8259             }else{
8260                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8261             }
8262             return this;
8263         },
8264
8265         /**
8266          * Gets the left X coordinate
8267          * @param {Boolean} local True to get the local css position instead of page coordinate
8268          * @return {Number}
8269          */
8270         getLeft : function(local){
8271             if(!local){
8272                 return this.getX();
8273             }else{
8274                 return parseInt(this.getStyle("left"), 10) || 0;
8275             }
8276         },
8277
8278         /**
8279          * Gets the right X coordinate of the element (element X position + element width)
8280          * @param {Boolean} local True to get the local css position instead of page coordinate
8281          * @return {Number}
8282          */
8283         getRight : function(local){
8284             if(!local){
8285                 return this.getX() + this.getWidth();
8286             }else{
8287                 return (this.getLeft(true) + this.getWidth()) || 0;
8288             }
8289         },
8290
8291         /**
8292          * Gets the top Y coordinate
8293          * @param {Boolean} local True to get the local css position instead of page coordinate
8294          * @return {Number}
8295          */
8296         getTop : function(local) {
8297             if(!local){
8298                 return this.getY();
8299             }else{
8300                 return parseInt(this.getStyle("top"), 10) || 0;
8301             }
8302         },
8303
8304         /**
8305          * Gets the bottom Y coordinate of the element (element Y position + element height)
8306          * @param {Boolean} local True to get the local css position instead of page coordinate
8307          * @return {Number}
8308          */
8309         getBottom : function(local){
8310             if(!local){
8311                 return this.getY() + this.getHeight();
8312             }else{
8313                 return (this.getTop(true) + this.getHeight()) || 0;
8314             }
8315         },
8316
8317         /**
8318         * Initializes positioning on this element. If a desired position is not passed, it will make the
8319         * the element positioned relative IF it is not already positioned.
8320         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8321         * @param {Number} zIndex (optional) The zIndex to apply
8322         * @param {Number} x (optional) Set the page X position
8323         * @param {Number} y (optional) Set the page Y position
8324         */
8325         position : function(pos, zIndex, x, y){
8326             if(!pos){
8327                if(this.getStyle('position') == 'static'){
8328                    this.setStyle('position', 'relative');
8329                }
8330             }else{
8331                 this.setStyle("position", pos);
8332             }
8333             if(zIndex){
8334                 this.setStyle("z-index", zIndex);
8335             }
8336             if(x !== undefined && y !== undefined){
8337                 this.setXY([x, y]);
8338             }else if(x !== undefined){
8339                 this.setX(x);
8340             }else if(y !== undefined){
8341                 this.setY(y);
8342             }
8343         },
8344
8345         /**
8346         * Clear positioning back to the default when the document was loaded
8347         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8348         * @return {Roo.Element} this
8349          */
8350         clearPositioning : function(value){
8351             value = value ||'';
8352             this.setStyle({
8353                 "left": value,
8354                 "right": value,
8355                 "top": value,
8356                 "bottom": value,
8357                 "z-index": "",
8358                 "position" : "static"
8359             });
8360             return this;
8361         },
8362
8363         /**
8364         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8365         * snapshot before performing an update and then restoring the element.
8366         * @return {Object}
8367         */
8368         getPositioning : function(){
8369             var l = this.getStyle("left");
8370             var t = this.getStyle("top");
8371             return {
8372                 "position" : this.getStyle("position"),
8373                 "left" : l,
8374                 "right" : l ? "" : this.getStyle("right"),
8375                 "top" : t,
8376                 "bottom" : t ? "" : this.getStyle("bottom"),
8377                 "z-index" : this.getStyle("z-index")
8378             };
8379         },
8380
8381         /**
8382          * Gets the width of the border(s) for the specified side(s)
8383          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8384          * passing lr would get the border (l)eft width + the border (r)ight width.
8385          * @return {Number} The width of the sides passed added together
8386          */
8387         getBorderWidth : function(side){
8388             return this.addStyles(side, El.borders);
8389         },
8390
8391         /**
8392          * Gets the width of the padding(s) for the specified side(s)
8393          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8394          * passing lr would get the padding (l)eft + the padding (r)ight.
8395          * @return {Number} The padding of the sides passed added together
8396          */
8397         getPadding : function(side){
8398             return this.addStyles(side, El.paddings);
8399         },
8400
8401         /**
8402         * Set positioning with an object returned by getPositioning().
8403         * @param {Object} posCfg
8404         * @return {Roo.Element} this
8405          */
8406         setPositioning : function(pc){
8407             this.applyStyles(pc);
8408             if(pc.right == "auto"){
8409                 this.dom.style.right = "";
8410             }
8411             if(pc.bottom == "auto"){
8412                 this.dom.style.bottom = "";
8413             }
8414             return this;
8415         },
8416
8417         // private
8418         fixDisplay : function(){
8419             if(this.getStyle("display") == "none"){
8420                 this.setStyle("visibility", "hidden");
8421                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8422                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8423                     this.setStyle("display", "block");
8424                 }
8425             }
8426         },
8427
8428         /**
8429          * Quick set left and top adding default units
8430          * @param {String} left The left CSS property value
8431          * @param {String} top The top CSS property value
8432          * @return {Roo.Element} this
8433          */
8434          setLeftTop : function(left, top){
8435             this.dom.style.left = this.addUnits(left);
8436             this.dom.style.top = this.addUnits(top);
8437             return this;
8438         },
8439
8440         /**
8441          * Move this element relative to its current position.
8442          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8443          * @param {Number} distance How far to move the element in pixels
8444          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8445          * @return {Roo.Element} this
8446          */
8447          move : function(direction, distance, animate){
8448             var xy = this.getXY();
8449             direction = direction.toLowerCase();
8450             switch(direction){
8451                 case "l":
8452                 case "left":
8453                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8454                     break;
8455                case "r":
8456                case "right":
8457                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8458                     break;
8459                case "t":
8460                case "top":
8461                case "up":
8462                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8463                     break;
8464                case "b":
8465                case "bottom":
8466                case "down":
8467                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8468                     break;
8469             }
8470             return this;
8471         },
8472
8473         /**
8474          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8475          * @return {Roo.Element} this
8476          */
8477         clip : function(){
8478             if(!this.isClipped){
8479                this.isClipped = true;
8480                this.originalClip = {
8481                    "o": this.getStyle("overflow"),
8482                    "x": this.getStyle("overflow-x"),
8483                    "y": this.getStyle("overflow-y")
8484                };
8485                this.setStyle("overflow", "hidden");
8486                this.setStyle("overflow-x", "hidden");
8487                this.setStyle("overflow-y", "hidden");
8488             }
8489             return this;
8490         },
8491
8492         /**
8493          *  Return clipping (overflow) to original clipping before clip() was called
8494          * @return {Roo.Element} this
8495          */
8496         unclip : function(){
8497             if(this.isClipped){
8498                 this.isClipped = false;
8499                 var o = this.originalClip;
8500                 if(o.o){this.setStyle("overflow", o.o);}
8501                 if(o.x){this.setStyle("overflow-x", o.x);}
8502                 if(o.y){this.setStyle("overflow-y", o.y);}
8503             }
8504             return this;
8505         },
8506
8507
8508         /**
8509          * Gets the x,y coordinates specified by the anchor position on the element.
8510          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8511          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8512          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8513          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8514          * @return {Array} [x, y] An array containing the element's x and y coordinates
8515          */
8516         getAnchorXY : function(anchor, local, s){
8517             //Passing a different size is useful for pre-calculating anchors,
8518             //especially for anchored animations that change the el size.
8519
8520             var w, h, vp = false;
8521             if(!s){
8522                 var d = this.dom;
8523                 if(d == document.body || d == document){
8524                     vp = true;
8525                     w = D.getViewWidth(); h = D.getViewHeight();
8526                 }else{
8527                     w = this.getWidth(); h = this.getHeight();
8528                 }
8529             }else{
8530                 w = s.width;  h = s.height;
8531             }
8532             var x = 0, y = 0, r = Math.round;
8533             switch((anchor || "tl").toLowerCase()){
8534                 case "c":
8535                     x = r(w*.5);
8536                     y = r(h*.5);
8537                 break;
8538                 case "t":
8539                     x = r(w*.5);
8540                     y = 0;
8541                 break;
8542                 case "l":
8543                     x = 0;
8544                     y = r(h*.5);
8545                 break;
8546                 case "r":
8547                     x = w;
8548                     y = r(h*.5);
8549                 break;
8550                 case "b":
8551                     x = r(w*.5);
8552                     y = h;
8553                 break;
8554                 case "tl":
8555                     x = 0;
8556                     y = 0;
8557                 break;
8558                 case "bl":
8559                     x = 0;
8560                     y = h;
8561                 break;
8562                 case "br":
8563                     x = w;
8564                     y = h;
8565                 break;
8566                 case "tr":
8567                     x = w;
8568                     y = 0;
8569                 break;
8570             }
8571             if(local === true){
8572                 return [x, y];
8573             }
8574             if(vp){
8575                 var sc = this.getScroll();
8576                 return [x + sc.left, y + sc.top];
8577             }
8578             //Add the element's offset xy
8579             var o = this.getXY();
8580             return [x+o[0], y+o[1]];
8581         },
8582
8583         /**
8584          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8585          * supported position values.
8586          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8587          * @param {String} position The position to align to.
8588          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8589          * @return {Array} [x, y]
8590          */
8591         getAlignToXY : function(el, p, o)
8592         {
8593             el = Roo.get(el);
8594             var d = this.dom;
8595             if(!el.dom){
8596                 throw "Element.alignTo with an element that doesn't exist";
8597             }
8598             var c = false; //constrain to viewport
8599             var p1 = "", p2 = "";
8600             o = o || [0,0];
8601
8602             if(!p){
8603                 p = "tl-bl";
8604             }else if(p == "?"){
8605                 p = "tl-bl?";
8606             }else if(p.indexOf("-") == -1){
8607                 p = "tl-" + p;
8608             }
8609             p = p.toLowerCase();
8610             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8611             if(!m){
8612                throw "Element.alignTo with an invalid alignment " + p;
8613             }
8614             p1 = m[1]; p2 = m[2]; c = !!m[3];
8615
8616             //Subtract the aligned el's internal xy from the target's offset xy
8617             //plus custom offset to get the aligned el's new offset xy
8618             var a1 = this.getAnchorXY(p1, true);
8619             var a2 = el.getAnchorXY(p2, false);
8620             var x = a2[0] - a1[0] + o[0];
8621             var y = a2[1] - a1[1] + o[1];
8622             if(c){
8623                 //constrain the aligned el to viewport if necessary
8624                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8625                 // 5px of margin for ie
8626                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8627
8628                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8629                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8630                 //otherwise swap the aligned el to the opposite border of the target.
8631                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8632                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8633                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t")  );
8634                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8635
8636                var doc = document;
8637                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8638                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8639
8640                if((x+w) > dw + scrollX){
8641                     x = swapX ? r.left-w : dw+scrollX-w;
8642                 }
8643                if(x < scrollX){
8644                    x = swapX ? r.right : scrollX;
8645                }
8646                if((y+h) > dh + scrollY){
8647                     y = swapY ? r.top-h : dh+scrollY-h;
8648                 }
8649                if (y < scrollY){
8650                    y = swapY ? r.bottom : scrollY;
8651                }
8652             }
8653             return [x,y];
8654         },
8655
8656         // private
8657         getConstrainToXY : function(){
8658             var os = {top:0, left:0, bottom:0, right: 0};
8659
8660             return function(el, local, offsets, proposedXY){
8661                 el = Roo.get(el);
8662                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8663
8664                 var vw, vh, vx = 0, vy = 0;
8665                 if(el.dom == document.body || el.dom == document){
8666                     vw = Roo.lib.Dom.getViewWidth();
8667                     vh = Roo.lib.Dom.getViewHeight();
8668                 }else{
8669                     vw = el.dom.clientWidth;
8670                     vh = el.dom.clientHeight;
8671                     if(!local){
8672                         var vxy = el.getXY();
8673                         vx = vxy[0];
8674                         vy = vxy[1];
8675                     }
8676                 }
8677
8678                 var s = el.getScroll();
8679
8680                 vx += offsets.left + s.left;
8681                 vy += offsets.top + s.top;
8682
8683                 vw -= offsets.right;
8684                 vh -= offsets.bottom;
8685
8686                 var vr = vx+vw;
8687                 var vb = vy+vh;
8688
8689                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8690                 var x = xy[0], y = xy[1];
8691                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8692
8693                 // only move it if it needs it
8694                 var moved = false;
8695
8696                 // first validate right/bottom
8697                 if((x + w) > vr){
8698                     x = vr - w;
8699                     moved = true;
8700                 }
8701                 if((y + h) > vb){
8702                     y = vb - h;
8703                     moved = true;
8704                 }
8705                 // then make sure top/left isn't negative
8706                 if(x < vx){
8707                     x = vx;
8708                     moved = true;
8709                 }
8710                 if(y < vy){
8711                     y = vy;
8712                     moved = true;
8713                 }
8714                 return moved ? [x, y] : false;
8715             };
8716         }(),
8717
8718         // private
8719         adjustForConstraints : function(xy, parent, offsets){
8720             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8721         },
8722
8723         /**
8724          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8725          * document it aligns it to the viewport.
8726          * The position parameter is optional, and can be specified in any one of the following formats:
8727          * <ul>
8728          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8729          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8730          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8731          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8732          *   <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
8733          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8734          * </ul>
8735          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8736          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8737          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8738          * that specified in order to enforce the viewport constraints.
8739          * Following are all of the supported anchor positions:
8740     <pre>
8741     Value  Description
8742     -----  -----------------------------
8743     tl     The top left corner (default)
8744     t      The center of the top edge
8745     tr     The top right corner
8746     l      The center of the left edge
8747     c      In the center of the element
8748     r      The center of the right edge
8749     bl     The bottom left corner
8750     b      The center of the bottom edge
8751     br     The bottom right corner
8752     </pre>
8753     Example Usage:
8754     <pre><code>
8755     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8756     el.alignTo("other-el");
8757
8758     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8759     el.alignTo("other-el", "tr?");
8760
8761     // align the bottom right corner of el with the center left edge of other-el
8762     el.alignTo("other-el", "br-l?");
8763
8764     // align the center of el with the bottom left corner of other-el and
8765     // adjust the x position by -6 pixels (and the y position by 0)
8766     el.alignTo("other-el", "c-bl", [-6, 0]);
8767     </code></pre>
8768          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8769          * @param {String} position The position to align to.
8770          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8771          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8772          * @return {Roo.Element} this
8773          */
8774         alignTo : function(element, position, offsets, animate){
8775             var xy = this.getAlignToXY(element, position, offsets);
8776             this.setXY(xy, this.preanim(arguments, 3));
8777             return this;
8778         },
8779
8780         /**
8781          * Anchors an element to another element and realigns it when the window is resized.
8782          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8783          * @param {String} position The position to align to.
8784          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8785          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8786          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8787          * is a number, it is used as the buffer delay (defaults to 50ms).
8788          * @param {Function} callback The function to call after the animation finishes
8789          * @return {Roo.Element} this
8790          */
8791         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8792             var action = function(){
8793                 this.alignTo(el, alignment, offsets, animate);
8794                 Roo.callback(callback, this);
8795             };
8796             Roo.EventManager.onWindowResize(action, this);
8797             var tm = typeof monitorScroll;
8798             if(tm != 'undefined'){
8799                 Roo.EventManager.on(window, 'scroll', action, this,
8800                     {buffer: tm == 'number' ? monitorScroll : 50});
8801             }
8802             action.call(this); // align immediately
8803             return this;
8804         },
8805         /**
8806          * Clears any opacity settings from this element. Required in some cases for IE.
8807          * @return {Roo.Element} this
8808          */
8809         clearOpacity : function(){
8810             if (window.ActiveXObject) {
8811                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8812                     this.dom.style.filter = "";
8813                 }
8814             } else {
8815                 this.dom.style.opacity = "";
8816                 this.dom.style["-moz-opacity"] = "";
8817                 this.dom.style["-khtml-opacity"] = "";
8818             }
8819             return this;
8820         },
8821
8822         /**
8823          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8824          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8825          * @return {Roo.Element} this
8826          */
8827         hide : function(animate){
8828             this.setVisible(false, this.preanim(arguments, 0));
8829             return this;
8830         },
8831
8832         /**
8833         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8834         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8835          * @return {Roo.Element} this
8836          */
8837         show : function(animate){
8838             this.setVisible(true, this.preanim(arguments, 0));
8839             return this;
8840         },
8841
8842         /**
8843          * @private Test if size has a unit, otherwise appends the default
8844          */
8845         addUnits : function(size){
8846             return Roo.Element.addUnits(size, this.defaultUnit);
8847         },
8848
8849         /**
8850          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8851          * @return {Roo.Element} this
8852          */
8853         beginMeasure : function(){
8854             var el = this.dom;
8855             if(el.offsetWidth || el.offsetHeight){
8856                 return this; // offsets work already
8857             }
8858             var changed = [];
8859             var p = this.dom, b = document.body; // start with this element
8860             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8861                 var pe = Roo.get(p);
8862                 if(pe.getStyle('display') == 'none'){
8863                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8864                     p.style.visibility = "hidden";
8865                     p.style.display = "block";
8866                 }
8867                 p = p.parentNode;
8868             }
8869             this._measureChanged = changed;
8870             return this;
8871
8872         },
8873
8874         /**
8875          * Restores displays to before beginMeasure was called
8876          * @return {Roo.Element} this
8877          */
8878         endMeasure : function(){
8879             var changed = this._measureChanged;
8880             if(changed){
8881                 for(var i = 0, len = changed.length; i < len; i++) {
8882                     var r = changed[i];
8883                     r.el.style.visibility = r.visibility;
8884                     r.el.style.display = "none";
8885                 }
8886                 this._measureChanged = null;
8887             }
8888             return this;
8889         },
8890
8891         /**
8892         * Update the innerHTML of this element, optionally searching for and processing scripts
8893         * @param {String} html The new HTML
8894         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8895         * @param {Function} callback For async script loading you can be noticed when the update completes
8896         * @return {Roo.Element} this
8897          */
8898         update : function(html, loadScripts, callback){
8899             if(typeof html == "undefined"){
8900                 html = "";
8901             }
8902             if(loadScripts !== true){
8903                 this.dom.innerHTML = html;
8904                 if(typeof callback == "function"){
8905                     callback();
8906                 }
8907                 return this;
8908             }
8909             var id = Roo.id();
8910             var dom = this.dom;
8911
8912             html += '<span id="' + id + '"></span>';
8913
8914             E.onAvailable(id, function(){
8915                 var hd = document.getElementsByTagName("head")[0];
8916                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8917                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8918                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8919
8920                 var match;
8921                 while(match = re.exec(html)){
8922                     var attrs = match[1];
8923                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8924                     if(srcMatch && srcMatch[2]){
8925                        var s = document.createElement("script");
8926                        s.src = srcMatch[2];
8927                        var typeMatch = attrs.match(typeRe);
8928                        if(typeMatch && typeMatch[2]){
8929                            s.type = typeMatch[2];
8930                        }
8931                        hd.appendChild(s);
8932                     }else if(match[2] && match[2].length > 0){
8933                         if(window.execScript) {
8934                            window.execScript(match[2]);
8935                         } else {
8936                             /**
8937                              * eval:var:id
8938                              * eval:var:dom
8939                              * eval:var:html
8940                              * 
8941                              */
8942                            window.eval(match[2]);
8943                         }
8944                     }
8945                 }
8946                 var el = document.getElementById(id);
8947                 if(el){el.parentNode.removeChild(el);}
8948                 if(typeof callback == "function"){
8949                     callback();
8950                 }
8951             });
8952             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8953             return this;
8954         },
8955
8956         /**
8957          * Direct access to the UpdateManager update() method (takes the same parameters).
8958          * @param {String/Function} url The url for this request or a function to call to get the url
8959          * @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}
8960          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8961          * @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.
8962          * @return {Roo.Element} this
8963          */
8964         load : function(){
8965             var um = this.getUpdateManager();
8966             um.update.apply(um, arguments);
8967             return this;
8968         },
8969
8970         /**
8971         * Gets this element's UpdateManager
8972         * @return {Roo.UpdateManager} The UpdateManager
8973         */
8974         getUpdateManager : function(){
8975             if(!this.updateManager){
8976                 this.updateManager = new Roo.UpdateManager(this);
8977             }
8978             return this.updateManager;
8979         },
8980
8981         /**
8982          * Disables text selection for this element (normalized across browsers)
8983          * @return {Roo.Element} this
8984          */
8985         unselectable : function(){
8986             this.dom.unselectable = "on";
8987             this.swallowEvent("selectstart", true);
8988             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8989             this.addClass("x-unselectable");
8990             return this;
8991         },
8992
8993         /**
8994         * Calculates the x, y to center this element on the screen
8995         * @return {Array} The x, y values [x, y]
8996         */
8997         getCenterXY : function(){
8998             return this.getAlignToXY(document, 'c-c');
8999         },
9000
9001         /**
9002         * Centers the Element in either the viewport, or another Element.
9003         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
9004         */
9005         center : function(centerIn){
9006             this.alignTo(centerIn || document, 'c-c');
9007             return this;
9008         },
9009
9010         /**
9011          * Tests various css rules/browsers to determine if this element uses a border box
9012          * @return {Boolean}
9013          */
9014         isBorderBox : function(){
9015             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
9016         },
9017
9018         /**
9019          * Return a box {x, y, width, height} that can be used to set another elements
9020          * size/location to match this element.
9021          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
9022          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
9023          * @return {Object} box An object in the format {x, y, width, height}
9024          */
9025         getBox : function(contentBox, local){
9026             var xy;
9027             if(!local){
9028                 xy = this.getXY();
9029             }else{
9030                 var left = parseInt(this.getStyle("left"), 10) || 0;
9031                 var top = parseInt(this.getStyle("top"), 10) || 0;
9032                 xy = [left, top];
9033             }
9034             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
9035             if(!contentBox){
9036                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
9037             }else{
9038                 var l = this.getBorderWidth("l")+this.getPadding("l");
9039                 var r = this.getBorderWidth("r")+this.getPadding("r");
9040                 var t = this.getBorderWidth("t")+this.getPadding("t");
9041                 var b = this.getBorderWidth("b")+this.getPadding("b");
9042                 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)};
9043             }
9044             bx.right = bx.x + bx.width;
9045             bx.bottom = bx.y + bx.height;
9046             return bx;
9047         },
9048
9049         /**
9050          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
9051          for more information about the sides.
9052          * @param {String} sides
9053          * @return {Number}
9054          */
9055         getFrameWidth : function(sides, onlyContentBox){
9056             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
9057         },
9058
9059         /**
9060          * 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.
9061          * @param {Object} box The box to fill {x, y, width, height}
9062          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
9063          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9064          * @return {Roo.Element} this
9065          */
9066         setBox : function(box, adjust, animate){
9067             var w = box.width, h = box.height;
9068             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
9069                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9070                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9071             }
9072             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
9073             return this;
9074         },
9075
9076         /**
9077          * Forces the browser to repaint this element
9078          * @return {Roo.Element} this
9079          */
9080          repaint : function(){
9081             var dom = this.dom;
9082             this.addClass("x-repaint");
9083             setTimeout(function(){
9084                 Roo.get(dom).removeClass("x-repaint");
9085             }, 1);
9086             return this;
9087         },
9088
9089         /**
9090          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
9091          * then it returns the calculated width of the sides (see getPadding)
9092          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
9093          * @return {Object/Number}
9094          */
9095         getMargins : function(side){
9096             if(!side){
9097                 return {
9098                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
9099                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
9100                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
9101                     right: parseInt(this.getStyle("margin-right"), 10) || 0
9102                 };
9103             }else{
9104                 return this.addStyles(side, El.margins);
9105              }
9106         },
9107
9108         // private
9109         addStyles : function(sides, styles){
9110             var val = 0, v, w;
9111             for(var i = 0, len = sides.length; i < len; i++){
9112                 v = this.getStyle(styles[sides.charAt(i)]);
9113                 if(v){
9114                      w = parseInt(v, 10);
9115                      if(w){ val += w; }
9116                 }
9117             }
9118             return val;
9119         },
9120
9121         /**
9122          * Creates a proxy element of this element
9123          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
9124          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
9125          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
9126          * @return {Roo.Element} The new proxy element
9127          */
9128         createProxy : function(config, renderTo, matchBox){
9129             if(renderTo){
9130                 renderTo = Roo.getDom(renderTo);
9131             }else{
9132                 renderTo = document.body;
9133             }
9134             config = typeof config == "object" ?
9135                 config : {tag : "div", cls: config};
9136             var proxy = Roo.DomHelper.append(renderTo, config, true);
9137             if(matchBox){
9138                proxy.setBox(this.getBox());
9139             }
9140             return proxy;
9141         },
9142
9143         /**
9144          * Puts a mask over this element to disable user interaction. Requires core.css.
9145          * This method can only be applied to elements which accept child nodes.
9146          * @param {String} msg (optional) A message to display in the mask
9147          * @param {String} msgCls (optional) A css class to apply to the msg element
9148          * @return {Element} The mask  element
9149          */
9150         mask : function(msg, msgCls)
9151         {
9152             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
9153                 this.setStyle("position", "relative");
9154             }
9155             if(!this._mask){
9156                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
9157             }
9158             
9159             this.addClass("x-masked");
9160             this._mask.setDisplayed(true);
9161             
9162             // we wander
9163             var z = 0;
9164             var dom = this.dom;
9165             while (dom && dom.style) {
9166                 if (!isNaN(parseInt(dom.style.zIndex))) {
9167                     z = Math.max(z, parseInt(dom.style.zIndex));
9168                 }
9169                 dom = dom.parentNode;
9170             }
9171             // if we are masking the body - then it hides everything..
9172             if (this.dom == document.body) {
9173                 z = 1000000;
9174                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9175                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9176             }
9177            
9178             if(typeof msg == 'string'){
9179                 if(!this._maskMsg){
9180                     this._maskMsg = Roo.DomHelper.append(this.dom, {
9181                         cls: "roo-el-mask-msg", 
9182                         cn: [
9183                             {
9184                                 tag: 'i',
9185                                 cls: 'fa fa-spinner fa-spin'
9186                             },
9187                             {
9188                                 tag: 'div'
9189                             }   
9190                         ]
9191                     }, true);
9192                 }
9193                 var mm = this._maskMsg;
9194                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9195                 if (mm.dom.lastChild) { // weird IE issue?
9196                     mm.dom.lastChild.innerHTML = msg;
9197                 }
9198                 mm.setDisplayed(true);
9199                 mm.center(this);
9200                 mm.setStyle('z-index', z + 102);
9201             }
9202             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9203                 this._mask.setHeight(this.getHeight());
9204             }
9205             this._mask.setStyle('z-index', z + 100);
9206             
9207             return this._mask;
9208         },
9209
9210         /**
9211          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9212          * it is cached for reuse.
9213          */
9214         unmask : function(removeEl){
9215             if(this._mask){
9216                 if(removeEl === true){
9217                     this._mask.remove();
9218                     delete this._mask;
9219                     if(this._maskMsg){
9220                         this._maskMsg.remove();
9221                         delete this._maskMsg;
9222                     }
9223                 }else{
9224                     this._mask.setDisplayed(false);
9225                     if(this._maskMsg){
9226                         this._maskMsg.setDisplayed(false);
9227                     }
9228                 }
9229             }
9230             this.removeClass("x-masked");
9231         },
9232
9233         /**
9234          * Returns true if this element is masked
9235          * @return {Boolean}
9236          */
9237         isMasked : function(){
9238             return this._mask && this._mask.isVisible();
9239         },
9240
9241         /**
9242          * Creates an iframe shim for this element to keep selects and other windowed objects from
9243          * showing through.
9244          * @return {Roo.Element} The new shim element
9245          */
9246         createShim : function(){
9247             var el = document.createElement('iframe');
9248             el.frameBorder = 'no';
9249             el.className = 'roo-shim';
9250             if(Roo.isIE && Roo.isSecure){
9251                 el.src = Roo.SSL_SECURE_URL;
9252             }
9253             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9254             shim.autoBoxAdjust = false;
9255             return shim;
9256         },
9257
9258         /**
9259          * Removes this element from the DOM and deletes it from the cache
9260          */
9261         remove : function(){
9262             if(this.dom.parentNode){
9263                 this.dom.parentNode.removeChild(this.dom);
9264             }
9265             delete El.cache[this.dom.id];
9266         },
9267
9268         /**
9269          * Sets up event handlers to add and remove a css class when the mouse is over this element
9270          * @param {String} className
9271          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9272          * mouseout events for children elements
9273          * @return {Roo.Element} this
9274          */
9275         addClassOnOver : function(className, preventFlicker){
9276             this.on("mouseover", function(){
9277                 Roo.fly(this, '_internal').addClass(className);
9278             }, this.dom);
9279             var removeFn = function(e){
9280                 if(preventFlicker !== true || !e.within(this, true)){
9281                     Roo.fly(this, '_internal').removeClass(className);
9282                 }
9283             };
9284             this.on("mouseout", removeFn, this.dom);
9285             return this;
9286         },
9287
9288         /**
9289          * Sets up event handlers to add and remove a css class when this element has the focus
9290          * @param {String} className
9291          * @return {Roo.Element} this
9292          */
9293         addClassOnFocus : function(className){
9294             this.on("focus", function(){
9295                 Roo.fly(this, '_internal').addClass(className);
9296             }, this.dom);
9297             this.on("blur", function(){
9298                 Roo.fly(this, '_internal').removeClass(className);
9299             }, this.dom);
9300             return this;
9301         },
9302         /**
9303          * 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)
9304          * @param {String} className
9305          * @return {Roo.Element} this
9306          */
9307         addClassOnClick : function(className){
9308             var dom = this.dom;
9309             this.on("mousedown", function(){
9310                 Roo.fly(dom, '_internal').addClass(className);
9311                 var d = Roo.get(document);
9312                 var fn = function(){
9313                     Roo.fly(dom, '_internal').removeClass(className);
9314                     d.removeListener("mouseup", fn);
9315                 };
9316                 d.on("mouseup", fn);
9317             });
9318             return this;
9319         },
9320
9321         /**
9322          * Stops the specified event from bubbling and optionally prevents the default action
9323          * @param {String} eventName
9324          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9325          * @return {Roo.Element} this
9326          */
9327         swallowEvent : function(eventName, preventDefault){
9328             var fn = function(e){
9329                 e.stopPropagation();
9330                 if(preventDefault){
9331                     e.preventDefault();
9332                 }
9333             };
9334             if(eventName instanceof Array){
9335                 for(var i = 0, len = eventName.length; i < len; i++){
9336                      this.on(eventName[i], fn);
9337                 }
9338                 return this;
9339             }
9340             this.on(eventName, fn);
9341             return this;
9342         },
9343
9344         /**
9345          * @private
9346          */
9347       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9348
9349         /**
9350          * Sizes this element to its parent element's dimensions performing
9351          * neccessary box adjustments.
9352          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9353          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9354          * @return {Roo.Element} this
9355          */
9356         fitToParent : function(monitorResize, targetParent) {
9357           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9358           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9359           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9360             return;
9361           }
9362           var p = Roo.get(targetParent || this.dom.parentNode);
9363           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9364           if (monitorResize === true) {
9365             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9366             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9367           }
9368           return this;
9369         },
9370
9371         /**
9372          * Gets the next sibling, skipping text nodes
9373          * @return {HTMLElement} The next sibling or null
9374          */
9375         getNextSibling : function(){
9376             var n = this.dom.nextSibling;
9377             while(n && n.nodeType != 1){
9378                 n = n.nextSibling;
9379             }
9380             return n;
9381         },
9382
9383         /**
9384          * Gets the previous sibling, skipping text nodes
9385          * @return {HTMLElement} The previous sibling or null
9386          */
9387         getPrevSibling : function(){
9388             var n = this.dom.previousSibling;
9389             while(n && n.nodeType != 1){
9390                 n = n.previousSibling;
9391             }
9392             return n;
9393         },
9394
9395
9396         /**
9397          * Appends the passed element(s) to this element
9398          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9399          * @return {Roo.Element} this
9400          */
9401         appendChild: function(el){
9402             el = Roo.get(el);
9403             el.appendTo(this);
9404             return this;
9405         },
9406
9407         /**
9408          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9409          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9410          * automatically generated with the specified attributes.
9411          * @param {HTMLElement} insertBefore (optional) a child element of this element
9412          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9413          * @return {Roo.Element} The new child element
9414          */
9415         createChild: function(config, insertBefore, returnDom){
9416             config = config || {tag:'div'};
9417             if(insertBefore){
9418                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9419             }
9420             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9421         },
9422
9423         /**
9424          * Appends this element to the passed element
9425          * @param {String/HTMLElement/Element} el The new parent element
9426          * @return {Roo.Element} this
9427          */
9428         appendTo: function(el){
9429             el = Roo.getDom(el);
9430             el.appendChild(this.dom);
9431             return this;
9432         },
9433
9434         /**
9435          * Inserts this element before the passed element in the DOM
9436          * @param {String/HTMLElement/Element} el The element to insert before
9437          * @return {Roo.Element} this
9438          */
9439         insertBefore: function(el){
9440             el = Roo.getDom(el);
9441             el.parentNode.insertBefore(this.dom, el);
9442             return this;
9443         },
9444
9445         /**
9446          * Inserts this element after the passed element in the DOM
9447          * @param {String/HTMLElement/Element} el The element to insert after
9448          * @return {Roo.Element} this
9449          */
9450         insertAfter: function(el){
9451             el = Roo.getDom(el);
9452             el.parentNode.insertBefore(this.dom, el.nextSibling);
9453             return this;
9454         },
9455
9456         /**
9457          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9458          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9459          * @return {Roo.Element} The new child
9460          */
9461         insertFirst: function(el, returnDom){
9462             el = el || {};
9463             if(typeof el == 'object' && !el.nodeType){ // dh config
9464                 return this.createChild(el, this.dom.firstChild, returnDom);
9465             }else{
9466                 el = Roo.getDom(el);
9467                 this.dom.insertBefore(el, this.dom.firstChild);
9468                 return !returnDom ? Roo.get(el) : el;
9469             }
9470         },
9471
9472         /**
9473          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9474          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9475          * @param {String} where (optional) 'before' or 'after' defaults to before
9476          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9477          * @return {Roo.Element} the inserted Element
9478          */
9479         insertSibling: function(el, where, returnDom){
9480             where = where ? where.toLowerCase() : 'before';
9481             el = el || {};
9482             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9483
9484             if(typeof el == 'object' && !el.nodeType){ // dh config
9485                 if(where == 'after' && !this.dom.nextSibling){
9486                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9487                 }else{
9488                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9489                 }
9490
9491             }else{
9492                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9493                             where == 'before' ? this.dom : this.dom.nextSibling);
9494                 if(!returnDom){
9495                     rt = Roo.get(rt);
9496                 }
9497             }
9498             return rt;
9499         },
9500
9501         /**
9502          * Creates and wraps this element with another element
9503          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9504          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9505          * @return {HTMLElement/Element} The newly created wrapper element
9506          */
9507         wrap: function(config, returnDom){
9508             if(!config){
9509                 config = {tag: "div"};
9510             }
9511             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9512             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9513             return newEl;
9514         },
9515
9516         /**
9517          * Replaces the passed element with this element
9518          * @param {String/HTMLElement/Element} el The element to replace
9519          * @return {Roo.Element} this
9520          */
9521         replace: function(el){
9522             el = Roo.get(el);
9523             this.insertBefore(el);
9524             el.remove();
9525             return this;
9526         },
9527
9528         /**
9529          * Inserts an html fragment into this element
9530          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9531          * @param {String} html The HTML fragment
9532          * @param {Boolean} returnEl True to return an Roo.Element
9533          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9534          */
9535         insertHtml : function(where, html, returnEl){
9536             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9537             return returnEl ? Roo.get(el) : el;
9538         },
9539
9540         /**
9541          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9542          * @param {Object} o The object with the attributes
9543          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9544          * @return {Roo.Element} this
9545          */
9546         set : function(o, useSet){
9547             var el = this.dom;
9548             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9549             for(var attr in o){
9550                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
9551                 if(attr=="cls"){
9552                     el.className = o["cls"];
9553                 }else{
9554                     if(useSet) {
9555                         el.setAttribute(attr, o[attr]);
9556                     } else {
9557                         el[attr] = o[attr];
9558                     }
9559                 }
9560             }
9561             if(o.style){
9562                 Roo.DomHelper.applyStyles(el, o.style);
9563             }
9564             return this;
9565         },
9566
9567         /**
9568          * Convenience method for constructing a KeyMap
9569          * @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:
9570          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9571          * @param {Function} fn The function to call
9572          * @param {Object} scope (optional) The scope of the function
9573          * @return {Roo.KeyMap} The KeyMap created
9574          */
9575         addKeyListener : function(key, fn, scope){
9576             var config;
9577             if(typeof key != "object" || key instanceof Array){
9578                 config = {
9579                     key: key,
9580                     fn: fn,
9581                     scope: scope
9582                 };
9583             }else{
9584                 config = {
9585                     key : key.key,
9586                     shift : key.shift,
9587                     ctrl : key.ctrl,
9588                     alt : key.alt,
9589                     fn: fn,
9590                     scope: scope
9591                 };
9592             }
9593             return new Roo.KeyMap(this, config);
9594         },
9595
9596         /**
9597          * Creates a KeyMap for this element
9598          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9599          * @return {Roo.KeyMap} The KeyMap created
9600          */
9601         addKeyMap : function(config){
9602             return new Roo.KeyMap(this, config);
9603         },
9604
9605         /**
9606          * Returns true if this element is scrollable.
9607          * @return {Boolean}
9608          */
9609          isScrollable : function(){
9610             var dom = this.dom;
9611             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9612         },
9613
9614         /**
9615          * 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().
9616          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9617          * @param {Number} value The new scroll value
9618          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9619          * @return {Element} this
9620          */
9621
9622         scrollTo : function(side, value, animate){
9623             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9624             if(!animate || !A){
9625                 this.dom[prop] = value;
9626             }else{
9627                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9628                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9629             }
9630             return this;
9631         },
9632
9633         /**
9634          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9635          * within this element's scrollable range.
9636          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9637          * @param {Number} distance How far to scroll the element in pixels
9638          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9639          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9640          * was scrolled as far as it could go.
9641          */
9642          scroll : function(direction, distance, animate){
9643              if(!this.isScrollable()){
9644                  return;
9645              }
9646              var el = this.dom;
9647              var l = el.scrollLeft, t = el.scrollTop;
9648              var w = el.scrollWidth, h = el.scrollHeight;
9649              var cw = el.clientWidth, ch = el.clientHeight;
9650              direction = direction.toLowerCase();
9651              var scrolled = false;
9652              var a = this.preanim(arguments, 2);
9653              switch(direction){
9654                  case "l":
9655                  case "left":
9656                      if(w - l > cw){
9657                          var v = Math.min(l + distance, w-cw);
9658                          this.scrollTo("left", v, a);
9659                          scrolled = true;
9660                      }
9661                      break;
9662                 case "r":
9663                 case "right":
9664                      if(l > 0){
9665                          var v = Math.max(l - distance, 0);
9666                          this.scrollTo("left", v, a);
9667                          scrolled = true;
9668                      }
9669                      break;
9670                 case "t":
9671                 case "top":
9672                 case "up":
9673                      if(t > 0){
9674                          var v = Math.max(t - distance, 0);
9675                          this.scrollTo("top", v, a);
9676                          scrolled = true;
9677                      }
9678                      break;
9679                 case "b":
9680                 case "bottom":
9681                 case "down":
9682                      if(h - t > ch){
9683                          var v = Math.min(t + distance, h-ch);
9684                          this.scrollTo("top", v, a);
9685                          scrolled = true;
9686                      }
9687                      break;
9688              }
9689              return scrolled;
9690         },
9691
9692         /**
9693          * Translates the passed page coordinates into left/top css values for this element
9694          * @param {Number/Array} x The page x or an array containing [x, y]
9695          * @param {Number} y The page y
9696          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9697          */
9698         translatePoints : function(x, y){
9699             if(typeof x == 'object' || x instanceof Array){
9700                 y = x[1]; x = x[0];
9701             }
9702             var p = this.getStyle('position');
9703             var o = this.getXY();
9704
9705             var l = parseInt(this.getStyle('left'), 10);
9706             var t = parseInt(this.getStyle('top'), 10);
9707
9708             if(isNaN(l)){
9709                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9710             }
9711             if(isNaN(t)){
9712                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9713             }
9714
9715             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9716         },
9717
9718         /**
9719          * Returns the current scroll position of the element.
9720          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9721          */
9722         getScroll : function(){
9723             var d = this.dom, doc = document;
9724             if(d == doc || d == doc.body){
9725                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9726                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9727                 return {left: l, top: t};
9728             }else{
9729                 return {left: d.scrollLeft, top: d.scrollTop};
9730             }
9731         },
9732
9733         /**
9734          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9735          * are convert to standard 6 digit hex color.
9736          * @param {String} attr The css attribute
9737          * @param {String} defaultValue The default value to use when a valid color isn't found
9738          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9739          * YUI color anims.
9740          */
9741         getColor : function(attr, defaultValue, prefix){
9742             var v = this.getStyle(attr);
9743             if(!v || v == "transparent" || v == "inherit") {
9744                 return defaultValue;
9745             }
9746             var color = typeof prefix == "undefined" ? "#" : prefix;
9747             if(v.substr(0, 4) == "rgb("){
9748                 var rvs = v.slice(4, v.length -1).split(",");
9749                 for(var i = 0; i < 3; i++){
9750                     var h = parseInt(rvs[i]).toString(16);
9751                     if(h < 16){
9752                         h = "0" + h;
9753                     }
9754                     color += h;
9755                 }
9756             } else {
9757                 if(v.substr(0, 1) == "#"){
9758                     if(v.length == 4) {
9759                         for(var i = 1; i < 4; i++){
9760                             var c = v.charAt(i);
9761                             color +=  c + c;
9762                         }
9763                     }else if(v.length == 7){
9764                         color += v.substr(1);
9765                     }
9766                 }
9767             }
9768             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9769         },
9770
9771         /**
9772          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9773          * gradient background, rounded corners and a 4-way shadow.
9774          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9775          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9776          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9777          * @return {Roo.Element} this
9778          */
9779         boxWrap : function(cls){
9780             cls = cls || 'x-box';
9781             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9782             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9783             return el;
9784         },
9785
9786         /**
9787          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9788          * @param {String} namespace The namespace in which to look for the attribute
9789          * @param {String} name The attribute name
9790          * @return {String} The attribute value
9791          */
9792         getAttributeNS : Roo.isIE ? function(ns, name){
9793             var d = this.dom;
9794             var type = typeof d[ns+":"+name];
9795             if(type != 'undefined' && type != 'unknown'){
9796                 return d[ns+":"+name];
9797             }
9798             return d[name];
9799         } : function(ns, name){
9800             var d = this.dom;
9801             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9802         },
9803         
9804         
9805         /**
9806          * Sets or Returns the value the dom attribute value
9807          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9808          * @param {String} value (optional) The value to set the attribute to
9809          * @return {String} The attribute value
9810          */
9811         attr : function(name){
9812             if (arguments.length > 1) {
9813                 this.dom.setAttribute(name, arguments[1]);
9814                 return arguments[1];
9815             }
9816             if (typeof(name) == 'object') {
9817                 for(var i in name) {
9818                     this.attr(i, name[i]);
9819                 }
9820                 return name;
9821             }
9822             
9823             
9824             if (!this.dom.hasAttribute(name)) {
9825                 return undefined;
9826             }
9827             return this.dom.getAttribute(name);
9828         }
9829         
9830         
9831         
9832     };
9833
9834     var ep = El.prototype;
9835
9836     /**
9837      * Appends an event handler (Shorthand for addListener)
9838      * @param {String}   eventName     The type of event to append
9839      * @param {Function} fn        The method the event invokes
9840      * @param {Object} scope       (optional) The scope (this object) of the fn
9841      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9842      * @method
9843      */
9844     ep.on = ep.addListener;
9845         // backwards compat
9846     ep.mon = ep.addListener;
9847
9848     /**
9849      * Removes an event handler from this element (shorthand for removeListener)
9850      * @param {String} eventName the type of event to remove
9851      * @param {Function} fn the method the event invokes
9852      * @return {Roo.Element} this
9853      * @method
9854      */
9855     ep.un = ep.removeListener;
9856
9857     /**
9858      * true to automatically adjust width and height settings for box-model issues (default to true)
9859      */
9860     ep.autoBoxAdjust = true;
9861
9862     // private
9863     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9864
9865     // private
9866     El.addUnits = function(v, defaultUnit){
9867         if(v === "" || v == "auto"){
9868             return v;
9869         }
9870         if(v === undefined){
9871             return '';
9872         }
9873         if(typeof v == "number" || !El.unitPattern.test(v)){
9874             return v + (defaultUnit || 'px');
9875         }
9876         return v;
9877     };
9878
9879     // special markup used throughout Roo when box wrapping elements
9880     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>';
9881     /**
9882      * Visibility mode constant - Use visibility to hide element
9883      * @static
9884      * @type Number
9885      */
9886     El.VISIBILITY = 1;
9887     /**
9888      * Visibility mode constant - Use display to hide element
9889      * @static
9890      * @type Number
9891      */
9892     El.DISPLAY = 2;
9893
9894     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9895     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9896     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9897
9898
9899
9900     /**
9901      * @private
9902      */
9903     El.cache = {};
9904
9905     var docEl;
9906
9907     /**
9908      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9909      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9910      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9911      * @return {Element} The Element object
9912      * @static
9913      */
9914     El.get = function(el){
9915         var ex, elm, id;
9916         if(!el){ return null; }
9917         if(typeof el == "string"){ // element id
9918             if(!(elm = document.getElementById(el))){
9919                 return null;
9920             }
9921             if(ex = El.cache[el]){
9922                 ex.dom = elm;
9923             }else{
9924                 ex = El.cache[el] = new El(elm);
9925             }
9926             return ex;
9927         }else if(el.tagName){ // dom element
9928             if(!(id = el.id)){
9929                 id = Roo.id(el);
9930             }
9931             if(ex = El.cache[id]){
9932                 ex.dom = el;
9933             }else{
9934                 ex = El.cache[id] = new El(el);
9935             }
9936             return ex;
9937         }else if(el instanceof El){
9938             if(el != docEl){
9939                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9940                                                               // catch case where it hasn't been appended
9941                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9942             }
9943             return el;
9944         }else if(el.isComposite){
9945             return el;
9946         }else if(el instanceof Array){
9947             return El.select(el);
9948         }else if(el == document){
9949             // create a bogus element object representing the document object
9950             if(!docEl){
9951                 var f = function(){};
9952                 f.prototype = El.prototype;
9953                 docEl = new f();
9954                 docEl.dom = document;
9955             }
9956             return docEl;
9957         }
9958         return null;
9959     };
9960
9961     // private
9962     El.uncache = function(el){
9963         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9964             if(a[i]){
9965                 delete El.cache[a[i].id || a[i]];
9966             }
9967         }
9968     };
9969
9970     // private
9971     // Garbage collection - uncache elements/purge listeners on orphaned elements
9972     // so we don't hold a reference and cause the browser to retain them
9973     El.garbageCollect = function(){
9974         if(!Roo.enableGarbageCollector){
9975             clearInterval(El.collectorThread);
9976             return;
9977         }
9978         for(var eid in El.cache){
9979             var el = El.cache[eid], d = el.dom;
9980             // -------------------------------------------------------
9981             // Determining what is garbage:
9982             // -------------------------------------------------------
9983             // !d
9984             // dom node is null, definitely garbage
9985             // -------------------------------------------------------
9986             // !d.parentNode
9987             // no parentNode == direct orphan, definitely garbage
9988             // -------------------------------------------------------
9989             // !d.offsetParent && !document.getElementById(eid)
9990             // display none elements have no offsetParent so we will
9991             // also try to look it up by it's id. However, check
9992             // offsetParent first so we don't do unneeded lookups.
9993             // This enables collection of elements that are not orphans
9994             // directly, but somewhere up the line they have an orphan
9995             // parent.
9996             // -------------------------------------------------------
9997             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9998                 delete El.cache[eid];
9999                 if(d && Roo.enableListenerCollection){
10000                     E.purgeElement(d);
10001                 }
10002             }
10003         }
10004     }
10005     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
10006
10007
10008     // dom is optional
10009     El.Flyweight = function(dom){
10010         this.dom = dom;
10011     };
10012     El.Flyweight.prototype = El.prototype;
10013
10014     El._flyweights = {};
10015     /**
10016      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10017      * the dom node can be overwritten by other code.
10018      * @param {String/HTMLElement} el The dom node or id
10019      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10020      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10021      * @static
10022      * @return {Element} The shared Element object
10023      */
10024     El.fly = function(el, named){
10025         named = named || '_global';
10026         el = Roo.getDom(el);
10027         if(!el){
10028             return null;
10029         }
10030         if(!El._flyweights[named]){
10031             El._flyweights[named] = new El.Flyweight();
10032         }
10033         El._flyweights[named].dom = el;
10034         return El._flyweights[named];
10035     };
10036
10037     /**
10038      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10039      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10040      * Shorthand of {@link Roo.Element#get}
10041      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10042      * @return {Element} The Element object
10043      * @member Roo
10044      * @method get
10045      */
10046     Roo.get = El.get;
10047     /**
10048      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10049      * the dom node can be overwritten by other code.
10050      * Shorthand of {@link Roo.Element#fly}
10051      * @param {String/HTMLElement} el The dom node or id
10052      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10053      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10054      * @static
10055      * @return {Element} The shared Element object
10056      * @member Roo
10057      * @method fly
10058      */
10059     Roo.fly = El.fly;
10060
10061     // speedy lookup for elements never to box adjust
10062     var noBoxAdjust = Roo.isStrict ? {
10063         select:1
10064     } : {
10065         input:1, select:1, textarea:1
10066     };
10067     if(Roo.isIE || Roo.isGecko){
10068         noBoxAdjust['button'] = 1;
10069     }
10070
10071
10072     Roo.EventManager.on(window, 'unload', function(){
10073         delete El.cache;
10074         delete El._flyweights;
10075     });
10076 })();
10077
10078
10079
10080
10081 if(Roo.DomQuery){
10082     Roo.Element.selectorFunction = Roo.DomQuery.select;
10083 }
10084
10085 Roo.Element.select = function(selector, unique, root){
10086     var els;
10087     if(typeof selector == "string"){
10088         els = Roo.Element.selectorFunction(selector, root);
10089     }else if(selector.length !== undefined){
10090         els = selector;
10091     }else{
10092         throw "Invalid selector";
10093     }
10094     if(unique === true){
10095         return new Roo.CompositeElement(els);
10096     }else{
10097         return new Roo.CompositeElementLite(els);
10098     }
10099 };
10100 /**
10101  * Selects elements based on the passed CSS selector to enable working on them as 1.
10102  * @param {String/Array} selector The CSS selector or an array of elements
10103  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
10104  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
10105  * @return {CompositeElementLite/CompositeElement}
10106  * @member Roo
10107  * @method select
10108  */
10109 Roo.select = Roo.Element.select;
10110
10111
10112
10113
10114
10115
10116
10117
10118
10119
10120
10121
10122
10123
10124 /*
10125  * Based on:
10126  * Ext JS Library 1.1.1
10127  * Copyright(c) 2006-2007, Ext JS, LLC.
10128  *
10129  * Originally Released Under LGPL - original licence link has changed is not relivant.
10130  *
10131  * Fork - LGPL
10132  * <script type="text/javascript">
10133  */
10134
10135
10136
10137 //Notifies Element that fx methods are available
10138 Roo.enableFx = true;
10139
10140 /**
10141  * @class Roo.Fx
10142  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
10143  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
10144  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
10145  * Element effects to work.</p><br/>
10146  *
10147  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
10148  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
10149  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
10150  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
10151  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
10152  * expected results and should be done with care.</p><br/>
10153  *
10154  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
10155  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
10156 <pre>
10157 Value  Description
10158 -----  -----------------------------
10159 tl     The top left corner
10160 t      The center of the top edge
10161 tr     The top right corner
10162 l      The center of the left edge
10163 r      The center of the right edge
10164 bl     The bottom left corner
10165 b      The center of the bottom edge
10166 br     The bottom right corner
10167 </pre>
10168  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
10169  * below are common options that can be passed to any Fx method.</b>
10170  * @cfg {Function} callback A function called when the effect is finished
10171  * @cfg {Object} scope The scope of the effect function
10172  * @cfg {String} easing A valid Easing value for the effect
10173  * @cfg {String} afterCls A css class to apply after the effect
10174  * @cfg {Number} duration The length of time (in seconds) that the effect should last
10175  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
10176  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
10177  * effects that end with the element being visually hidden, ignored otherwise)
10178  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
10179  * a function which returns such a specification that will be applied to the Element after the effect finishes
10180  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
10181  * @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
10182  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10183  */
10184 Roo.Fx = {
10185         /**
10186          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10187          * origin for the slide effect.  This function automatically handles wrapping the element with
10188          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10189          * Usage:
10190          *<pre><code>
10191 // default: slide the element in from the top
10192 el.slideIn();
10193
10194 // custom: slide the element in from the right with a 2-second duration
10195 el.slideIn('r', { duration: 2 });
10196
10197 // common config options shown with default values
10198 el.slideIn('t', {
10199     easing: 'easeOut',
10200     duration: .5
10201 });
10202 </code></pre>
10203          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10204          * @param {Object} options (optional) Object literal with any of the Fx config options
10205          * @return {Roo.Element} The Element
10206          */
10207     slideIn : function(anchor, o){
10208         var el = this.getFxEl();
10209         o = o || {};
10210
10211         el.queueFx(o, function(){
10212
10213             anchor = anchor || "t";
10214
10215             // fix display to visibility
10216             this.fixDisplay();
10217
10218             // restore values after effect
10219             var r = this.getFxRestore();
10220             var b = this.getBox();
10221             // fixed size for slide
10222             this.setSize(b);
10223
10224             // wrap if needed
10225             var wrap = this.fxWrap(r.pos, o, "hidden");
10226
10227             var st = this.dom.style;
10228             st.visibility = "visible";
10229             st.position = "absolute";
10230
10231             // clear out temp styles after slide and unwrap
10232             var after = function(){
10233                 el.fxUnwrap(wrap, r.pos, o);
10234                 st.width = r.width;
10235                 st.height = r.height;
10236                 el.afterFx(o);
10237             };
10238             // time to calc the positions
10239             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10240
10241             switch(anchor.toLowerCase()){
10242                 case "t":
10243                     wrap.setSize(b.width, 0);
10244                     st.left = st.bottom = "0";
10245                     a = {height: bh};
10246                 break;
10247                 case "l":
10248                     wrap.setSize(0, b.height);
10249                     st.right = st.top = "0";
10250                     a = {width: bw};
10251                 break;
10252                 case "r":
10253                     wrap.setSize(0, b.height);
10254                     wrap.setX(b.right);
10255                     st.left = st.top = "0";
10256                     a = {width: bw, points: pt};
10257                 break;
10258                 case "b":
10259                     wrap.setSize(b.width, 0);
10260                     wrap.setY(b.bottom);
10261                     st.left = st.top = "0";
10262                     a = {height: bh, points: pt};
10263                 break;
10264                 case "tl":
10265                     wrap.setSize(0, 0);
10266                     st.right = st.bottom = "0";
10267                     a = {width: bw, height: bh};
10268                 break;
10269                 case "bl":
10270                     wrap.setSize(0, 0);
10271                     wrap.setY(b.y+b.height);
10272                     st.right = st.top = "0";
10273                     a = {width: bw, height: bh, points: pt};
10274                 break;
10275                 case "br":
10276                     wrap.setSize(0, 0);
10277                     wrap.setXY([b.right, b.bottom]);
10278                     st.left = st.top = "0";
10279                     a = {width: bw, height: bh, points: pt};
10280                 break;
10281                 case "tr":
10282                     wrap.setSize(0, 0);
10283                     wrap.setX(b.x+b.width);
10284                     st.left = st.bottom = "0";
10285                     a = {width: bw, height: bh, points: pt};
10286                 break;
10287             }
10288             this.dom.style.visibility = "visible";
10289             wrap.show();
10290
10291             arguments.callee.anim = wrap.fxanim(a,
10292                 o,
10293                 'motion',
10294                 .5,
10295                 'easeOut', after);
10296         });
10297         return this;
10298     },
10299     
10300         /**
10301          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10302          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10303          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10304          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10305          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10306          * Usage:
10307          *<pre><code>
10308 // default: slide the element out to the top
10309 el.slideOut();
10310
10311 // custom: slide the element out to the right with a 2-second duration
10312 el.slideOut('r', { duration: 2 });
10313
10314 // common config options shown with default values
10315 el.slideOut('t', {
10316     easing: 'easeOut',
10317     duration: .5,
10318     remove: false,
10319     useDisplay: false
10320 });
10321 </code></pre>
10322          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10323          * @param {Object} options (optional) Object literal with any of the Fx config options
10324          * @return {Roo.Element} The Element
10325          */
10326     slideOut : function(anchor, o){
10327         var el = this.getFxEl();
10328         o = o || {};
10329
10330         el.queueFx(o, function(){
10331
10332             anchor = anchor || "t";
10333
10334             // restore values after effect
10335             var r = this.getFxRestore();
10336             
10337             var b = this.getBox();
10338             // fixed size for slide
10339             this.setSize(b);
10340
10341             // wrap if needed
10342             var wrap = this.fxWrap(r.pos, o, "visible");
10343
10344             var st = this.dom.style;
10345             st.visibility = "visible";
10346             st.position = "absolute";
10347
10348             wrap.setSize(b);
10349
10350             var after = function(){
10351                 if(o.useDisplay){
10352                     el.setDisplayed(false);
10353                 }else{
10354                     el.hide();
10355                 }
10356
10357                 el.fxUnwrap(wrap, r.pos, o);
10358
10359                 st.width = r.width;
10360                 st.height = r.height;
10361
10362                 el.afterFx(o);
10363             };
10364
10365             var a, zero = {to: 0};
10366             switch(anchor.toLowerCase()){
10367                 case "t":
10368                     st.left = st.bottom = "0";
10369                     a = {height: zero};
10370                 break;
10371                 case "l":
10372                     st.right = st.top = "0";
10373                     a = {width: zero};
10374                 break;
10375                 case "r":
10376                     st.left = st.top = "0";
10377                     a = {width: zero, points: {to:[b.right, b.y]}};
10378                 break;
10379                 case "b":
10380                     st.left = st.top = "0";
10381                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10382                 break;
10383                 case "tl":
10384                     st.right = st.bottom = "0";
10385                     a = {width: zero, height: zero};
10386                 break;
10387                 case "bl":
10388                     st.right = st.top = "0";
10389                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10390                 break;
10391                 case "br":
10392                     st.left = st.top = "0";
10393                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10394                 break;
10395                 case "tr":
10396                     st.left = st.bottom = "0";
10397                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10398                 break;
10399             }
10400
10401             arguments.callee.anim = wrap.fxanim(a,
10402                 o,
10403                 'motion',
10404                 .5,
10405                 "easeOut", after);
10406         });
10407         return this;
10408     },
10409
10410         /**
10411          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10412          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10413          * The element must be removed from the DOM using the 'remove' config option if desired.
10414          * Usage:
10415          *<pre><code>
10416 // default
10417 el.puff();
10418
10419 // common config options shown with default values
10420 el.puff({
10421     easing: 'easeOut',
10422     duration: .5,
10423     remove: false,
10424     useDisplay: false
10425 });
10426 </code></pre>
10427          * @param {Object} options (optional) Object literal with any of the Fx config options
10428          * @return {Roo.Element} The Element
10429          */
10430     puff : function(o){
10431         var el = this.getFxEl();
10432         o = o || {};
10433
10434         el.queueFx(o, function(){
10435             this.clearOpacity();
10436             this.show();
10437
10438             // restore values after effect
10439             var r = this.getFxRestore();
10440             var st = this.dom.style;
10441
10442             var after = function(){
10443                 if(o.useDisplay){
10444                     el.setDisplayed(false);
10445                 }else{
10446                     el.hide();
10447                 }
10448
10449                 el.clearOpacity();
10450
10451                 el.setPositioning(r.pos);
10452                 st.width = r.width;
10453                 st.height = r.height;
10454                 st.fontSize = '';
10455                 el.afterFx(o);
10456             };
10457
10458             var width = this.getWidth();
10459             var height = this.getHeight();
10460
10461             arguments.callee.anim = this.fxanim({
10462                     width : {to: this.adjustWidth(width * 2)},
10463                     height : {to: this.adjustHeight(height * 2)},
10464                     points : {by: [-(width * .5), -(height * .5)]},
10465                     opacity : {to: 0},
10466                     fontSize: {to:200, unit: "%"}
10467                 },
10468                 o,
10469                 'motion',
10470                 .5,
10471                 "easeOut", after);
10472         });
10473         return this;
10474     },
10475
10476         /**
10477          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10478          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10479          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10480          * Usage:
10481          *<pre><code>
10482 // default
10483 el.switchOff();
10484
10485 // all config options shown with default values
10486 el.switchOff({
10487     easing: 'easeIn',
10488     duration: .3,
10489     remove: false,
10490     useDisplay: false
10491 });
10492 </code></pre>
10493          * @param {Object} options (optional) Object literal with any of the Fx config options
10494          * @return {Roo.Element} The Element
10495          */
10496     switchOff : function(o){
10497         var el = this.getFxEl();
10498         o = o || {};
10499
10500         el.queueFx(o, function(){
10501             this.clearOpacity();
10502             this.clip();
10503
10504             // restore values after effect
10505             var r = this.getFxRestore();
10506             var st = this.dom.style;
10507
10508             var after = function(){
10509                 if(o.useDisplay){
10510                     el.setDisplayed(false);
10511                 }else{
10512                     el.hide();
10513                 }
10514
10515                 el.clearOpacity();
10516                 el.setPositioning(r.pos);
10517                 st.width = r.width;
10518                 st.height = r.height;
10519
10520                 el.afterFx(o);
10521             };
10522
10523             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10524                 this.clearOpacity();
10525                 (function(){
10526                     this.fxanim({
10527                         height:{to:1},
10528                         points:{by:[0, this.getHeight() * .5]}
10529                     }, o, 'motion', 0.3, 'easeIn', after);
10530                 }).defer(100, this);
10531             });
10532         });
10533         return this;
10534     },
10535
10536     /**
10537      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10538      * changed using the "attr" config option) and then fading back to the original color. If no original
10539      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10540      * Usage:
10541 <pre><code>
10542 // default: highlight background to yellow
10543 el.highlight();
10544
10545 // custom: highlight foreground text to blue for 2 seconds
10546 el.highlight("0000ff", { attr: 'color', duration: 2 });
10547
10548 // common config options shown with default values
10549 el.highlight("ffff9c", {
10550     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10551     endColor: (current color) or "ffffff",
10552     easing: 'easeIn',
10553     duration: 1
10554 });
10555 </code></pre>
10556      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10557      * @param {Object} options (optional) Object literal with any of the Fx config options
10558      * @return {Roo.Element} The Element
10559      */ 
10560     highlight : function(color, o){
10561         var el = this.getFxEl();
10562         o = o || {};
10563
10564         el.queueFx(o, function(){
10565             color = color || "ffff9c";
10566             attr = o.attr || "backgroundColor";
10567
10568             this.clearOpacity();
10569             this.show();
10570
10571             var origColor = this.getColor(attr);
10572             var restoreColor = this.dom.style[attr];
10573             endColor = (o.endColor || origColor) || "ffffff";
10574
10575             var after = function(){
10576                 el.dom.style[attr] = restoreColor;
10577                 el.afterFx(o);
10578             };
10579
10580             var a = {};
10581             a[attr] = {from: color, to: endColor};
10582             arguments.callee.anim = this.fxanim(a,
10583                 o,
10584                 'color',
10585                 1,
10586                 'easeIn', after);
10587         });
10588         return this;
10589     },
10590
10591    /**
10592     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10593     * Usage:
10594 <pre><code>
10595 // default: a single light blue ripple
10596 el.frame();
10597
10598 // custom: 3 red ripples lasting 3 seconds total
10599 el.frame("ff0000", 3, { duration: 3 });
10600
10601 // common config options shown with default values
10602 el.frame("C3DAF9", 1, {
10603     duration: 1 //duration of entire animation (not each individual ripple)
10604     // Note: Easing is not configurable and will be ignored if included
10605 });
10606 </code></pre>
10607     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10608     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10609     * @param {Object} options (optional) Object literal with any of the Fx config options
10610     * @return {Roo.Element} The Element
10611     */
10612     frame : function(color, count, o){
10613         var el = this.getFxEl();
10614         o = o || {};
10615
10616         el.queueFx(o, function(){
10617             color = color || "#C3DAF9";
10618             if(color.length == 6){
10619                 color = "#" + color;
10620             }
10621             count = count || 1;
10622             duration = o.duration || 1;
10623             this.show();
10624
10625             var b = this.getBox();
10626             var animFn = function(){
10627                 var proxy = this.createProxy({
10628
10629                      style:{
10630                         visbility:"hidden",
10631                         position:"absolute",
10632                         "z-index":"35000", // yee haw
10633                         border:"0px solid " + color
10634                      }
10635                   });
10636                 var scale = Roo.isBorderBox ? 2 : 1;
10637                 proxy.animate({
10638                     top:{from:b.y, to:b.y - 20},
10639                     left:{from:b.x, to:b.x - 20},
10640                     borderWidth:{from:0, to:10},
10641                     opacity:{from:1, to:0},
10642                     height:{from:b.height, to:(b.height + (20*scale))},
10643                     width:{from:b.width, to:(b.width + (20*scale))}
10644                 }, duration, function(){
10645                     proxy.remove();
10646                 });
10647                 if(--count > 0){
10648                      animFn.defer((duration/2)*1000, this);
10649                 }else{
10650                     el.afterFx(o);
10651                 }
10652             };
10653             animFn.call(this);
10654         });
10655         return this;
10656     },
10657
10658    /**
10659     * Creates a pause before any subsequent queued effects begin.  If there are
10660     * no effects queued after the pause it will have no effect.
10661     * Usage:
10662 <pre><code>
10663 el.pause(1);
10664 </code></pre>
10665     * @param {Number} seconds The length of time to pause (in seconds)
10666     * @return {Roo.Element} The Element
10667     */
10668     pause : function(seconds){
10669         var el = this.getFxEl();
10670         var o = {};
10671
10672         el.queueFx(o, function(){
10673             setTimeout(function(){
10674                 el.afterFx(o);
10675             }, seconds * 1000);
10676         });
10677         return this;
10678     },
10679
10680    /**
10681     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10682     * using the "endOpacity" config option.
10683     * Usage:
10684 <pre><code>
10685 // default: fade in from opacity 0 to 100%
10686 el.fadeIn();
10687
10688 // custom: fade in from opacity 0 to 75% over 2 seconds
10689 el.fadeIn({ endOpacity: .75, duration: 2});
10690
10691 // common config options shown with default values
10692 el.fadeIn({
10693     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10694     easing: 'easeOut',
10695     duration: .5
10696 });
10697 </code></pre>
10698     * @param {Object} options (optional) Object literal with any of the Fx config options
10699     * @return {Roo.Element} The Element
10700     */
10701     fadeIn : function(o){
10702         var el = this.getFxEl();
10703         o = o || {};
10704         el.queueFx(o, function(){
10705             this.setOpacity(0);
10706             this.fixDisplay();
10707             this.dom.style.visibility = 'visible';
10708             var to = o.endOpacity || 1;
10709             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10710                 o, null, .5, "easeOut", function(){
10711                 if(to == 1){
10712                     this.clearOpacity();
10713                 }
10714                 el.afterFx(o);
10715             });
10716         });
10717         return this;
10718     },
10719
10720    /**
10721     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10722     * using the "endOpacity" config option.
10723     * Usage:
10724 <pre><code>
10725 // default: fade out from the element's current opacity to 0
10726 el.fadeOut();
10727
10728 // custom: fade out from the element's current opacity to 25% over 2 seconds
10729 el.fadeOut({ endOpacity: .25, duration: 2});
10730
10731 // common config options shown with default values
10732 el.fadeOut({
10733     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10734     easing: 'easeOut',
10735     duration: .5
10736     remove: false,
10737     useDisplay: false
10738 });
10739 </code></pre>
10740     * @param {Object} options (optional) Object literal with any of the Fx config options
10741     * @return {Roo.Element} The Element
10742     */
10743     fadeOut : function(o){
10744         var el = this.getFxEl();
10745         o = o || {};
10746         el.queueFx(o, function(){
10747             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10748                 o, null, .5, "easeOut", function(){
10749                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10750                      this.dom.style.display = "none";
10751                 }else{
10752                      this.dom.style.visibility = "hidden";
10753                 }
10754                 this.clearOpacity();
10755                 el.afterFx(o);
10756             });
10757         });
10758         return this;
10759     },
10760
10761    /**
10762     * Animates the transition of an element's dimensions from a starting height/width
10763     * to an ending height/width.
10764     * Usage:
10765 <pre><code>
10766 // change height and width to 100x100 pixels
10767 el.scale(100, 100);
10768
10769 // common config options shown with default values.  The height and width will default to
10770 // the element's existing values if passed as null.
10771 el.scale(
10772     [element's width],
10773     [element's height], {
10774     easing: 'easeOut',
10775     duration: .35
10776 });
10777 </code></pre>
10778     * @param {Number} width  The new width (pass undefined to keep the original width)
10779     * @param {Number} height  The new height (pass undefined to keep the original height)
10780     * @param {Object} options (optional) Object literal with any of the Fx config options
10781     * @return {Roo.Element} The Element
10782     */
10783     scale : function(w, h, o){
10784         this.shift(Roo.apply({}, o, {
10785             width: w,
10786             height: h
10787         }));
10788         return this;
10789     },
10790
10791    /**
10792     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10793     * Any of these properties not specified in the config object will not be changed.  This effect 
10794     * requires that at least one new dimension, position or opacity setting must be passed in on
10795     * the config object in order for the function to have any effect.
10796     * Usage:
10797 <pre><code>
10798 // slide the element horizontally to x position 200 while changing the height and opacity
10799 el.shift({ x: 200, height: 50, opacity: .8 });
10800
10801 // common config options shown with default values.
10802 el.shift({
10803     width: [element's width],
10804     height: [element's height],
10805     x: [element's x position],
10806     y: [element's y position],
10807     opacity: [element's opacity],
10808     easing: 'easeOut',
10809     duration: .35
10810 });
10811 </code></pre>
10812     * @param {Object} options  Object literal with any of the Fx config options
10813     * @return {Roo.Element} The Element
10814     */
10815     shift : function(o){
10816         var el = this.getFxEl();
10817         o = o || {};
10818         el.queueFx(o, function(){
10819             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10820             if(w !== undefined){
10821                 a.width = {to: this.adjustWidth(w)};
10822             }
10823             if(h !== undefined){
10824                 a.height = {to: this.adjustHeight(h)};
10825             }
10826             if(x !== undefined || y !== undefined){
10827                 a.points = {to: [
10828                     x !== undefined ? x : this.getX(),
10829                     y !== undefined ? y : this.getY()
10830                 ]};
10831             }
10832             if(op !== undefined){
10833                 a.opacity = {to: op};
10834             }
10835             if(o.xy !== undefined){
10836                 a.points = {to: o.xy};
10837             }
10838             arguments.callee.anim = this.fxanim(a,
10839                 o, 'motion', .35, "easeOut", function(){
10840                 el.afterFx(o);
10841             });
10842         });
10843         return this;
10844     },
10845
10846         /**
10847          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10848          * ending point of the effect.
10849          * Usage:
10850          *<pre><code>
10851 // default: slide the element downward while fading out
10852 el.ghost();
10853
10854 // custom: slide the element out to the right with a 2-second duration
10855 el.ghost('r', { duration: 2 });
10856
10857 // common config options shown with default values
10858 el.ghost('b', {
10859     easing: 'easeOut',
10860     duration: .5
10861     remove: false,
10862     useDisplay: false
10863 });
10864 </code></pre>
10865          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10866          * @param {Object} options (optional) Object literal with any of the Fx config options
10867          * @return {Roo.Element} The Element
10868          */
10869     ghost : function(anchor, o){
10870         var el = this.getFxEl();
10871         o = o || {};
10872
10873         el.queueFx(o, function(){
10874             anchor = anchor || "b";
10875
10876             // restore values after effect
10877             var r = this.getFxRestore();
10878             var w = this.getWidth(),
10879                 h = this.getHeight();
10880
10881             var st = this.dom.style;
10882
10883             var after = function(){
10884                 if(o.useDisplay){
10885                     el.setDisplayed(false);
10886                 }else{
10887                     el.hide();
10888                 }
10889
10890                 el.clearOpacity();
10891                 el.setPositioning(r.pos);
10892                 st.width = r.width;
10893                 st.height = r.height;
10894
10895                 el.afterFx(o);
10896             };
10897
10898             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10899             switch(anchor.toLowerCase()){
10900                 case "t":
10901                     pt.by = [0, -h];
10902                 break;
10903                 case "l":
10904                     pt.by = [-w, 0];
10905                 break;
10906                 case "r":
10907                     pt.by = [w, 0];
10908                 break;
10909                 case "b":
10910                     pt.by = [0, h];
10911                 break;
10912                 case "tl":
10913                     pt.by = [-w, -h];
10914                 break;
10915                 case "bl":
10916                     pt.by = [-w, h];
10917                 break;
10918                 case "br":
10919                     pt.by = [w, h];
10920                 break;
10921                 case "tr":
10922                     pt.by = [w, -h];
10923                 break;
10924             }
10925
10926             arguments.callee.anim = this.fxanim(a,
10927                 o,
10928                 'motion',
10929                 .5,
10930                 "easeOut", after);
10931         });
10932         return this;
10933     },
10934
10935         /**
10936          * Ensures that all effects queued after syncFx is called on the element are
10937          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10938          * @return {Roo.Element} The Element
10939          */
10940     syncFx : function(){
10941         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10942             block : false,
10943             concurrent : true,
10944             stopFx : false
10945         });
10946         return this;
10947     },
10948
10949         /**
10950          * Ensures that all effects queued after sequenceFx is called on the element are
10951          * run in sequence.  This is the opposite of {@link #syncFx}.
10952          * @return {Roo.Element} The Element
10953          */
10954     sequenceFx : function(){
10955         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10956             block : false,
10957             concurrent : false,
10958             stopFx : false
10959         });
10960         return this;
10961     },
10962
10963         /* @private */
10964     nextFx : function(){
10965         var ef = this.fxQueue[0];
10966         if(ef){
10967             ef.call(this);
10968         }
10969     },
10970
10971         /**
10972          * Returns true if the element has any effects actively running or queued, else returns false.
10973          * @return {Boolean} True if element has active effects, else false
10974          */
10975     hasActiveFx : function(){
10976         return this.fxQueue && this.fxQueue[0];
10977     },
10978
10979         /**
10980          * Stops any running effects and clears the element's internal effects queue if it contains
10981          * any additional effects that haven't started yet.
10982          * @return {Roo.Element} The Element
10983          */
10984     stopFx : function(){
10985         if(this.hasActiveFx()){
10986             var cur = this.fxQueue[0];
10987             if(cur && cur.anim && cur.anim.isAnimated()){
10988                 this.fxQueue = [cur]; // clear out others
10989                 cur.anim.stop(true);
10990             }
10991         }
10992         return this;
10993     },
10994
10995         /* @private */
10996     beforeFx : function(o){
10997         if(this.hasActiveFx() && !o.concurrent){
10998            if(o.stopFx){
10999                this.stopFx();
11000                return true;
11001            }
11002            return false;
11003         }
11004         return true;
11005     },
11006
11007         /**
11008          * Returns true if the element is currently blocking so that no other effect can be queued
11009          * until this effect is finished, else returns false if blocking is not set.  This is commonly
11010          * used to ensure that an effect initiated by a user action runs to completion prior to the
11011          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
11012          * @return {Boolean} True if blocking, else false
11013          */
11014     hasFxBlock : function(){
11015         var q = this.fxQueue;
11016         return q && q[0] && q[0].block;
11017     },
11018
11019         /* @private */
11020     queueFx : function(o, fn){
11021         if(!this.fxQueue){
11022             this.fxQueue = [];
11023         }
11024         if(!this.hasFxBlock()){
11025             Roo.applyIf(o, this.fxDefaults);
11026             if(!o.concurrent){
11027                 var run = this.beforeFx(o);
11028                 fn.block = o.block;
11029                 this.fxQueue.push(fn);
11030                 if(run){
11031                     this.nextFx();
11032                 }
11033             }else{
11034                 fn.call(this);
11035             }
11036         }
11037         return this;
11038     },
11039
11040         /* @private */
11041     fxWrap : function(pos, o, vis){
11042         var wrap;
11043         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
11044             var wrapXY;
11045             if(o.fixPosition){
11046                 wrapXY = this.getXY();
11047             }
11048             var div = document.createElement("div");
11049             div.style.visibility = vis;
11050             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
11051             wrap.setPositioning(pos);
11052             if(wrap.getStyle("position") == "static"){
11053                 wrap.position("relative");
11054             }
11055             this.clearPositioning('auto');
11056             wrap.clip();
11057             wrap.dom.appendChild(this.dom);
11058             if(wrapXY){
11059                 wrap.setXY(wrapXY);
11060             }
11061         }
11062         return wrap;
11063     },
11064
11065         /* @private */
11066     fxUnwrap : function(wrap, pos, o){
11067         this.clearPositioning();
11068         this.setPositioning(pos);
11069         if(!o.wrap){
11070             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
11071             wrap.remove();
11072         }
11073     },
11074
11075         /* @private */
11076     getFxRestore : function(){
11077         var st = this.dom.style;
11078         return {pos: this.getPositioning(), width: st.width, height : st.height};
11079     },
11080
11081         /* @private */
11082     afterFx : function(o){
11083         if(o.afterStyle){
11084             this.applyStyles(o.afterStyle);
11085         }
11086         if(o.afterCls){
11087             this.addClass(o.afterCls);
11088         }
11089         if(o.remove === true){
11090             this.remove();
11091         }
11092         Roo.callback(o.callback, o.scope, [this]);
11093         if(!o.concurrent){
11094             this.fxQueue.shift();
11095             this.nextFx();
11096         }
11097     },
11098
11099         /* @private */
11100     getFxEl : function(){ // support for composite element fx
11101         return Roo.get(this.dom);
11102     },
11103
11104         /* @private */
11105     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
11106         animType = animType || 'run';
11107         opt = opt || {};
11108         var anim = Roo.lib.Anim[animType](
11109             this.dom, args,
11110             (opt.duration || defaultDur) || .35,
11111             (opt.easing || defaultEase) || 'easeOut',
11112             function(){
11113                 Roo.callback(cb, this);
11114             },
11115             this
11116         );
11117         opt.anim = anim;
11118         return anim;
11119     }
11120 };
11121
11122 // backwords compat
11123 Roo.Fx.resize = Roo.Fx.scale;
11124
11125 //When included, Roo.Fx is automatically applied to Element so that all basic
11126 //effects are available directly via the Element API
11127 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
11128  * Based on:
11129  * Ext JS Library 1.1.1
11130  * Copyright(c) 2006-2007, Ext JS, LLC.
11131  *
11132  * Originally Released Under LGPL - original licence link has changed is not relivant.
11133  *
11134  * Fork - LGPL
11135  * <script type="text/javascript">
11136  */
11137
11138
11139 /**
11140  * @class Roo.CompositeElement
11141  * Standard composite class. Creates a Roo.Element for every element in the collection.
11142  * <br><br>
11143  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11144  * actions will be performed on all the elements in this collection.</b>
11145  * <br><br>
11146  * All methods return <i>this</i> and can be chained.
11147  <pre><code>
11148  var els = Roo.select("#some-el div.some-class", true);
11149  // or select directly from an existing element
11150  var el = Roo.get('some-el');
11151  el.select('div.some-class', true);
11152
11153  els.setWidth(100); // all elements become 100 width
11154  els.hide(true); // all elements fade out and hide
11155  // or
11156  els.setWidth(100).hide(true);
11157  </code></pre>
11158  */
11159 Roo.CompositeElement = function(els){
11160     this.elements = [];
11161     this.addElements(els);
11162 };
11163 Roo.CompositeElement.prototype = {
11164     isComposite: true,
11165     addElements : function(els){
11166         if(!els) {
11167             return this;
11168         }
11169         if(typeof els == "string"){
11170             els = Roo.Element.selectorFunction(els);
11171         }
11172         var yels = this.elements;
11173         var index = yels.length-1;
11174         for(var i = 0, len = els.length; i < len; i++) {
11175                 yels[++index] = Roo.get(els[i]);
11176         }
11177         return this;
11178     },
11179
11180     /**
11181     * Clears this composite and adds the elements returned by the passed selector.
11182     * @param {String/Array} els A string CSS selector, an array of elements or an element
11183     * @return {CompositeElement} this
11184     */
11185     fill : function(els){
11186         this.elements = [];
11187         this.add(els);
11188         return this;
11189     },
11190
11191     /**
11192     * Filters this composite to only elements that match the passed selector.
11193     * @param {String} selector A string CSS selector
11194     * @param {Boolean} inverse return inverse filter (not matches)
11195     * @return {CompositeElement} this
11196     */
11197     filter : function(selector, inverse){
11198         var els = [];
11199         inverse = inverse || false;
11200         this.each(function(el){
11201             var match = inverse ? !el.is(selector) : el.is(selector);
11202             if(match){
11203                 els[els.length] = el.dom;
11204             }
11205         });
11206         this.fill(els);
11207         return this;
11208     },
11209
11210     invoke : function(fn, args){
11211         var els = this.elements;
11212         for(var i = 0, len = els.length; i < len; i++) {
11213                 Roo.Element.prototype[fn].apply(els[i], args);
11214         }
11215         return this;
11216     },
11217     /**
11218     * Adds elements to this composite.
11219     * @param {String/Array} els A string CSS selector, an array of elements or an element
11220     * @return {CompositeElement} this
11221     */
11222     add : function(els){
11223         if(typeof els == "string"){
11224             this.addElements(Roo.Element.selectorFunction(els));
11225         }else if(els.length !== undefined){
11226             this.addElements(els);
11227         }else{
11228             this.addElements([els]);
11229         }
11230         return this;
11231     },
11232     /**
11233     * Calls the passed function passing (el, this, index) for each element in this composite.
11234     * @param {Function} fn The function to call
11235     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11236     * @return {CompositeElement} this
11237     */
11238     each : function(fn, scope){
11239         var els = this.elements;
11240         for(var i = 0, len = els.length; i < len; i++){
11241             if(fn.call(scope || els[i], els[i], this, i) === false) {
11242                 break;
11243             }
11244         }
11245         return this;
11246     },
11247
11248     /**
11249      * Returns the Element object at the specified index
11250      * @param {Number} index
11251      * @return {Roo.Element}
11252      */
11253     item : function(index){
11254         return this.elements[index] || null;
11255     },
11256
11257     /**
11258      * Returns the first Element
11259      * @return {Roo.Element}
11260      */
11261     first : function(){
11262         return this.item(0);
11263     },
11264
11265     /**
11266      * Returns the last Element
11267      * @return {Roo.Element}
11268      */
11269     last : function(){
11270         return this.item(this.elements.length-1);
11271     },
11272
11273     /**
11274      * Returns the number of elements in this composite
11275      * @return Number
11276      */
11277     getCount : function(){
11278         return this.elements.length;
11279     },
11280
11281     /**
11282      * Returns true if this composite contains the passed element
11283      * @return Boolean
11284      */
11285     contains : function(el){
11286         return this.indexOf(el) !== -1;
11287     },
11288
11289     /**
11290      * Returns true if this composite contains the passed element
11291      * @return Boolean
11292      */
11293     indexOf : function(el){
11294         return this.elements.indexOf(Roo.get(el));
11295     },
11296
11297
11298     /**
11299     * Removes the specified element(s).
11300     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11301     * or an array of any of those.
11302     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11303     * @return {CompositeElement} this
11304     */
11305     removeElement : function(el, removeDom){
11306         if(el instanceof Array){
11307             for(var i = 0, len = el.length; i < len; i++){
11308                 this.removeElement(el[i]);
11309             }
11310             return this;
11311         }
11312         var index = typeof el == 'number' ? el : this.indexOf(el);
11313         if(index !== -1){
11314             if(removeDom){
11315                 var d = this.elements[index];
11316                 if(d.dom){
11317                     d.remove();
11318                 }else{
11319                     d.parentNode.removeChild(d);
11320                 }
11321             }
11322             this.elements.splice(index, 1);
11323         }
11324         return this;
11325     },
11326
11327     /**
11328     * Replaces the specified element with the passed element.
11329     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11330     * to replace.
11331     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11332     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11333     * @return {CompositeElement} this
11334     */
11335     replaceElement : function(el, replacement, domReplace){
11336         var index = typeof el == 'number' ? el : this.indexOf(el);
11337         if(index !== -1){
11338             if(domReplace){
11339                 this.elements[index].replaceWith(replacement);
11340             }else{
11341                 this.elements.splice(index, 1, Roo.get(replacement))
11342             }
11343         }
11344         return this;
11345     },
11346
11347     /**
11348      * Removes all elements.
11349      */
11350     clear : function(){
11351         this.elements = [];
11352     }
11353 };
11354 (function(){
11355     Roo.CompositeElement.createCall = function(proto, fnName){
11356         if(!proto[fnName]){
11357             proto[fnName] = function(){
11358                 return this.invoke(fnName, arguments);
11359             };
11360         }
11361     };
11362     for(var fnName in Roo.Element.prototype){
11363         if(typeof Roo.Element.prototype[fnName] == "function"){
11364             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11365         }
11366     };
11367 })();
11368 /*
11369  * Based on:
11370  * Ext JS Library 1.1.1
11371  * Copyright(c) 2006-2007, Ext JS, LLC.
11372  *
11373  * Originally Released Under LGPL - original licence link has changed is not relivant.
11374  *
11375  * Fork - LGPL
11376  * <script type="text/javascript">
11377  */
11378
11379 /**
11380  * @class Roo.CompositeElementLite
11381  * @extends Roo.CompositeElement
11382  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11383  <pre><code>
11384  var els = Roo.select("#some-el div.some-class");
11385  // or select directly from an existing element
11386  var el = Roo.get('some-el');
11387  el.select('div.some-class');
11388
11389  els.setWidth(100); // all elements become 100 width
11390  els.hide(true); // all elements fade out and hide
11391  // or
11392  els.setWidth(100).hide(true);
11393  </code></pre><br><br>
11394  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11395  * actions will be performed on all the elements in this collection.</b>
11396  */
11397 Roo.CompositeElementLite = function(els){
11398     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11399     this.el = new Roo.Element.Flyweight();
11400 };
11401 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11402     addElements : function(els){
11403         if(els){
11404             if(els instanceof Array){
11405                 this.elements = this.elements.concat(els);
11406             }else{
11407                 var yels = this.elements;
11408                 var index = yels.length-1;
11409                 for(var i = 0, len = els.length; i < len; i++) {
11410                     yels[++index] = els[i];
11411                 }
11412             }
11413         }
11414         return this;
11415     },
11416     invoke : function(fn, args){
11417         var els = this.elements;
11418         var el = this.el;
11419         for(var i = 0, len = els.length; i < len; i++) {
11420             el.dom = els[i];
11421                 Roo.Element.prototype[fn].apply(el, args);
11422         }
11423         return this;
11424     },
11425     /**
11426      * Returns a flyweight Element of the dom element object at the specified index
11427      * @param {Number} index
11428      * @return {Roo.Element}
11429      */
11430     item : function(index){
11431         if(!this.elements[index]){
11432             return null;
11433         }
11434         this.el.dom = this.elements[index];
11435         return this.el;
11436     },
11437
11438     // fixes scope with flyweight
11439     addListener : function(eventName, handler, scope, opt){
11440         var els = this.elements;
11441         for(var i = 0, len = els.length; i < len; i++) {
11442             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11443         }
11444         return this;
11445     },
11446
11447     /**
11448     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11449     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11450     * a reference to the dom node, use el.dom.</b>
11451     * @param {Function} fn The function to call
11452     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11453     * @return {CompositeElement} this
11454     */
11455     each : function(fn, scope){
11456         var els = this.elements;
11457         var el = this.el;
11458         for(var i = 0, len = els.length; i < len; i++){
11459             el.dom = els[i];
11460                 if(fn.call(scope || el, el, this, i) === false){
11461                 break;
11462             }
11463         }
11464         return this;
11465     },
11466
11467     indexOf : function(el){
11468         return this.elements.indexOf(Roo.getDom(el));
11469     },
11470
11471     replaceElement : function(el, replacement, domReplace){
11472         var index = typeof el == 'number' ? el : this.indexOf(el);
11473         if(index !== -1){
11474             replacement = Roo.getDom(replacement);
11475             if(domReplace){
11476                 var d = this.elements[index];
11477                 d.parentNode.insertBefore(replacement, d);
11478                 d.parentNode.removeChild(d);
11479             }
11480             this.elements.splice(index, 1, replacement);
11481         }
11482         return this;
11483     }
11484 });
11485 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11486
11487 /*
11488  * Based on:
11489  * Ext JS Library 1.1.1
11490  * Copyright(c) 2006-2007, Ext JS, LLC.
11491  *
11492  * Originally Released Under LGPL - original licence link has changed is not relivant.
11493  *
11494  * Fork - LGPL
11495  * <script type="text/javascript">
11496  */
11497
11498  
11499
11500 /**
11501  * @class Roo.data.Connection
11502  * @extends Roo.util.Observable
11503  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11504  * either to a configured URL, or to a URL specified at request time. 
11505  * 
11506  * Requests made by this class are asynchronous, and will return immediately. No data from
11507  * the server will be available to the statement immediately following the {@link #request} call.
11508  * To process returned data, use a callback in the request options object, or an event listener.
11509  * 
11510  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11511  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11512  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11513  * property and, if present, the IFRAME's XML document as the responseXML property.
11514  * 
11515  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11516  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11517  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11518  * standard DOM methods.
11519  * @constructor
11520  * @param {Object} config a configuration object.
11521  */
11522 Roo.data.Connection = function(config){
11523     Roo.apply(this, config);
11524     this.addEvents({
11525         /**
11526          * @event beforerequest
11527          * Fires before a network request is made to retrieve a data object.
11528          * @param {Connection} conn This Connection object.
11529          * @param {Object} options The options config object passed to the {@link #request} method.
11530          */
11531         "beforerequest" : true,
11532         /**
11533          * @event requestcomplete
11534          * Fires if the request was successfully completed.
11535          * @param {Connection} conn This Connection object.
11536          * @param {Object} response The XHR object containing the response data.
11537          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11538          * @param {Object} options The options config object passed to the {@link #request} method.
11539          */
11540         "requestcomplete" : true,
11541         /**
11542          * @event requestexception
11543          * Fires if an error HTTP status was returned from the server.
11544          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11545          * @param {Connection} conn This Connection object.
11546          * @param {Object} response The XHR object containing the response data.
11547          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11548          * @param {Object} options The options config object passed to the {@link #request} method.
11549          */
11550         "requestexception" : true
11551     });
11552     Roo.data.Connection.superclass.constructor.call(this);
11553 };
11554
11555 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11556     /**
11557      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11558      */
11559     /**
11560      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11561      * extra parameters to each request made by this object. (defaults to undefined)
11562      */
11563     /**
11564      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11565      *  to each request made by this object. (defaults to undefined)
11566      */
11567     /**
11568      * @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)
11569      */
11570     /**
11571      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11572      */
11573     timeout : 30000,
11574     /**
11575      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11576      * @type Boolean
11577      */
11578     autoAbort:false,
11579
11580     /**
11581      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11582      * @type Boolean
11583      */
11584     disableCaching: true,
11585
11586     /**
11587      * Sends an HTTP request to a remote server.
11588      * @param {Object} options An object which may contain the following properties:<ul>
11589      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11590      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11591      * request, a url encoded string or a function to call to get either.</li>
11592      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11593      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11594      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11595      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11596      * <li>options {Object} The parameter to the request call.</li>
11597      * <li>success {Boolean} True if the request succeeded.</li>
11598      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11599      * </ul></li>
11600      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11601      * The callback is passed the following parameters:<ul>
11602      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11603      * <li>options {Object} The parameter to the request call.</li>
11604      * </ul></li>
11605      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11606      * The callback is passed the following parameters:<ul>
11607      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11608      * <li>options {Object} The parameter to the request call.</li>
11609      * </ul></li>
11610      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11611      * for the callback function. Defaults to the browser window.</li>
11612      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11613      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11614      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11615      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11616      * params for the post data. Any params will be appended to the URL.</li>
11617      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11618      * </ul>
11619      * @return {Number} transactionId
11620      */
11621     request : function(o){
11622         if(this.fireEvent("beforerequest", this, o) !== false){
11623             var p = o.params;
11624
11625             if(typeof p == "function"){
11626                 p = p.call(o.scope||window, o);
11627             }
11628             if(typeof p == "object"){
11629                 p = Roo.urlEncode(o.params);
11630             }
11631             if(this.extraParams){
11632                 var extras = Roo.urlEncode(this.extraParams);
11633                 p = p ? (p + '&' + extras) : extras;
11634             }
11635
11636             var url = o.url || this.url;
11637             if(typeof url == 'function'){
11638                 url = url.call(o.scope||window, o);
11639             }
11640
11641             if(o.form){
11642                 var form = Roo.getDom(o.form);
11643                 url = url || form.action;
11644
11645                 var enctype = form.getAttribute("enctype");
11646                 
11647                 if (o.formData) {
11648                     return this.doFormDataUpload(o, url);
11649                 }
11650                 
11651                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11652                     return this.doFormUpload(o, p, url);
11653                 }
11654                 var f = Roo.lib.Ajax.serializeForm(form);
11655                 p = p ? (p + '&' + f) : f;
11656             }
11657             
11658             if (!o.form && o.formData) {
11659                 o.formData = o.formData === true ? new FormData() : o.formData;
11660                 for (var k in o.params) {
11661                     o.formData.append(k,o.params[k]);
11662                 }
11663                     
11664                 return this.doFormDataUpload(o, url);
11665             }
11666             
11667
11668             var hs = o.headers;
11669             if(this.defaultHeaders){
11670                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11671                 if(!o.headers){
11672                     o.headers = hs;
11673                 }
11674             }
11675
11676             var cb = {
11677                 success: this.handleResponse,
11678                 failure: this.handleFailure,
11679                 scope: this,
11680                 argument: {options: o},
11681                 timeout : o.timeout || this.timeout
11682             };
11683
11684             var method = o.method||this.method||(p ? "POST" : "GET");
11685
11686             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11687                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11688             }
11689
11690             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11691                 if(o.autoAbort){
11692                     this.abort();
11693                 }
11694             }else if(this.autoAbort !== false){
11695                 this.abort();
11696             }
11697
11698             if((method == 'GET' && p) || o.xmlData){
11699                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11700                 p = '';
11701             }
11702             Roo.lib.Ajax.useDefaultHeader = typeof(o.headers) == 'undefined' || typeof(o.headers['Content-Type']) == 'undefined';
11703             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11704             Roo.lib.Ajax.useDefaultHeader == true;
11705             return this.transId;
11706         }else{
11707             Roo.callback(o.callback, o.scope, [o, null, null]);
11708             return null;
11709         }
11710     },
11711
11712     /**
11713      * Determine whether this object has a request outstanding.
11714      * @param {Number} transactionId (Optional) defaults to the last transaction
11715      * @return {Boolean} True if there is an outstanding request.
11716      */
11717     isLoading : function(transId){
11718         if(transId){
11719             return Roo.lib.Ajax.isCallInProgress(transId);
11720         }else{
11721             return this.transId ? true : false;
11722         }
11723     },
11724
11725     /**
11726      * Aborts any outstanding request.
11727      * @param {Number} transactionId (Optional) defaults to the last transaction
11728      */
11729     abort : function(transId){
11730         if(transId || this.isLoading()){
11731             Roo.lib.Ajax.abort(transId || this.transId);
11732         }
11733     },
11734
11735     // private
11736     handleResponse : function(response){
11737         this.transId = false;
11738         var options = response.argument.options;
11739         response.argument = options ? options.argument : null;
11740         this.fireEvent("requestcomplete", this, response, options);
11741         Roo.callback(options.success, options.scope, [response, options]);
11742         Roo.callback(options.callback, options.scope, [options, true, response]);
11743     },
11744
11745     // private
11746     handleFailure : function(response, e){
11747         this.transId = false;
11748         var options = response.argument.options;
11749         response.argument = options ? options.argument : null;
11750         this.fireEvent("requestexception", this, response, options, e);
11751         Roo.callback(options.failure, options.scope, [response, options]);
11752         Roo.callback(options.callback, options.scope, [options, false, response]);
11753     },
11754
11755     // private
11756     doFormUpload : function(o, ps, url){
11757         var id = Roo.id();
11758         var frame = document.createElement('iframe');
11759         frame.id = id;
11760         frame.name = id;
11761         frame.className = 'x-hidden';
11762         if(Roo.isIE){
11763             frame.src = Roo.SSL_SECURE_URL;
11764         }
11765         document.body.appendChild(frame);
11766
11767         if(Roo.isIE){
11768            document.frames[id].name = id;
11769         }
11770
11771         var form = Roo.getDom(o.form);
11772         form.target = id;
11773         form.method = 'POST';
11774         form.enctype = form.encoding = 'multipart/form-data';
11775         if(url){
11776             form.action = url;
11777         }
11778
11779         var hiddens, hd;
11780         if(ps){ // add dynamic params
11781             hiddens = [];
11782             ps = Roo.urlDecode(ps, false);
11783             for(var k in ps){
11784                 if(ps.hasOwnProperty(k)){
11785                     hd = document.createElement('input');
11786                     hd.type = 'hidden';
11787                     hd.name = k;
11788                     hd.value = ps[k];
11789                     form.appendChild(hd);
11790                     hiddens.push(hd);
11791                 }
11792             }
11793         }
11794
11795         function cb(){
11796             var r = {  // bogus response object
11797                 responseText : '',
11798                 responseXML : null
11799             };
11800
11801             r.argument = o ? o.argument : null;
11802
11803             try { //
11804                 var doc;
11805                 if(Roo.isIE){
11806                     doc = frame.contentWindow.document;
11807                 }else {
11808                     doc = (frame.contentDocument || window.frames[id].document);
11809                 }
11810                 if(doc && doc.body){
11811                     r.responseText = doc.body.innerHTML;
11812                 }
11813                 if(doc && doc.XMLDocument){
11814                     r.responseXML = doc.XMLDocument;
11815                 }else {
11816                     r.responseXML = doc;
11817                 }
11818             }
11819             catch(e) {
11820                 // ignore
11821             }
11822
11823             Roo.EventManager.removeListener(frame, 'load', cb, this);
11824
11825             this.fireEvent("requestcomplete", this, r, o);
11826             Roo.callback(o.success, o.scope, [r, o]);
11827             Roo.callback(o.callback, o.scope, [o, true, r]);
11828
11829             setTimeout(function(){document.body.removeChild(frame);}, 100);
11830         }
11831
11832         Roo.EventManager.on(frame, 'load', cb, this);
11833         form.submit();
11834
11835         if(hiddens){ // remove dynamic params
11836             for(var i = 0, len = hiddens.length; i < len; i++){
11837                 form.removeChild(hiddens[i]);
11838             }
11839         }
11840     },
11841     // this is a 'formdata version???'
11842     
11843     
11844     doFormDataUpload : function(o,  url)
11845     {
11846         var formData;
11847         if (o.form) {
11848             var form =  Roo.getDom(o.form);
11849             form.enctype = form.encoding = 'multipart/form-data';
11850             formData = o.formData === true ? new FormData(form) : o.formData;
11851         } else {
11852             formData = o.formData === true ? new FormData() : o.formData;
11853         }
11854         
11855       
11856         var cb = {
11857             success: this.handleResponse,
11858             failure: this.handleFailure,
11859             scope: this,
11860             argument: {options: o},
11861             timeout : o.timeout || this.timeout
11862         };
11863  
11864         if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11865             if(o.autoAbort){
11866                 this.abort();
11867             }
11868         }else if(this.autoAbort !== false){
11869             this.abort();
11870         }
11871
11872         //Roo.lib.Ajax.defaultPostHeader = null;
11873         Roo.lib.Ajax.useDefaultHeader = false;
11874         this.transId = Roo.lib.Ajax.request( "POST", url, cb,  formData, o);
11875         Roo.lib.Ajax.useDefaultHeader = true;
11876  
11877          
11878     }
11879     
11880 });
11881 /*
11882  * Based on:
11883  * Ext JS Library 1.1.1
11884  * Copyright(c) 2006-2007, Ext JS, LLC.
11885  *
11886  * Originally Released Under LGPL - original licence link has changed is not relivant.
11887  *
11888  * Fork - LGPL
11889  * <script type="text/javascript">
11890  */
11891  
11892 /**
11893  * Global Ajax request class.
11894  * 
11895  * @class Roo.Ajax
11896  * @extends Roo.data.Connection
11897  * @static
11898  * 
11899  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11900  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11901  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11902  * @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)
11903  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11904  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11905  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11906  */
11907 Roo.Ajax = new Roo.data.Connection({
11908     // fix up the docs
11909     /**
11910      * @scope Roo.Ajax
11911      * @type {Boolear} 
11912      */
11913     autoAbort : false,
11914
11915     /**
11916      * Serialize the passed form into a url encoded string
11917      * @scope Roo.Ajax
11918      * @param {String/HTMLElement} form
11919      * @return {String}
11920      */
11921     serializeForm : function(form){
11922         return Roo.lib.Ajax.serializeForm(form);
11923     }
11924 });/*
11925  * Based on:
11926  * Ext JS Library 1.1.1
11927  * Copyright(c) 2006-2007, Ext JS, LLC.
11928  *
11929  * Originally Released Under LGPL - original licence link has changed is not relivant.
11930  *
11931  * Fork - LGPL
11932  * <script type="text/javascript">
11933  */
11934
11935  
11936 /**
11937  * @class Roo.UpdateManager
11938  * @extends Roo.util.Observable
11939  * Provides AJAX-style update for Element object.<br><br>
11940  * Usage:<br>
11941  * <pre><code>
11942  * // Get it from a Roo.Element object
11943  * var el = Roo.get("foo");
11944  * var mgr = el.getUpdateManager();
11945  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11946  * ...
11947  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11948  * <br>
11949  * // or directly (returns the same UpdateManager instance)
11950  * var mgr = new Roo.UpdateManager("myElementId");
11951  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11952  * mgr.on("update", myFcnNeedsToKnow);
11953  * <br>
11954    // short handed call directly from the element object
11955    Roo.get("foo").load({
11956         url: "bar.php",
11957         scripts:true,
11958         params: "for=bar",
11959         text: "Loading Foo..."
11960    });
11961  * </code></pre>
11962  * @constructor
11963  * Create new UpdateManager directly.
11964  * @param {String/HTMLElement/Roo.Element} el The element to update
11965  * @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).
11966  */
11967 Roo.UpdateManager = function(el, forceNew){
11968     el = Roo.get(el);
11969     if(!forceNew && el.updateManager){
11970         return el.updateManager;
11971     }
11972     /**
11973      * The Element object
11974      * @type Roo.Element
11975      */
11976     this.el = el;
11977     /**
11978      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11979      * @type String
11980      */
11981     this.defaultUrl = null;
11982
11983     this.addEvents({
11984         /**
11985          * @event beforeupdate
11986          * Fired before an update is made, return false from your handler and the update is cancelled.
11987          * @param {Roo.Element} el
11988          * @param {String/Object/Function} url
11989          * @param {String/Object} params
11990          */
11991         "beforeupdate": true,
11992         /**
11993          * @event update
11994          * Fired after successful update is made.
11995          * @param {Roo.Element} el
11996          * @param {Object} oResponseObject The response Object
11997          */
11998         "update": true,
11999         /**
12000          * @event failure
12001          * Fired on update failure.
12002          * @param {Roo.Element} el
12003          * @param {Object} oResponseObject The response Object
12004          */
12005         "failure": true
12006     });
12007     var d = Roo.UpdateManager.defaults;
12008     /**
12009      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
12010      * @type String
12011      */
12012     this.sslBlankUrl = d.sslBlankUrl;
12013     /**
12014      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
12015      * @type Boolean
12016      */
12017     this.disableCaching = d.disableCaching;
12018     /**
12019      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12020      * @type String
12021      */
12022     this.indicatorText = d.indicatorText;
12023     /**
12024      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
12025      * @type String
12026      */
12027     this.showLoadIndicator = d.showLoadIndicator;
12028     /**
12029      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
12030      * @type Number
12031      */
12032     this.timeout = d.timeout;
12033
12034     /**
12035      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
12036      * @type Boolean
12037      */
12038     this.loadScripts = d.loadScripts;
12039
12040     /**
12041      * Transaction object of current executing transaction
12042      */
12043     this.transaction = null;
12044
12045     /**
12046      * @private
12047      */
12048     this.autoRefreshProcId = null;
12049     /**
12050      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
12051      * @type Function
12052      */
12053     this.refreshDelegate = this.refresh.createDelegate(this);
12054     /**
12055      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
12056      * @type Function
12057      */
12058     this.updateDelegate = this.update.createDelegate(this);
12059     /**
12060      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
12061      * @type Function
12062      */
12063     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
12064     /**
12065      * @private
12066      */
12067     this.successDelegate = this.processSuccess.createDelegate(this);
12068     /**
12069      * @private
12070      */
12071     this.failureDelegate = this.processFailure.createDelegate(this);
12072
12073     if(!this.renderer){
12074      /**
12075       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
12076       */
12077     this.renderer = new Roo.UpdateManager.BasicRenderer();
12078     }
12079     
12080     Roo.UpdateManager.superclass.constructor.call(this);
12081 };
12082
12083 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
12084     /**
12085      * Get the Element this UpdateManager is bound to
12086      * @return {Roo.Element} The element
12087      */
12088     getEl : function(){
12089         return this.el;
12090     },
12091     /**
12092      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
12093      * @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:
12094 <pre><code>
12095 um.update({<br/>
12096     url: "your-url.php",<br/>
12097     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
12098     callback: yourFunction,<br/>
12099     scope: yourObject, //(optional scope)  <br/>
12100     discardUrl: false, <br/>
12101     nocache: false,<br/>
12102     text: "Loading...",<br/>
12103     timeout: 30,<br/>
12104     scripts: false<br/>
12105 });
12106 </code></pre>
12107      * The only required property is url. The optional properties nocache, text and scripts
12108      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
12109      * @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}
12110      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12111      * @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.
12112      */
12113     update : function(url, params, callback, discardUrl){
12114         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
12115             var method = this.method,
12116                 cfg;
12117             if(typeof url == "object"){ // must be config object
12118                 cfg = url;
12119                 url = cfg.url;
12120                 params = params || cfg.params;
12121                 callback = callback || cfg.callback;
12122                 discardUrl = discardUrl || cfg.discardUrl;
12123                 if(callback && cfg.scope){
12124                     callback = callback.createDelegate(cfg.scope);
12125                 }
12126                 if(typeof cfg.method != "undefined"){method = cfg.method;};
12127                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
12128                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
12129                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
12130                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
12131             }
12132             this.showLoading();
12133             if(!discardUrl){
12134                 this.defaultUrl = url;
12135             }
12136             if(typeof url == "function"){
12137                 url = url.call(this);
12138             }
12139
12140             method = method || (params ? "POST" : "GET");
12141             if(method == "GET"){
12142                 url = this.prepareUrl(url);
12143             }
12144
12145             var o = Roo.apply(cfg ||{}, {
12146                 url : url,
12147                 params: params,
12148                 success: this.successDelegate,
12149                 failure: this.failureDelegate,
12150                 callback: undefined,
12151                 timeout: (this.timeout*1000),
12152                 argument: {"url": url, "form": null, "callback": callback, "params": params}
12153             });
12154             Roo.log("updated manager called with timeout of " + o.timeout);
12155             this.transaction = Roo.Ajax.request(o);
12156         }
12157     },
12158
12159     /**
12160      * 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.
12161      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
12162      * @param {String/HTMLElement} form The form Id or form element
12163      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
12164      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
12165      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12166      */
12167     formUpdate : function(form, url, reset, callback){
12168         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
12169             if(typeof url == "function"){
12170                 url = url.call(this);
12171             }
12172             form = Roo.getDom(form);
12173             this.transaction = Roo.Ajax.request({
12174                 form: form,
12175                 url:url,
12176                 success: this.successDelegate,
12177                 failure: this.failureDelegate,
12178                 timeout: (this.timeout*1000),
12179                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
12180             });
12181             this.showLoading.defer(1, this);
12182         }
12183     },
12184
12185     /**
12186      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
12187      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12188      */
12189     refresh : function(callback){
12190         if(this.defaultUrl == null){
12191             return;
12192         }
12193         this.update(this.defaultUrl, null, callback, true);
12194     },
12195
12196     /**
12197      * Set this element to auto refresh.
12198      * @param {Number} interval How often to update (in seconds).
12199      * @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)
12200      * @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}
12201      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12202      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
12203      */
12204     startAutoRefresh : function(interval, url, params, callback, refreshNow){
12205         if(refreshNow){
12206             this.update(url || this.defaultUrl, params, callback, true);
12207         }
12208         if(this.autoRefreshProcId){
12209             clearInterval(this.autoRefreshProcId);
12210         }
12211         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
12212     },
12213
12214     /**
12215      * Stop auto refresh on this element.
12216      */
12217      stopAutoRefresh : function(){
12218         if(this.autoRefreshProcId){
12219             clearInterval(this.autoRefreshProcId);
12220             delete this.autoRefreshProcId;
12221         }
12222     },
12223
12224     isAutoRefreshing : function(){
12225        return this.autoRefreshProcId ? true : false;
12226     },
12227     /**
12228      * Called to update the element to "Loading" state. Override to perform custom action.
12229      */
12230     showLoading : function(){
12231         if(this.showLoadIndicator){
12232             this.el.update(this.indicatorText);
12233         }
12234     },
12235
12236     /**
12237      * Adds unique parameter to query string if disableCaching = true
12238      * @private
12239      */
12240     prepareUrl : function(url){
12241         if(this.disableCaching){
12242             var append = "_dc=" + (new Date().getTime());
12243             if(url.indexOf("?") !== -1){
12244                 url += "&" + append;
12245             }else{
12246                 url += "?" + append;
12247             }
12248         }
12249         return url;
12250     },
12251
12252     /**
12253      * @private
12254      */
12255     processSuccess : function(response){
12256         this.transaction = null;
12257         if(response.argument.form && response.argument.reset){
12258             try{ // put in try/catch since some older FF releases had problems with this
12259                 response.argument.form.reset();
12260             }catch(e){}
12261         }
12262         if(this.loadScripts){
12263             this.renderer.render(this.el, response, this,
12264                 this.updateComplete.createDelegate(this, [response]));
12265         }else{
12266             this.renderer.render(this.el, response, this);
12267             this.updateComplete(response);
12268         }
12269     },
12270
12271     updateComplete : function(response){
12272         this.fireEvent("update", this.el, response);
12273         if(typeof response.argument.callback == "function"){
12274             response.argument.callback(this.el, true, response);
12275         }
12276     },
12277
12278     /**
12279      * @private
12280      */
12281     processFailure : function(response){
12282         this.transaction = null;
12283         this.fireEvent("failure", this.el, response);
12284         if(typeof response.argument.callback == "function"){
12285             response.argument.callback(this.el, false, response);
12286         }
12287     },
12288
12289     /**
12290      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12291      * @param {Object} renderer The object implementing the render() method
12292      */
12293     setRenderer : function(renderer){
12294         this.renderer = renderer;
12295     },
12296
12297     getRenderer : function(){
12298        return this.renderer;
12299     },
12300
12301     /**
12302      * Set the defaultUrl used for updates
12303      * @param {String/Function} defaultUrl The url or a function to call to get the url
12304      */
12305     setDefaultUrl : function(defaultUrl){
12306         this.defaultUrl = defaultUrl;
12307     },
12308
12309     /**
12310      * Aborts the executing transaction
12311      */
12312     abort : function(){
12313         if(this.transaction){
12314             Roo.Ajax.abort(this.transaction);
12315         }
12316     },
12317
12318     /**
12319      * Returns true if an update is in progress
12320      * @return {Boolean}
12321      */
12322     isUpdating : function(){
12323         if(this.transaction){
12324             return Roo.Ajax.isLoading(this.transaction);
12325         }
12326         return false;
12327     }
12328 });
12329
12330 /**
12331  * @class Roo.UpdateManager.defaults
12332  * @static (not really - but it helps the doc tool)
12333  * The defaults collection enables customizing the default properties of UpdateManager
12334  */
12335    Roo.UpdateManager.defaults = {
12336        /**
12337          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12338          * @type Number
12339          */
12340          timeout : 30,
12341
12342          /**
12343          * True to process scripts by default (Defaults to false).
12344          * @type Boolean
12345          */
12346         loadScripts : false,
12347
12348         /**
12349         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12350         * @type String
12351         */
12352         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12353         /**
12354          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12355          * @type Boolean
12356          */
12357         disableCaching : false,
12358         /**
12359          * Whether to show indicatorText when loading (Defaults to true).
12360          * @type Boolean
12361          */
12362         showLoadIndicator : true,
12363         /**
12364          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12365          * @type String
12366          */
12367         indicatorText : '<div class="loading-indicator">Loading...</div>'
12368    };
12369
12370 /**
12371  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12372  *Usage:
12373  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12374  * @param {String/HTMLElement/Roo.Element} el The element to update
12375  * @param {String} url The url
12376  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12377  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12378  * @static
12379  * @deprecated
12380  * @member Roo.UpdateManager
12381  */
12382 Roo.UpdateManager.updateElement = function(el, url, params, options){
12383     var um = Roo.get(el, true).getUpdateManager();
12384     Roo.apply(um, options);
12385     um.update(url, params, options ? options.callback : null);
12386 };
12387 // alias for backwards compat
12388 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12389 /**
12390  * @class Roo.UpdateManager.BasicRenderer
12391  * Default Content renderer. Updates the elements innerHTML with the responseText.
12392  */
12393 Roo.UpdateManager.BasicRenderer = function(){};
12394
12395 Roo.UpdateManager.BasicRenderer.prototype = {
12396     /**
12397      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12398      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12399      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12400      * @param {Roo.Element} el The element being rendered
12401      * @param {Object} response The YUI Connect response object
12402      * @param {UpdateManager} updateManager The calling update manager
12403      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12404      */
12405      render : function(el, response, updateManager, callback){
12406         el.update(response.responseText, updateManager.loadScripts, callback);
12407     }
12408 };
12409 /*
12410  * Based on:
12411  * Roo JS
12412  * (c)) Alan Knowles
12413  * Licence : LGPL
12414  */
12415
12416
12417 /**
12418  * @class Roo.DomTemplate
12419  * @extends Roo.Template
12420  * An effort at a dom based template engine..
12421  *
12422  * Similar to XTemplate, except it uses dom parsing to create the template..
12423  *
12424  * Supported features:
12425  *
12426  *  Tags:
12427
12428 <pre><code>
12429       {a_variable} - output encoded.
12430       {a_variable.format:("Y-m-d")} - call a method on the variable
12431       {a_variable:raw} - unencoded output
12432       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12433       {a_variable:this.method_on_template(...)} - call a method on the template object.
12434  
12435 </code></pre>
12436  *  The tpl tag:
12437 <pre><code>
12438         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12439         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12440         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12441         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12442   
12443 </code></pre>
12444  *      
12445  */
12446 Roo.DomTemplate = function()
12447 {
12448      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12449      if (this.html) {
12450         this.compile();
12451      }
12452 };
12453
12454
12455 Roo.extend(Roo.DomTemplate, Roo.Template, {
12456     /**
12457      * id counter for sub templates.
12458      */
12459     id : 0,
12460     /**
12461      * flag to indicate if dom parser is inside a pre,
12462      * it will strip whitespace if not.
12463      */
12464     inPre : false,
12465     
12466     /**
12467      * The various sub templates
12468      */
12469     tpls : false,
12470     
12471     
12472     
12473     /**
12474      *
12475      * basic tag replacing syntax
12476      * WORD:WORD()
12477      *
12478      * // you can fake an object call by doing this
12479      *  x.t:(test,tesT) 
12480      * 
12481      */
12482     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12483     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12484     
12485     iterChild : function (node, method) {
12486         
12487         var oldPre = this.inPre;
12488         if (node.tagName == 'PRE') {
12489             this.inPre = true;
12490         }
12491         for( var i = 0; i < node.childNodes.length; i++) {
12492             method.call(this, node.childNodes[i]);
12493         }
12494         this.inPre = oldPre;
12495     },
12496     
12497     
12498     
12499     /**
12500      * compile the template
12501      *
12502      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12503      *
12504      */
12505     compile: function()
12506     {
12507         var s = this.html;
12508         
12509         // covert the html into DOM...
12510         var doc = false;
12511         var div =false;
12512         try {
12513             doc = document.implementation.createHTMLDocument("");
12514             doc.documentElement.innerHTML =   this.html  ;
12515             div = doc.documentElement;
12516         } catch (e) {
12517             // old IE... - nasty -- it causes all sorts of issues.. with
12518             // images getting pulled from server..
12519             div = document.createElement('div');
12520             div.innerHTML = this.html;
12521         }
12522         //doc.documentElement.innerHTML = htmlBody
12523          
12524         
12525         
12526         this.tpls = [];
12527         var _t = this;
12528         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12529         
12530         var tpls = this.tpls;
12531         
12532         // create a top level template from the snippet..
12533         
12534         //Roo.log(div.innerHTML);
12535         
12536         var tpl = {
12537             uid : 'master',
12538             id : this.id++,
12539             attr : false,
12540             value : false,
12541             body : div.innerHTML,
12542             
12543             forCall : false,
12544             execCall : false,
12545             dom : div,
12546             isTop : true
12547             
12548         };
12549         tpls.unshift(tpl);
12550         
12551         
12552         // compile them...
12553         this.tpls = [];
12554         Roo.each(tpls, function(tp){
12555             this.compileTpl(tp);
12556             this.tpls[tp.id] = tp;
12557         }, this);
12558         
12559         this.master = tpls[0];
12560         return this;
12561         
12562         
12563     },
12564     
12565     compileNode : function(node, istop) {
12566         // test for
12567         //Roo.log(node);
12568         
12569         
12570         // skip anything not a tag..
12571         if (node.nodeType != 1) {
12572             if (node.nodeType == 3 && !this.inPre) {
12573                 // reduce white space..
12574                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12575                 
12576             }
12577             return;
12578         }
12579         
12580         var tpl = {
12581             uid : false,
12582             id : false,
12583             attr : false,
12584             value : false,
12585             body : '',
12586             
12587             forCall : false,
12588             execCall : false,
12589             dom : false,
12590             isTop : istop
12591             
12592             
12593         };
12594         
12595         
12596         switch(true) {
12597             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12598             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12599             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12600             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12601             // no default..
12602         }
12603         
12604         
12605         if (!tpl.attr) {
12606             // just itterate children..
12607             this.iterChild(node,this.compileNode);
12608             return;
12609         }
12610         tpl.uid = this.id++;
12611         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12612         node.removeAttribute('roo-'+ tpl.attr);
12613         if (tpl.attr != 'name') {
12614             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12615             node.parentNode.replaceChild(placeholder,  node);
12616         } else {
12617             
12618             var placeholder =  document.createElement('span');
12619             placeholder.className = 'roo-tpl-' + tpl.value;
12620             node.parentNode.replaceChild(placeholder,  node);
12621         }
12622         
12623         // parent now sees '{domtplXXXX}
12624         this.iterChild(node,this.compileNode);
12625         
12626         // we should now have node body...
12627         var div = document.createElement('div');
12628         div.appendChild(node);
12629         tpl.dom = node;
12630         // this has the unfortunate side effect of converting tagged attributes
12631         // eg. href="{...}" into %7C...%7D
12632         // this has been fixed by searching for those combo's although it's a bit hacky..
12633         
12634         
12635         tpl.body = div.innerHTML;
12636         
12637         
12638          
12639         tpl.id = tpl.uid;
12640         switch(tpl.attr) {
12641             case 'for' :
12642                 switch (tpl.value) {
12643                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12644                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12645                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12646                 }
12647                 break;
12648             
12649             case 'exec':
12650                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12651                 break;
12652             
12653             case 'if':     
12654                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12655                 break;
12656             
12657             case 'name':
12658                 tpl.id  = tpl.value; // replace non characters???
12659                 break;
12660             
12661         }
12662         
12663         
12664         this.tpls.push(tpl);
12665         
12666         
12667         
12668     },
12669     
12670     
12671     
12672     
12673     /**
12674      * Compile a segment of the template into a 'sub-template'
12675      *
12676      * 
12677      * 
12678      *
12679      */
12680     compileTpl : function(tpl)
12681     {
12682         var fm = Roo.util.Format;
12683         var useF = this.disableFormats !== true;
12684         
12685         var sep = Roo.isGecko ? "+\n" : ",\n";
12686         
12687         var undef = function(str) {
12688             Roo.debug && Roo.log("Property not found :"  + str);
12689             return '';
12690         };
12691           
12692         //Roo.log(tpl.body);
12693         
12694         
12695         
12696         var fn = function(m, lbrace, name, format, args)
12697         {
12698             //Roo.log("ARGS");
12699             //Roo.log(arguments);
12700             args = args ? args.replace(/\\'/g,"'") : args;
12701             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12702             if (typeof(format) == 'undefined') {
12703                 format =  'htmlEncode'; 
12704             }
12705             if (format == 'raw' ) {
12706                 format = false;
12707             }
12708             
12709             if(name.substr(0, 6) == 'domtpl'){
12710                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12711             }
12712             
12713             // build an array of options to determine if value is undefined..
12714             
12715             // basically get 'xxxx.yyyy' then do
12716             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12717             //    (function () { Roo.log("Property not found"); return ''; })() :
12718             //    ......
12719             
12720             var udef_ar = [];
12721             var lookfor = '';
12722             Roo.each(name.split('.'), function(st) {
12723                 lookfor += (lookfor.length ? '.': '') + st;
12724                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12725             });
12726             
12727             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12728             
12729             
12730             if(format && useF){
12731                 
12732                 args = args ? ',' + args : "";
12733                  
12734                 if(format.substr(0, 5) != "this."){
12735                     format = "fm." + format + '(';
12736                 }else{
12737                     format = 'this.call("'+ format.substr(5) + '", ';
12738                     args = ", values";
12739                 }
12740                 
12741                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12742             }
12743              
12744             if (args && args.length) {
12745                 // called with xxyx.yuu:(test,test)
12746                 // change to ()
12747                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12748             }
12749             // raw.. - :raw modifier..
12750             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12751             
12752         };
12753         var body;
12754         // branched to use + in gecko and [].join() in others
12755         if(Roo.isGecko){
12756             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12757                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12758                     "';};};";
12759         }else{
12760             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12761             body.push(tpl.body.replace(/(\r\n|\n)/g,
12762                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12763             body.push("'].join('');};};");
12764             body = body.join('');
12765         }
12766         
12767         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12768        
12769         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12770         eval(body);
12771         
12772         return this;
12773     },
12774      
12775     /**
12776      * same as applyTemplate, except it's done to one of the subTemplates
12777      * when using named templates, you can do:
12778      *
12779      * var str = pl.applySubTemplate('your-name', values);
12780      *
12781      * 
12782      * @param {Number} id of the template
12783      * @param {Object} values to apply to template
12784      * @param {Object} parent (normaly the instance of this object)
12785      */
12786     applySubTemplate : function(id, values, parent)
12787     {
12788         
12789         
12790         var t = this.tpls[id];
12791         
12792         
12793         try { 
12794             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12795                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12796                 return '';
12797             }
12798         } catch(e) {
12799             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12800             Roo.log(values);
12801           
12802             return '';
12803         }
12804         try { 
12805             
12806             if(t.execCall && t.execCall.call(this, values, parent)){
12807                 return '';
12808             }
12809         } catch(e) {
12810             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12811             Roo.log(values);
12812             return '';
12813         }
12814         
12815         try {
12816             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12817             parent = t.target ? values : parent;
12818             if(t.forCall && vs instanceof Array){
12819                 var buf = [];
12820                 for(var i = 0, len = vs.length; i < len; i++){
12821                     try {
12822                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12823                     } catch (e) {
12824                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12825                         Roo.log(e.body);
12826                         //Roo.log(t.compiled);
12827                         Roo.log(vs[i]);
12828                     }   
12829                 }
12830                 return buf.join('');
12831             }
12832         } catch (e) {
12833             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12834             Roo.log(values);
12835             return '';
12836         }
12837         try {
12838             return t.compiled.call(this, vs, parent);
12839         } catch (e) {
12840             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12841             Roo.log(e.body);
12842             //Roo.log(t.compiled);
12843             Roo.log(values);
12844             return '';
12845         }
12846     },
12847
12848    
12849
12850     applyTemplate : function(values){
12851         return this.master.compiled.call(this, values, {});
12852         //var s = this.subs;
12853     },
12854
12855     apply : function(){
12856         return this.applyTemplate.apply(this, arguments);
12857     }
12858
12859  });
12860
12861 Roo.DomTemplate.from = function(el){
12862     el = Roo.getDom(el);
12863     return new Roo.Domtemplate(el.value || el.innerHTML);
12864 };/*
12865  * Based on:
12866  * Ext JS Library 1.1.1
12867  * Copyright(c) 2006-2007, Ext JS, LLC.
12868  *
12869  * Originally Released Under LGPL - original licence link has changed is not relivant.
12870  *
12871  * Fork - LGPL
12872  * <script type="text/javascript">
12873  */
12874
12875 /**
12876  * @class Roo.util.DelayedTask
12877  * Provides a convenient method of performing setTimeout where a new
12878  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12879  * You can use this class to buffer
12880  * the keypress events for a certain number of milliseconds, and perform only if they stop
12881  * for that amount of time.
12882  * @constructor The parameters to this constructor serve as defaults and are not required.
12883  * @param {Function} fn (optional) The default function to timeout
12884  * @param {Object} scope (optional) The default scope of that timeout
12885  * @param {Array} args (optional) The default Array of arguments
12886  */
12887 Roo.util.DelayedTask = function(fn, scope, args){
12888     var id = null, d, t;
12889
12890     var call = function(){
12891         var now = new Date().getTime();
12892         if(now - t >= d){
12893             clearInterval(id);
12894             id = null;
12895             fn.apply(scope, args || []);
12896         }
12897     };
12898     /**
12899      * Cancels any pending timeout and queues a new one
12900      * @param {Number} delay The milliseconds to delay
12901      * @param {Function} newFn (optional) Overrides function passed to constructor
12902      * @param {Object} newScope (optional) Overrides scope passed to constructor
12903      * @param {Array} newArgs (optional) Overrides args passed to constructor
12904      */
12905     this.delay = function(delay, newFn, newScope, newArgs){
12906         if(id && delay != d){
12907             this.cancel();
12908         }
12909         d = delay;
12910         t = new Date().getTime();
12911         fn = newFn || fn;
12912         scope = newScope || scope;
12913         args = newArgs || args;
12914         if(!id){
12915             id = setInterval(call, d);
12916         }
12917     };
12918
12919     /**
12920      * Cancel the last queued timeout
12921      */
12922     this.cancel = function(){
12923         if(id){
12924             clearInterval(id);
12925             id = null;
12926         }
12927     };
12928 };/*
12929  * Based on:
12930  * Ext JS Library 1.1.1
12931  * Copyright(c) 2006-2007, Ext JS, LLC.
12932  *
12933  * Originally Released Under LGPL - original licence link has changed is not relivant.
12934  *
12935  * Fork - LGPL
12936  * <script type="text/javascript">
12937  */
12938  
12939  
12940 Roo.util.TaskRunner = function(interval){
12941     interval = interval || 10;
12942     var tasks = [], removeQueue = [];
12943     var id = 0;
12944     var running = false;
12945
12946     var stopThread = function(){
12947         running = false;
12948         clearInterval(id);
12949         id = 0;
12950     };
12951
12952     var startThread = function(){
12953         if(!running){
12954             running = true;
12955             id = setInterval(runTasks, interval);
12956         }
12957     };
12958
12959     var removeTask = function(task){
12960         removeQueue.push(task);
12961         if(task.onStop){
12962             task.onStop();
12963         }
12964     };
12965
12966     var runTasks = function(){
12967         if(removeQueue.length > 0){
12968             for(var i = 0, len = removeQueue.length; i < len; i++){
12969                 tasks.remove(removeQueue[i]);
12970             }
12971             removeQueue = [];
12972             if(tasks.length < 1){
12973                 stopThread();
12974                 return;
12975             }
12976         }
12977         var now = new Date().getTime();
12978         for(var i = 0, len = tasks.length; i < len; ++i){
12979             var t = tasks[i];
12980             var itime = now - t.taskRunTime;
12981             if(t.interval <= itime){
12982                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12983                 t.taskRunTime = now;
12984                 if(rt === false || t.taskRunCount === t.repeat){
12985                     removeTask(t);
12986                     return;
12987                 }
12988             }
12989             if(t.duration && t.duration <= (now - t.taskStartTime)){
12990                 removeTask(t);
12991             }
12992         }
12993     };
12994
12995     /**
12996      * Queues a new task.
12997      * @param {Object} task
12998      */
12999     this.start = function(task){
13000         tasks.push(task);
13001         task.taskStartTime = new Date().getTime();
13002         task.taskRunTime = 0;
13003         task.taskRunCount = 0;
13004         startThread();
13005         return task;
13006     };
13007
13008     this.stop = function(task){
13009         removeTask(task);
13010         return task;
13011     };
13012
13013     this.stopAll = function(){
13014         stopThread();
13015         for(var i = 0, len = tasks.length; i < len; i++){
13016             if(tasks[i].onStop){
13017                 tasks[i].onStop();
13018             }
13019         }
13020         tasks = [];
13021         removeQueue = [];
13022     };
13023 };
13024
13025 Roo.TaskMgr = new Roo.util.TaskRunner();/*
13026  * Based on:
13027  * Ext JS Library 1.1.1
13028  * Copyright(c) 2006-2007, Ext JS, LLC.
13029  *
13030  * Originally Released Under LGPL - original licence link has changed is not relivant.
13031  *
13032  * Fork - LGPL
13033  * <script type="text/javascript">
13034  */
13035
13036  
13037 /**
13038  * @class Roo.util.MixedCollection
13039  * @extends Roo.util.Observable
13040  * A Collection class that maintains both numeric indexes and keys and exposes events.
13041  * @constructor
13042  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
13043  * collection (defaults to false)
13044  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
13045  * and return the key value for that item.  This is used when available to look up the key on items that
13046  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
13047  * equivalent to providing an implementation for the {@link #getKey} method.
13048  */
13049 Roo.util.MixedCollection = function(allowFunctions, keyFn){
13050     this.items = [];
13051     this.map = {};
13052     this.keys = [];
13053     this.length = 0;
13054     this.addEvents({
13055         /**
13056          * @event clear
13057          * Fires when the collection is cleared.
13058          */
13059         "clear" : true,
13060         /**
13061          * @event add
13062          * Fires when an item is added to the collection.
13063          * @param {Number} index The index at which the item was added.
13064          * @param {Object} o The item added.
13065          * @param {String} key The key associated with the added item.
13066          */
13067         "add" : true,
13068         /**
13069          * @event replace
13070          * Fires when an item is replaced in the collection.
13071          * @param {String} key he key associated with the new added.
13072          * @param {Object} old The item being replaced.
13073          * @param {Object} new The new item.
13074          */
13075         "replace" : true,
13076         /**
13077          * @event remove
13078          * Fires when an item is removed from the collection.
13079          * @param {Object} o The item being removed.
13080          * @param {String} key (optional) The key associated with the removed item.
13081          */
13082         "remove" : true,
13083         "sort" : true
13084     });
13085     this.allowFunctions = allowFunctions === true;
13086     if(keyFn){
13087         this.getKey = keyFn;
13088     }
13089     Roo.util.MixedCollection.superclass.constructor.call(this);
13090 };
13091
13092 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
13093     allowFunctions : false,
13094     
13095 /**
13096  * Adds an item to the collection.
13097  * @param {String} key The key to associate with the item
13098  * @param {Object} o The item to add.
13099  * @return {Object} The item added.
13100  */
13101     add : function(key, o){
13102         if(arguments.length == 1){
13103             o = arguments[0];
13104             key = this.getKey(o);
13105         }
13106         if(typeof key == "undefined" || key === null){
13107             this.length++;
13108             this.items.push(o);
13109             this.keys.push(null);
13110         }else{
13111             var old = this.map[key];
13112             if(old){
13113                 return this.replace(key, o);
13114             }
13115             this.length++;
13116             this.items.push(o);
13117             this.map[key] = o;
13118             this.keys.push(key);
13119         }
13120         this.fireEvent("add", this.length-1, o, key);
13121         return o;
13122     },
13123        
13124 /**
13125   * MixedCollection has a generic way to fetch keys if you implement getKey.
13126 <pre><code>
13127 // normal way
13128 var mc = new Roo.util.MixedCollection();
13129 mc.add(someEl.dom.id, someEl);
13130 mc.add(otherEl.dom.id, otherEl);
13131 //and so on
13132
13133 // using getKey
13134 var mc = new Roo.util.MixedCollection();
13135 mc.getKey = function(el){
13136    return el.dom.id;
13137 };
13138 mc.add(someEl);
13139 mc.add(otherEl);
13140
13141 // or via the constructor
13142 var mc = new Roo.util.MixedCollection(false, function(el){
13143    return el.dom.id;
13144 });
13145 mc.add(someEl);
13146 mc.add(otherEl);
13147 </code></pre>
13148  * @param o {Object} The item for which to find the key.
13149  * @return {Object} The key for the passed item.
13150  */
13151     getKey : function(o){
13152          return o.id; 
13153     },
13154    
13155 /**
13156  * Replaces an item in the collection.
13157  * @param {String} key The key associated with the item to replace, or the item to replace.
13158  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
13159  * @return {Object}  The new item.
13160  */
13161     replace : function(key, o){
13162         if(arguments.length == 1){
13163             o = arguments[0];
13164             key = this.getKey(o);
13165         }
13166         var old = this.item(key);
13167         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
13168              return this.add(key, o);
13169         }
13170         var index = this.indexOfKey(key);
13171         this.items[index] = o;
13172         this.map[key] = o;
13173         this.fireEvent("replace", key, old, o);
13174         return o;
13175     },
13176    
13177 /**
13178  * Adds all elements of an Array or an Object to the collection.
13179  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
13180  * an Array of values, each of which are added to the collection.
13181  */
13182     addAll : function(objs){
13183         if(arguments.length > 1 || objs instanceof Array){
13184             var args = arguments.length > 1 ? arguments : objs;
13185             for(var i = 0, len = args.length; i < len; i++){
13186                 this.add(args[i]);
13187             }
13188         }else{
13189             for(var key in objs){
13190                 if(this.allowFunctions || typeof objs[key] != "function"){
13191                     this.add(key, objs[key]);
13192                 }
13193             }
13194         }
13195     },
13196    
13197 /**
13198  * Executes the specified function once for every item in the collection, passing each
13199  * item as the first and only parameter. returning false from the function will stop the iteration.
13200  * @param {Function} fn The function to execute for each item.
13201  * @param {Object} scope (optional) The scope in which to execute the function.
13202  */
13203     each : function(fn, scope){
13204         var items = [].concat(this.items); // each safe for removal
13205         for(var i = 0, len = items.length; i < len; i++){
13206             if(fn.call(scope || items[i], items[i], i, len) === false){
13207                 break;
13208             }
13209         }
13210     },
13211    
13212 /**
13213  * Executes the specified function once for every key in the collection, passing each
13214  * key, and its associated item as the first two parameters.
13215  * @param {Function} fn The function to execute for each item.
13216  * @param {Object} scope (optional) The scope in which to execute the function.
13217  */
13218     eachKey : function(fn, scope){
13219         for(var i = 0, len = this.keys.length; i < len; i++){
13220             fn.call(scope || window, this.keys[i], this.items[i], i, len);
13221         }
13222     },
13223    
13224 /**
13225  * Returns the first item in the collection which elicits a true return value from the
13226  * passed selection function.
13227  * @param {Function} fn The selection function to execute for each item.
13228  * @param {Object} scope (optional) The scope in which to execute the function.
13229  * @return {Object} The first item in the collection which returned true from the selection function.
13230  */
13231     find : function(fn, scope){
13232         for(var i = 0, len = this.items.length; i < len; i++){
13233             if(fn.call(scope || window, this.items[i], this.keys[i])){
13234                 return this.items[i];
13235             }
13236         }
13237         return null;
13238     },
13239    
13240 /**
13241  * Inserts an item at the specified index in the collection.
13242  * @param {Number} index The index to insert the item at.
13243  * @param {String} key The key to associate with the new item, or the item itself.
13244  * @param {Object} o  (optional) If the second parameter was a key, the new item.
13245  * @return {Object} The item inserted.
13246  */
13247     insert : function(index, key, o){
13248         if(arguments.length == 2){
13249             o = arguments[1];
13250             key = this.getKey(o);
13251         }
13252         if(index >= this.length){
13253             return this.add(key, o);
13254         }
13255         this.length++;
13256         this.items.splice(index, 0, o);
13257         if(typeof key != "undefined" && key != null){
13258             this.map[key] = o;
13259         }
13260         this.keys.splice(index, 0, key);
13261         this.fireEvent("add", index, o, key);
13262         return o;
13263     },
13264    
13265 /**
13266  * Removed an item from the collection.
13267  * @param {Object} o The item to remove.
13268  * @return {Object} The item removed.
13269  */
13270     remove : function(o){
13271         return this.removeAt(this.indexOf(o));
13272     },
13273    
13274 /**
13275  * Remove an item from a specified index in the collection.
13276  * @param {Number} index The index within the collection of the item to remove.
13277  */
13278     removeAt : function(index){
13279         if(index < this.length && index >= 0){
13280             this.length--;
13281             var o = this.items[index];
13282             this.items.splice(index, 1);
13283             var key = this.keys[index];
13284             if(typeof key != "undefined"){
13285                 delete this.map[key];
13286             }
13287             this.keys.splice(index, 1);
13288             this.fireEvent("remove", o, key);
13289         }
13290     },
13291    
13292 /**
13293  * Removed an item associated with the passed key fom the collection.
13294  * @param {String} key The key of the item to remove.
13295  */
13296     removeKey : function(key){
13297         return this.removeAt(this.indexOfKey(key));
13298     },
13299    
13300 /**
13301  * Returns the number of items in the collection.
13302  * @return {Number} the number of items in the collection.
13303  */
13304     getCount : function(){
13305         return this.length; 
13306     },
13307    
13308 /**
13309  * Returns index within the collection of the passed Object.
13310  * @param {Object} o The item to find the index of.
13311  * @return {Number} index of the item.
13312  */
13313     indexOf : function(o){
13314         if(!this.items.indexOf){
13315             for(var i = 0, len = this.items.length; i < len; i++){
13316                 if(this.items[i] == o) {
13317                     return i;
13318                 }
13319             }
13320             return -1;
13321         }else{
13322             return this.items.indexOf(o);
13323         }
13324     },
13325    
13326 /**
13327  * Returns index within the collection of the passed key.
13328  * @param {String} key The key to find the index of.
13329  * @return {Number} index of the key.
13330  */
13331     indexOfKey : function(key){
13332         if(!this.keys.indexOf){
13333             for(var i = 0, len = this.keys.length; i < len; i++){
13334                 if(this.keys[i] == key) {
13335                     return i;
13336                 }
13337             }
13338             return -1;
13339         }else{
13340             return this.keys.indexOf(key);
13341         }
13342     },
13343    
13344 /**
13345  * Returns the item associated with the passed key OR index. Key has priority over index.
13346  * @param {String/Number} key The key or index of the item.
13347  * @return {Object} The item associated with the passed key.
13348  */
13349     item : function(key){
13350         if (key === 'length') {
13351             return null;
13352         }
13353         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13354         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13355     },
13356     
13357 /**
13358  * Returns the item at the specified index.
13359  * @param {Number} index The index of the item.
13360  * @return {Object}
13361  */
13362     itemAt : function(index){
13363         return this.items[index];
13364     },
13365     
13366 /**
13367  * Returns the item associated with the passed key.
13368  * @param {String/Number} key The key of the item.
13369  * @return {Object} The item associated with the passed key.
13370  */
13371     key : function(key){
13372         return this.map[key];
13373     },
13374    
13375 /**
13376  * Returns true if the collection contains the passed Object as an item.
13377  * @param {Object} o  The Object to look for in the collection.
13378  * @return {Boolean} True if the collection contains the Object as an item.
13379  */
13380     contains : function(o){
13381         return this.indexOf(o) != -1;
13382     },
13383    
13384 /**
13385  * Returns true if the collection contains the passed Object as a key.
13386  * @param {String} key The key to look for in the collection.
13387  * @return {Boolean} True if the collection contains the Object as a key.
13388  */
13389     containsKey : function(key){
13390         return typeof this.map[key] != "undefined";
13391     },
13392    
13393 /**
13394  * Removes all items from the collection.
13395  */
13396     clear : function(){
13397         this.length = 0;
13398         this.items = [];
13399         this.keys = [];
13400         this.map = {};
13401         this.fireEvent("clear");
13402     },
13403    
13404 /**
13405  * Returns the first item in the collection.
13406  * @return {Object} the first item in the collection..
13407  */
13408     first : function(){
13409         return this.items[0]; 
13410     },
13411    
13412 /**
13413  * Returns the last item in the collection.
13414  * @return {Object} the last item in the collection..
13415  */
13416     last : function(){
13417         return this.items[this.length-1];   
13418     },
13419     
13420     _sort : function(property, dir, fn){
13421         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13422         fn = fn || function(a, b){
13423             return a-b;
13424         };
13425         var c = [], k = this.keys, items = this.items;
13426         for(var i = 0, len = items.length; i < len; i++){
13427             c[c.length] = {key: k[i], value: items[i], index: i};
13428         }
13429         c.sort(function(a, b){
13430             var v = fn(a[property], b[property]) * dsc;
13431             if(v == 0){
13432                 v = (a.index < b.index ? -1 : 1);
13433             }
13434             return v;
13435         });
13436         for(var i = 0, len = c.length; i < len; i++){
13437             items[i] = c[i].value;
13438             k[i] = c[i].key;
13439         }
13440         this.fireEvent("sort", this);
13441     },
13442     
13443     /**
13444      * Sorts this collection with the passed comparison function
13445      * @param {String} direction (optional) "ASC" or "DESC"
13446      * @param {Function} fn (optional) comparison function
13447      */
13448     sort : function(dir, fn){
13449         this._sort("value", dir, fn);
13450     },
13451     
13452     /**
13453      * Sorts this collection by keys
13454      * @param {String} direction (optional) "ASC" or "DESC"
13455      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13456      */
13457     keySort : function(dir, fn){
13458         this._sort("key", dir, fn || function(a, b){
13459             return String(a).toUpperCase()-String(b).toUpperCase();
13460         });
13461     },
13462     
13463     /**
13464      * Returns a range of items in this collection
13465      * @param {Number} startIndex (optional) defaults to 0
13466      * @param {Number} endIndex (optional) default to the last item
13467      * @return {Array} An array of items
13468      */
13469     getRange : function(start, end){
13470         var items = this.items;
13471         if(items.length < 1){
13472             return [];
13473         }
13474         start = start || 0;
13475         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13476         var r = [];
13477         if(start <= end){
13478             for(var i = start; i <= end; i++) {
13479                     r[r.length] = items[i];
13480             }
13481         }else{
13482             for(var i = start; i >= end; i--) {
13483                     r[r.length] = items[i];
13484             }
13485         }
13486         return r;
13487     },
13488         
13489     /**
13490      * Filter the <i>objects</i> in this collection by a specific property. 
13491      * Returns a new collection that has been filtered.
13492      * @param {String} property A property on your objects
13493      * @param {String/RegExp} value Either string that the property values 
13494      * should start with or a RegExp to test against the property
13495      * @return {MixedCollection} The new filtered collection
13496      */
13497     filter : function(property, value){
13498         if(!value.exec){ // not a regex
13499             value = String(value);
13500             if(value.length == 0){
13501                 return this.clone();
13502             }
13503             value = new RegExp("^" + Roo.escapeRe(value), "i");
13504         }
13505         return this.filterBy(function(o){
13506             return o && value.test(o[property]);
13507         });
13508         },
13509     
13510     /**
13511      * Filter by a function. * Returns a new collection that has been filtered.
13512      * The passed function will be called with each 
13513      * object in the collection. If the function returns true, the value is included 
13514      * otherwise it is filtered.
13515      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13516      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13517      * @return {MixedCollection} The new filtered collection
13518      */
13519     filterBy : function(fn, scope){
13520         var r = new Roo.util.MixedCollection();
13521         r.getKey = this.getKey;
13522         var k = this.keys, it = this.items;
13523         for(var i = 0, len = it.length; i < len; i++){
13524             if(fn.call(scope||this, it[i], k[i])){
13525                                 r.add(k[i], it[i]);
13526                         }
13527         }
13528         return r;
13529     },
13530     
13531     /**
13532      * Creates a duplicate of this collection
13533      * @return {MixedCollection}
13534      */
13535     clone : function(){
13536         var r = new Roo.util.MixedCollection();
13537         var k = this.keys, it = this.items;
13538         for(var i = 0, len = it.length; i < len; i++){
13539             r.add(k[i], it[i]);
13540         }
13541         r.getKey = this.getKey;
13542         return r;
13543     }
13544 });
13545 /**
13546  * Returns the item associated with the passed key or index.
13547  * @method
13548  * @param {String/Number} key The key or index of the item.
13549  * @return {Object} The item associated with the passed key.
13550  */
13551 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13552  * Based on:
13553  * Ext JS Library 1.1.1
13554  * Copyright(c) 2006-2007, Ext JS, LLC.
13555  *
13556  * Originally Released Under LGPL - original licence link has changed is not relivant.
13557  *
13558  * Fork - LGPL
13559  * <script type="text/javascript">
13560  */
13561 /**
13562  * @class Roo.util.JSON
13563  * Modified version of Douglas Crockford"s json.js that doesn"t
13564  * mess with the Object prototype 
13565  * http://www.json.org/js.html
13566  * @singleton
13567  */
13568 Roo.util.JSON = new (function(){
13569     var useHasOwn = {}.hasOwnProperty ? true : false;
13570     
13571     // crashes Safari in some instances
13572     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13573     
13574     var pad = function(n) {
13575         return n < 10 ? "0" + n : n;
13576     };
13577     
13578     var m = {
13579         "\b": '\\b',
13580         "\t": '\\t',
13581         "\n": '\\n',
13582         "\f": '\\f',
13583         "\r": '\\r',
13584         '"' : '\\"',
13585         "\\": '\\\\'
13586     };
13587
13588     var encodeString = function(s){
13589         if (/["\\\x00-\x1f]/.test(s)) {
13590             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13591                 var c = m[b];
13592                 if(c){
13593                     return c;
13594                 }
13595                 c = b.charCodeAt();
13596                 return "\\u00" +
13597                     Math.floor(c / 16).toString(16) +
13598                     (c % 16).toString(16);
13599             }) + '"';
13600         }
13601         return '"' + s + '"';
13602     };
13603     
13604     var encodeArray = function(o){
13605         var a = ["["], b, i, l = o.length, v;
13606             for (i = 0; i < l; i += 1) {
13607                 v = o[i];
13608                 switch (typeof v) {
13609                     case "undefined":
13610                     case "function":
13611                     case "unknown":
13612                         break;
13613                     default:
13614                         if (b) {
13615                             a.push(',');
13616                         }
13617                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13618                         b = true;
13619                 }
13620             }
13621             a.push("]");
13622             return a.join("");
13623     };
13624     
13625     var encodeDate = function(o){
13626         return '"' + o.getFullYear() + "-" +
13627                 pad(o.getMonth() + 1) + "-" +
13628                 pad(o.getDate()) + "T" +
13629                 pad(o.getHours()) + ":" +
13630                 pad(o.getMinutes()) + ":" +
13631                 pad(o.getSeconds()) + '"';
13632     };
13633     
13634     /**
13635      * Encodes an Object, Array or other value
13636      * @param {Mixed} o The variable to encode
13637      * @return {String} The JSON string
13638      */
13639     this.encode = function(o)
13640     {
13641         // should this be extended to fully wrap stringify..
13642         
13643         if(typeof o == "undefined" || o === null){
13644             return "null";
13645         }else if(o instanceof Array){
13646             return encodeArray(o);
13647         }else if(o instanceof Date){
13648             return encodeDate(o);
13649         }else if(typeof o == "string"){
13650             return encodeString(o);
13651         }else if(typeof o == "number"){
13652             return isFinite(o) ? String(o) : "null";
13653         }else if(typeof o == "boolean"){
13654             return String(o);
13655         }else {
13656             var a = ["{"], b, i, v;
13657             for (i in o) {
13658                 if(!useHasOwn || o.hasOwnProperty(i)) {
13659                     v = o[i];
13660                     switch (typeof v) {
13661                     case "undefined":
13662                     case "function":
13663                     case "unknown":
13664                         break;
13665                     default:
13666                         if(b){
13667                             a.push(',');
13668                         }
13669                         a.push(this.encode(i), ":",
13670                                 v === null ? "null" : this.encode(v));
13671                         b = true;
13672                     }
13673                 }
13674             }
13675             a.push("}");
13676             return a.join("");
13677         }
13678     };
13679     
13680     /**
13681      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13682      * @param {String} json The JSON string
13683      * @return {Object} The resulting object
13684      */
13685     this.decode = function(json){
13686         
13687         return  /** eval:var:json */ eval("(" + json + ')');
13688     };
13689 })();
13690 /** 
13691  * Shorthand for {@link Roo.util.JSON#encode}
13692  * @member Roo encode 
13693  * @method */
13694 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13695 /** 
13696  * Shorthand for {@link Roo.util.JSON#decode}
13697  * @member Roo decode 
13698  * @method */
13699 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13700 /*
13701  * Based on:
13702  * Ext JS Library 1.1.1
13703  * Copyright(c) 2006-2007, Ext JS, LLC.
13704  *
13705  * Originally Released Under LGPL - original licence link has changed is not relivant.
13706  *
13707  * Fork - LGPL
13708  * <script type="text/javascript">
13709  */
13710  
13711 /**
13712  * @class Roo.util.Format
13713  * Reusable data formatting functions
13714  * @singleton
13715  */
13716 Roo.util.Format = function(){
13717     var trimRe = /^\s+|\s+$/g;
13718     return {
13719         /**
13720          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13721          * @param {String} value The string to truncate
13722          * @param {Number} length The maximum length to allow before truncating
13723          * @return {String} The converted text
13724          */
13725         ellipsis : function(value, len){
13726             if(value && value.length > len){
13727                 return value.substr(0, len-3)+"...";
13728             }
13729             return value;
13730         },
13731
13732         /**
13733          * Checks a reference and converts it to empty string if it is undefined
13734          * @param {Mixed} value Reference to check
13735          * @return {Mixed} Empty string if converted, otherwise the original value
13736          */
13737         undef : function(value){
13738             return typeof value != "undefined" ? value : "";
13739         },
13740
13741         /**
13742          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13743          * @param {String} value The string to encode
13744          * @return {String} The encoded text
13745          */
13746         htmlEncode : function(value){
13747             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13748         },
13749
13750         /**
13751          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13752          * @param {String} value The string to decode
13753          * @return {String} The decoded text
13754          */
13755         htmlDecode : function(value){
13756             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13757         },
13758
13759         /**
13760          * Trims any whitespace from either side of a string
13761          * @param {String} value The text to trim
13762          * @return {String} The trimmed text
13763          */
13764         trim : function(value){
13765             return String(value).replace(trimRe, "");
13766         },
13767
13768         /**
13769          * Returns a substring from within an original string
13770          * @param {String} value The original text
13771          * @param {Number} start The start index of the substring
13772          * @param {Number} length The length of the substring
13773          * @return {String} The substring
13774          */
13775         substr : function(value, start, length){
13776             return String(value).substr(start, length);
13777         },
13778
13779         /**
13780          * Converts a string to all lower case letters
13781          * @param {String} value The text to convert
13782          * @return {String} The converted text
13783          */
13784         lowercase : function(value){
13785             return String(value).toLowerCase();
13786         },
13787
13788         /**
13789          * Converts a string to all upper case letters
13790          * @param {String} value The text to convert
13791          * @return {String} The converted text
13792          */
13793         uppercase : function(value){
13794             return String(value).toUpperCase();
13795         },
13796
13797         /**
13798          * Converts the first character only of a string to upper case
13799          * @param {String} value The text to convert
13800          * @return {String} The converted text
13801          */
13802         capitalize : function(value){
13803             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13804         },
13805
13806         // private
13807         call : function(value, fn){
13808             if(arguments.length > 2){
13809                 var args = Array.prototype.slice.call(arguments, 2);
13810                 args.unshift(value);
13811                  
13812                 return /** eval:var:value */  eval(fn).apply(window, args);
13813             }else{
13814                 /** eval:var:value */
13815                 return /** eval:var:value */ eval(fn).call(window, value);
13816             }
13817         },
13818
13819        
13820         /**
13821          * safer version of Math.toFixed..??/
13822          * @param {Number/String} value The numeric value to format
13823          * @param {Number/String} value Decimal places 
13824          * @return {String} The formatted currency string
13825          */
13826         toFixed : function(v, n)
13827         {
13828             // why not use to fixed - precision is buggered???
13829             if (!n) {
13830                 return Math.round(v-0);
13831             }
13832             var fact = Math.pow(10,n+1);
13833             v = (Math.round((v-0)*fact))/fact;
13834             var z = (''+fact).substring(2);
13835             if (v == Math.floor(v)) {
13836                 return Math.floor(v) + '.' + z;
13837             }
13838             
13839             // now just padd decimals..
13840             var ps = String(v).split('.');
13841             var fd = (ps[1] + z);
13842             var r = fd.substring(0,n); 
13843             var rm = fd.substring(n); 
13844             if (rm < 5) {
13845                 return ps[0] + '.' + r;
13846             }
13847             r*=1; // turn it into a number;
13848             r++;
13849             if (String(r).length != n) {
13850                 ps[0]*=1;
13851                 ps[0]++;
13852                 r = String(r).substring(1); // chop the end off.
13853             }
13854             
13855             return ps[0] + '.' + r;
13856              
13857         },
13858         
13859         /**
13860          * Format a number as US currency
13861          * @param {Number/String} value The numeric value to format
13862          * @return {String} The formatted currency string
13863          */
13864         usMoney : function(v){
13865             return '$' + Roo.util.Format.number(v);
13866         },
13867         
13868         /**
13869          * Format a number
13870          * eventually this should probably emulate php's number_format
13871          * @param {Number/String} value The numeric value to format
13872          * @param {Number} decimals number of decimal places
13873          * @param {String} delimiter for thousands (default comma)
13874          * @return {String} The formatted currency string
13875          */
13876         number : function(v, decimals, thousandsDelimiter)
13877         {
13878             // multiply and round.
13879             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13880             thousandsDelimiter = typeof(thousandsDelimiter) == 'undefined' ? ',' : thousandsDelimiter;
13881             
13882             var mul = Math.pow(10, decimals);
13883             var zero = String(mul).substring(1);
13884             v = (Math.round((v-0)*mul))/mul;
13885             
13886             // if it's '0' number.. then
13887             
13888             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13889             v = String(v);
13890             var ps = v.split('.');
13891             var whole = ps[0];
13892             
13893             var r = /(\d+)(\d{3})/;
13894             // add comma's
13895             
13896             if(thousandsDelimiter.length != 0) {
13897                 whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsDelimiter );
13898             } 
13899             
13900             var sub = ps[1] ?
13901                     // has decimals..
13902                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13903                     // does not have decimals
13904                     (decimals ? ('.' + zero) : '');
13905             
13906             
13907             return whole + sub ;
13908         },
13909         
13910         /**
13911          * Parse a value into a formatted date using the specified format pattern.
13912          * @param {Mixed} value The value to format
13913          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13914          * @return {String} The formatted date string
13915          */
13916         date : function(v, format){
13917             if(!v){
13918                 return "";
13919             }
13920             if(!(v instanceof Date)){
13921                 v = new Date(Date.parse(v));
13922             }
13923             return v.dateFormat(format || Roo.util.Format.defaults.date);
13924         },
13925
13926         /**
13927          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13928          * @param {String} format Any valid date format string
13929          * @return {Function} The date formatting function
13930          */
13931         dateRenderer : function(format){
13932             return function(v){
13933                 return Roo.util.Format.date(v, format);  
13934             };
13935         },
13936
13937         // private
13938         stripTagsRE : /<\/?[^>]+>/gi,
13939         
13940         /**
13941          * Strips all HTML tags
13942          * @param {Mixed} value The text from which to strip tags
13943          * @return {String} The stripped text
13944          */
13945         stripTags : function(v){
13946             return !v ? v : String(v).replace(this.stripTagsRE, "");
13947         }
13948     };
13949 }();
13950 Roo.util.Format.defaults = {
13951     date : 'd/M/Y'
13952 };/*
13953  * Based on:
13954  * Ext JS Library 1.1.1
13955  * Copyright(c) 2006-2007, Ext JS, LLC.
13956  *
13957  * Originally Released Under LGPL - original licence link has changed is not relivant.
13958  *
13959  * Fork - LGPL
13960  * <script type="text/javascript">
13961  */
13962
13963
13964  
13965
13966 /**
13967  * @class Roo.MasterTemplate
13968  * @extends Roo.Template
13969  * Provides a template that can have child templates. The syntax is:
13970 <pre><code>
13971 var t = new Roo.MasterTemplate(
13972         '&lt;select name="{name}"&gt;',
13973                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13974         '&lt;/select&gt;'
13975 );
13976 t.add('options', {value: 'foo', text: 'bar'});
13977 // or you can add multiple child elements in one shot
13978 t.addAll('options', [
13979     {value: 'foo', text: 'bar'},
13980     {value: 'foo2', text: 'bar2'},
13981     {value: 'foo3', text: 'bar3'}
13982 ]);
13983 // then append, applying the master template values
13984 t.append('my-form', {name: 'my-select'});
13985 </code></pre>
13986 * A name attribute for the child template is not required if you have only one child
13987 * template or you want to refer to them by index.
13988  */
13989 Roo.MasterTemplate = function(){
13990     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13991     this.originalHtml = this.html;
13992     var st = {};
13993     var m, re = this.subTemplateRe;
13994     re.lastIndex = 0;
13995     var subIndex = 0;
13996     while(m = re.exec(this.html)){
13997         var name = m[1], content = m[2];
13998         st[subIndex] = {
13999             name: name,
14000             index: subIndex,
14001             buffer: [],
14002             tpl : new Roo.Template(content)
14003         };
14004         if(name){
14005             st[name] = st[subIndex];
14006         }
14007         st[subIndex].tpl.compile();
14008         st[subIndex].tpl.call = this.call.createDelegate(this);
14009         subIndex++;
14010     }
14011     this.subCount = subIndex;
14012     this.subs = st;
14013 };
14014 Roo.extend(Roo.MasterTemplate, Roo.Template, {
14015     /**
14016     * The regular expression used to match sub templates
14017     * @type RegExp
14018     * @property
14019     */
14020     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
14021
14022     /**
14023      * Applies the passed values to a child template.
14024      * @param {String/Number} name (optional) The name or index of the child template
14025      * @param {Array/Object} values The values to be applied to the template
14026      * @return {MasterTemplate} this
14027      */
14028      add : function(name, values){
14029         if(arguments.length == 1){
14030             values = arguments[0];
14031             name = 0;
14032         }
14033         var s = this.subs[name];
14034         s.buffer[s.buffer.length] = s.tpl.apply(values);
14035         return this;
14036     },
14037
14038     /**
14039      * Applies all the passed values to a child template.
14040      * @param {String/Number} name (optional) The name or index of the child template
14041      * @param {Array} values The values to be applied to the template, this should be an array of objects.
14042      * @param {Boolean} reset (optional) True to reset the template first
14043      * @return {MasterTemplate} this
14044      */
14045     fill : function(name, values, reset){
14046         var a = arguments;
14047         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
14048             values = a[0];
14049             name = 0;
14050             reset = a[1];
14051         }
14052         if(reset){
14053             this.reset();
14054         }
14055         for(var i = 0, len = values.length; i < len; i++){
14056             this.add(name, values[i]);
14057         }
14058         return this;
14059     },
14060
14061     /**
14062      * Resets the template for reuse
14063      * @return {MasterTemplate} this
14064      */
14065      reset : function(){
14066         var s = this.subs;
14067         for(var i = 0; i < this.subCount; i++){
14068             s[i].buffer = [];
14069         }
14070         return this;
14071     },
14072
14073     applyTemplate : function(values){
14074         var s = this.subs;
14075         var replaceIndex = -1;
14076         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
14077             return s[++replaceIndex].buffer.join("");
14078         });
14079         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
14080     },
14081
14082     apply : function(){
14083         return this.applyTemplate.apply(this, arguments);
14084     },
14085
14086     compile : function(){return this;}
14087 });
14088
14089 /**
14090  * Alias for fill().
14091  * @method
14092  */
14093 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
14094  /**
14095  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
14096  * var tpl = Roo.MasterTemplate.from('element-id');
14097  * @param {String/HTMLElement} el
14098  * @param {Object} config
14099  * @static
14100  */
14101 Roo.MasterTemplate.from = function(el, config){
14102     el = Roo.getDom(el);
14103     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
14104 };/*
14105  * Based on:
14106  * Ext JS Library 1.1.1
14107  * Copyright(c) 2006-2007, Ext JS, LLC.
14108  *
14109  * Originally Released Under LGPL - original licence link has changed is not relivant.
14110  *
14111  * Fork - LGPL
14112  * <script type="text/javascript">
14113  */
14114
14115  
14116 /**
14117  * @class Roo.util.CSS
14118  * Utility class for manipulating CSS rules
14119  * @singleton
14120  */
14121 Roo.util.CSS = function(){
14122         var rules = null;
14123         var doc = document;
14124
14125     var camelRe = /(-[a-z])/gi;
14126     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
14127
14128    return {
14129    /**
14130     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
14131     * tag and appended to the HEAD of the document.
14132     * @param {String|Object} cssText The text containing the css rules
14133     * @param {String} id An id to add to the stylesheet for later removal
14134     * @return {StyleSheet}
14135     */
14136     createStyleSheet : function(cssText, id){
14137         var ss;
14138         var head = doc.getElementsByTagName("head")[0];
14139         var nrules = doc.createElement("style");
14140         nrules.setAttribute("type", "text/css");
14141         if(id){
14142             nrules.setAttribute("id", id);
14143         }
14144         if (typeof(cssText) != 'string') {
14145             // support object maps..
14146             // not sure if this a good idea.. 
14147             // perhaps it should be merged with the general css handling
14148             // and handle js style props.
14149             var cssTextNew = [];
14150             for(var n in cssText) {
14151                 var citems = [];
14152                 for(var k in cssText[n]) {
14153                     citems.push( k + ' : ' +cssText[n][k] + ';' );
14154                 }
14155                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
14156                 
14157             }
14158             cssText = cssTextNew.join("\n");
14159             
14160         }
14161        
14162        
14163        if(Roo.isIE){
14164            head.appendChild(nrules);
14165            ss = nrules.styleSheet;
14166            ss.cssText = cssText;
14167        }else{
14168            try{
14169                 nrules.appendChild(doc.createTextNode(cssText));
14170            }catch(e){
14171                nrules.cssText = cssText; 
14172            }
14173            head.appendChild(nrules);
14174            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
14175        }
14176        this.cacheStyleSheet(ss);
14177        return ss;
14178    },
14179
14180    /**
14181     * Removes a style or link tag by id
14182     * @param {String} id The id of the tag
14183     */
14184    removeStyleSheet : function(id){
14185        var existing = doc.getElementById(id);
14186        if(existing){
14187            existing.parentNode.removeChild(existing);
14188        }
14189    },
14190
14191    /**
14192     * Dynamically swaps an existing stylesheet reference for a new one
14193     * @param {String} id The id of an existing link tag to remove
14194     * @param {String} url The href of the new stylesheet to include
14195     */
14196    swapStyleSheet : function(id, url){
14197        this.removeStyleSheet(id);
14198        var ss = doc.createElement("link");
14199        ss.setAttribute("rel", "stylesheet");
14200        ss.setAttribute("type", "text/css");
14201        ss.setAttribute("id", id);
14202        ss.setAttribute("href", url);
14203        doc.getElementsByTagName("head")[0].appendChild(ss);
14204    },
14205    
14206    /**
14207     * Refresh the rule cache if you have dynamically added stylesheets
14208     * @return {Object} An object (hash) of rules indexed by selector
14209     */
14210    refreshCache : function(){
14211        return this.getRules(true);
14212    },
14213
14214    // private
14215    cacheStyleSheet : function(stylesheet){
14216        if(!rules){
14217            rules = {};
14218        }
14219        try{// try catch for cross domain access issue
14220            var ssRules = stylesheet.cssRules || stylesheet.rules;
14221            for(var j = ssRules.length-1; j >= 0; --j){
14222                rules[ssRules[j].selectorText] = ssRules[j];
14223            }
14224        }catch(e){}
14225    },
14226    
14227    /**
14228     * Gets all css rules for the document
14229     * @param {Boolean} refreshCache true to refresh the internal cache
14230     * @return {Object} An object (hash) of rules indexed by selector
14231     */
14232    getRules : function(refreshCache){
14233                 if(rules == null || refreshCache){
14234                         rules = {};
14235                         var ds = doc.styleSheets;
14236                         for(var i =0, len = ds.length; i < len; i++){
14237                             try{
14238                         this.cacheStyleSheet(ds[i]);
14239                     }catch(e){} 
14240                 }
14241                 }
14242                 return rules;
14243         },
14244         
14245         /**
14246     * Gets an an individual CSS rule by selector(s)
14247     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
14248     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
14249     * @return {CSSRule} The CSS rule or null if one is not found
14250     */
14251    getRule : function(selector, refreshCache){
14252                 var rs = this.getRules(refreshCache);
14253                 if(!(selector instanceof Array)){
14254                     return rs[selector];
14255                 }
14256                 for(var i = 0; i < selector.length; i++){
14257                         if(rs[selector[i]]){
14258                                 return rs[selector[i]];
14259                         }
14260                 }
14261                 return null;
14262         },
14263         
14264         
14265         /**
14266     * Updates a rule property
14267     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14268     * @param {String} property The css property
14269     * @param {String} value The new value for the property
14270     * @return {Boolean} true If a rule was found and updated
14271     */
14272    updateRule : function(selector, property, value){
14273                 if(!(selector instanceof Array)){
14274                         var rule = this.getRule(selector);
14275                         if(rule){
14276                                 rule.style[property.replace(camelRe, camelFn)] = value;
14277                                 return true;
14278                         }
14279                 }else{
14280                         for(var i = 0; i < selector.length; i++){
14281                                 if(this.updateRule(selector[i], property, value)){
14282                                         return true;
14283                                 }
14284                         }
14285                 }
14286                 return false;
14287         }
14288    };   
14289 }();/*
14290  * Based on:
14291  * Ext JS Library 1.1.1
14292  * Copyright(c) 2006-2007, Ext JS, LLC.
14293  *
14294  * Originally Released Under LGPL - original licence link has changed is not relivant.
14295  *
14296  * Fork - LGPL
14297  * <script type="text/javascript">
14298  */
14299
14300  
14301
14302 /**
14303  * @class Roo.util.ClickRepeater
14304  * @extends Roo.util.Observable
14305  * 
14306  * A wrapper class which can be applied to any element. Fires a "click" event while the
14307  * mouse is pressed. The interval between firings may be specified in the config but
14308  * defaults to 10 milliseconds.
14309  * 
14310  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14311  * 
14312  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14313  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14314  * Similar to an autorepeat key delay.
14315  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14316  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14317  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14318  *           "interval" and "delay" are ignored. "immediate" is honored.
14319  * @cfg {Boolean} preventDefault True to prevent the default click event
14320  * @cfg {Boolean} stopDefault True to stop the default click event
14321  * 
14322  * @history
14323  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14324  *     2007-02-02 jvs Renamed to ClickRepeater
14325  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14326  *
14327  *  @constructor
14328  * @param {String/HTMLElement/Element} el The element to listen on
14329  * @param {Object} config
14330  **/
14331 Roo.util.ClickRepeater = function(el, config)
14332 {
14333     this.el = Roo.get(el);
14334     this.el.unselectable();
14335
14336     Roo.apply(this, config);
14337
14338     this.addEvents({
14339     /**
14340      * @event mousedown
14341      * Fires when the mouse button is depressed.
14342      * @param {Roo.util.ClickRepeater} this
14343      */
14344         "mousedown" : true,
14345     /**
14346      * @event click
14347      * Fires on a specified interval during the time the element is pressed.
14348      * @param {Roo.util.ClickRepeater} this
14349      */
14350         "click" : true,
14351     /**
14352      * @event mouseup
14353      * Fires when the mouse key is released.
14354      * @param {Roo.util.ClickRepeater} this
14355      */
14356         "mouseup" : true
14357     });
14358
14359     this.el.on("mousedown", this.handleMouseDown, this);
14360     if(this.preventDefault || this.stopDefault){
14361         this.el.on("click", function(e){
14362             if(this.preventDefault){
14363                 e.preventDefault();
14364             }
14365             if(this.stopDefault){
14366                 e.stopEvent();
14367             }
14368         }, this);
14369     }
14370
14371     // allow inline handler
14372     if(this.handler){
14373         this.on("click", this.handler,  this.scope || this);
14374     }
14375
14376     Roo.util.ClickRepeater.superclass.constructor.call(this);
14377 };
14378
14379 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14380     interval : 20,
14381     delay: 250,
14382     preventDefault : true,
14383     stopDefault : false,
14384     timer : 0,
14385
14386     // private
14387     handleMouseDown : function(){
14388         clearTimeout(this.timer);
14389         this.el.blur();
14390         if(this.pressClass){
14391             this.el.addClass(this.pressClass);
14392         }
14393         this.mousedownTime = new Date();
14394
14395         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14396         this.el.on("mouseout", this.handleMouseOut, this);
14397
14398         this.fireEvent("mousedown", this);
14399         this.fireEvent("click", this);
14400         
14401         this.timer = this.click.defer(this.delay || this.interval, this);
14402     },
14403
14404     // private
14405     click : function(){
14406         this.fireEvent("click", this);
14407         this.timer = this.click.defer(this.getInterval(), this);
14408     },
14409
14410     // private
14411     getInterval: function(){
14412         if(!this.accelerate){
14413             return this.interval;
14414         }
14415         var pressTime = this.mousedownTime.getElapsed();
14416         if(pressTime < 500){
14417             return 400;
14418         }else if(pressTime < 1700){
14419             return 320;
14420         }else if(pressTime < 2600){
14421             return 250;
14422         }else if(pressTime < 3500){
14423             return 180;
14424         }else if(pressTime < 4400){
14425             return 140;
14426         }else if(pressTime < 5300){
14427             return 80;
14428         }else if(pressTime < 6200){
14429             return 50;
14430         }else{
14431             return 10;
14432         }
14433     },
14434
14435     // private
14436     handleMouseOut : function(){
14437         clearTimeout(this.timer);
14438         if(this.pressClass){
14439             this.el.removeClass(this.pressClass);
14440         }
14441         this.el.on("mouseover", this.handleMouseReturn, this);
14442     },
14443
14444     // private
14445     handleMouseReturn : function(){
14446         this.el.un("mouseover", this.handleMouseReturn);
14447         if(this.pressClass){
14448             this.el.addClass(this.pressClass);
14449         }
14450         this.click();
14451     },
14452
14453     // private
14454     handleMouseUp : function(){
14455         clearTimeout(this.timer);
14456         this.el.un("mouseover", this.handleMouseReturn);
14457         this.el.un("mouseout", this.handleMouseOut);
14458         Roo.get(document).un("mouseup", this.handleMouseUp);
14459         this.el.removeClass(this.pressClass);
14460         this.fireEvent("mouseup", this);
14461     }
14462 });/*
14463  * Based on:
14464  * Ext JS Library 1.1.1
14465  * Copyright(c) 2006-2007, Ext JS, LLC.
14466  *
14467  * Originally Released Under LGPL - original licence link has changed is not relivant.
14468  *
14469  * Fork - LGPL
14470  * <script type="text/javascript">
14471  */
14472
14473  
14474 /**
14475  * @class Roo.KeyNav
14476  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14477  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14478  * way to implement custom navigation schemes for any UI component.</p>
14479  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14480  * pageUp, pageDown, del, home, end.  Usage:</p>
14481  <pre><code>
14482 var nav = new Roo.KeyNav("my-element", {
14483     "left" : function(e){
14484         this.moveLeft(e.ctrlKey);
14485     },
14486     "right" : function(e){
14487         this.moveRight(e.ctrlKey);
14488     },
14489     "enter" : function(e){
14490         this.save();
14491     },
14492     scope : this
14493 });
14494 </code></pre>
14495  * @constructor
14496  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14497  * @param {Object} config The config
14498  */
14499 Roo.KeyNav = function(el, config){
14500     this.el = Roo.get(el);
14501     Roo.apply(this, config);
14502     if(!this.disabled){
14503         this.disabled = true;
14504         this.enable();
14505     }
14506 };
14507
14508 Roo.KeyNav.prototype = {
14509     /**
14510      * @cfg {Boolean} disabled
14511      * True to disable this KeyNav instance (defaults to false)
14512      */
14513     disabled : false,
14514     /**
14515      * @cfg {String} defaultEventAction
14516      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14517      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14518      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14519      */
14520     defaultEventAction: "stopEvent",
14521     /**
14522      * @cfg {Boolean} forceKeyDown
14523      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14524      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14525      * handle keydown instead of keypress.
14526      */
14527     forceKeyDown : false,
14528
14529     // private
14530     prepareEvent : function(e){
14531         var k = e.getKey();
14532         var h = this.keyToHandler[k];
14533         //if(h && this[h]){
14534         //    e.stopPropagation();
14535         //}
14536         if(Roo.isSafari && h && k >= 37 && k <= 40){
14537             e.stopEvent();
14538         }
14539     },
14540
14541     // private
14542     relay : function(e){
14543         var k = e.getKey();
14544         var h = this.keyToHandler[k];
14545         if(h && this[h]){
14546             if(this.doRelay(e, this[h], h) !== true){
14547                 e[this.defaultEventAction]();
14548             }
14549         }
14550     },
14551
14552     // private
14553     doRelay : function(e, h, hname){
14554         return h.call(this.scope || this, e);
14555     },
14556
14557     // possible handlers
14558     enter : false,
14559     left : false,
14560     right : false,
14561     up : false,
14562     down : false,
14563     tab : false,
14564     esc : false,
14565     pageUp : false,
14566     pageDown : false,
14567     del : false,
14568     home : false,
14569     end : false,
14570
14571     // quick lookup hash
14572     keyToHandler : {
14573         37 : "left",
14574         39 : "right",
14575         38 : "up",
14576         40 : "down",
14577         33 : "pageUp",
14578         34 : "pageDown",
14579         46 : "del",
14580         36 : "home",
14581         35 : "end",
14582         13 : "enter",
14583         27 : "esc",
14584         9  : "tab"
14585     },
14586
14587         /**
14588          * Enable this KeyNav
14589          */
14590         enable: function(){
14591                 if(this.disabled){
14592             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14593             // the EventObject will normalize Safari automatically
14594             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14595                 this.el.on("keydown", this.relay,  this);
14596             }else{
14597                 this.el.on("keydown", this.prepareEvent,  this);
14598                 this.el.on("keypress", this.relay,  this);
14599             }
14600                     this.disabled = false;
14601                 }
14602         },
14603
14604         /**
14605          * Disable this KeyNav
14606          */
14607         disable: function(){
14608                 if(!this.disabled){
14609                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14610                 this.el.un("keydown", this.relay);
14611             }else{
14612                 this.el.un("keydown", this.prepareEvent);
14613                 this.el.un("keypress", this.relay);
14614             }
14615                     this.disabled = true;
14616                 }
14617         }
14618 };/*
14619  * Based on:
14620  * Ext JS Library 1.1.1
14621  * Copyright(c) 2006-2007, Ext JS, LLC.
14622  *
14623  * Originally Released Under LGPL - original licence link has changed is not relivant.
14624  *
14625  * Fork - LGPL
14626  * <script type="text/javascript">
14627  */
14628
14629  
14630 /**
14631  * @class Roo.KeyMap
14632  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14633  * The constructor accepts the same config object as defined by {@link #addBinding}.
14634  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14635  * combination it will call the function with this signature (if the match is a multi-key
14636  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14637  * A KeyMap can also handle a string representation of keys.<br />
14638  * Usage:
14639  <pre><code>
14640 // map one key by key code
14641 var map = new Roo.KeyMap("my-element", {
14642     key: 13, // or Roo.EventObject.ENTER
14643     fn: myHandler,
14644     scope: myObject
14645 });
14646
14647 // map multiple keys to one action by string
14648 var map = new Roo.KeyMap("my-element", {
14649     key: "a\r\n\t",
14650     fn: myHandler,
14651     scope: myObject
14652 });
14653
14654 // map multiple keys to multiple actions by strings and array of codes
14655 var map = new Roo.KeyMap("my-element", [
14656     {
14657         key: [10,13],
14658         fn: function(){ alert("Return was pressed"); }
14659     }, {
14660         key: "abc",
14661         fn: function(){ alert('a, b or c was pressed'); }
14662     }, {
14663         key: "\t",
14664         ctrl:true,
14665         shift:true,
14666         fn: function(){ alert('Control + shift + tab was pressed.'); }
14667     }
14668 ]);
14669 </code></pre>
14670  * <b>Note: A KeyMap starts enabled</b>
14671  * @constructor
14672  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14673  * @param {Object} config The config (see {@link #addBinding})
14674  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14675  */
14676 Roo.KeyMap = function(el, config, eventName){
14677     this.el  = Roo.get(el);
14678     this.eventName = eventName || "keydown";
14679     this.bindings = [];
14680     if(config){
14681         this.addBinding(config);
14682     }
14683     this.enable();
14684 };
14685
14686 Roo.KeyMap.prototype = {
14687     /**
14688      * True to stop the event from bubbling and prevent the default browser action if the
14689      * key was handled by the KeyMap (defaults to false)
14690      * @type Boolean
14691      */
14692     stopEvent : false,
14693
14694     /**
14695      * Add a new binding to this KeyMap. The following config object properties are supported:
14696      * <pre>
14697 Property    Type             Description
14698 ----------  ---------------  ----------------------------------------------------------------------
14699 key         String/Array     A single keycode or an array of keycodes to handle
14700 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14701 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14702 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14703 fn          Function         The function to call when KeyMap finds the expected key combination
14704 scope       Object           The scope of the callback function
14705 </pre>
14706      *
14707      * Usage:
14708      * <pre><code>
14709 // Create a KeyMap
14710 var map = new Roo.KeyMap(document, {
14711     key: Roo.EventObject.ENTER,
14712     fn: handleKey,
14713     scope: this
14714 });
14715
14716 //Add a new binding to the existing KeyMap later
14717 map.addBinding({
14718     key: 'abc',
14719     shift: true,
14720     fn: handleKey,
14721     scope: this
14722 });
14723 </code></pre>
14724      * @param {Object/Array} config A single KeyMap config or an array of configs
14725      */
14726         addBinding : function(config){
14727         if(config instanceof Array){
14728             for(var i = 0, len = config.length; i < len; i++){
14729                 this.addBinding(config[i]);
14730             }
14731             return;
14732         }
14733         var keyCode = config.key,
14734             shift = config.shift, 
14735             ctrl = config.ctrl, 
14736             alt = config.alt,
14737             fn = config.fn,
14738             scope = config.scope;
14739         if(typeof keyCode == "string"){
14740             var ks = [];
14741             var keyString = keyCode.toUpperCase();
14742             for(var j = 0, len = keyString.length; j < len; j++){
14743                 ks.push(keyString.charCodeAt(j));
14744             }
14745             keyCode = ks;
14746         }
14747         var keyArray = keyCode instanceof Array;
14748         var handler = function(e){
14749             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14750                 var k = e.getKey();
14751                 if(keyArray){
14752                     for(var i = 0, len = keyCode.length; i < len; i++){
14753                         if(keyCode[i] == k){
14754                           if(this.stopEvent){
14755                               e.stopEvent();
14756                           }
14757                           fn.call(scope || window, k, e);
14758                           return;
14759                         }
14760                     }
14761                 }else{
14762                     if(k == keyCode){
14763                         if(this.stopEvent){
14764                            e.stopEvent();
14765                         }
14766                         fn.call(scope || window, k, e);
14767                     }
14768                 }
14769             }
14770         };
14771         this.bindings.push(handler);  
14772         },
14773
14774     /**
14775      * Shorthand for adding a single key listener
14776      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14777      * following options:
14778      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14779      * @param {Function} fn The function to call
14780      * @param {Object} scope (optional) The scope of the function
14781      */
14782     on : function(key, fn, scope){
14783         var keyCode, shift, ctrl, alt;
14784         if(typeof key == "object" && !(key instanceof Array)){
14785             keyCode = key.key;
14786             shift = key.shift;
14787             ctrl = key.ctrl;
14788             alt = key.alt;
14789         }else{
14790             keyCode = key;
14791         }
14792         this.addBinding({
14793             key: keyCode,
14794             shift: shift,
14795             ctrl: ctrl,
14796             alt: alt,
14797             fn: fn,
14798             scope: scope
14799         })
14800     },
14801
14802     // private
14803     handleKeyDown : function(e){
14804             if(this.enabled){ //just in case
14805             var b = this.bindings;
14806             for(var i = 0, len = b.length; i < len; i++){
14807                 b[i].call(this, e);
14808             }
14809             }
14810         },
14811         
14812         /**
14813          * Returns true if this KeyMap is enabled
14814          * @return {Boolean} 
14815          */
14816         isEnabled : function(){
14817             return this.enabled;  
14818         },
14819         
14820         /**
14821          * Enables this KeyMap
14822          */
14823         enable: function(){
14824                 if(!this.enabled){
14825                     this.el.on(this.eventName, this.handleKeyDown, this);
14826                     this.enabled = true;
14827                 }
14828         },
14829
14830         /**
14831          * Disable this KeyMap
14832          */
14833         disable: function(){
14834                 if(this.enabled){
14835                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14836                     this.enabled = false;
14837                 }
14838         }
14839 };/*
14840  * Based on:
14841  * Ext JS Library 1.1.1
14842  * Copyright(c) 2006-2007, Ext JS, LLC.
14843  *
14844  * Originally Released Under LGPL - original licence link has changed is not relivant.
14845  *
14846  * Fork - LGPL
14847  * <script type="text/javascript">
14848  */
14849
14850  
14851 /**
14852  * @class Roo.util.TextMetrics
14853  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14854  * wide, in pixels, a given block of text will be.
14855  * @singleton
14856  */
14857 Roo.util.TextMetrics = function(){
14858     var shared;
14859     return {
14860         /**
14861          * Measures the size of the specified text
14862          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14863          * that can affect the size of the rendered text
14864          * @param {String} text The text to measure
14865          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14866          * in order to accurately measure the text height
14867          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14868          */
14869         measure : function(el, text, fixedWidth){
14870             if(!shared){
14871                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14872             }
14873             shared.bind(el);
14874             shared.setFixedWidth(fixedWidth || 'auto');
14875             return shared.getSize(text);
14876         },
14877
14878         /**
14879          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14880          * the overhead of multiple calls to initialize the style properties on each measurement.
14881          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14882          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14883          * in order to accurately measure the text height
14884          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14885          */
14886         createInstance : function(el, fixedWidth){
14887             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14888         }
14889     };
14890 }();
14891
14892  
14893
14894 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14895     var ml = new Roo.Element(document.createElement('div'));
14896     document.body.appendChild(ml.dom);
14897     ml.position('absolute');
14898     ml.setLeftTop(-1000, -1000);
14899     ml.hide();
14900
14901     if(fixedWidth){
14902         ml.setWidth(fixedWidth);
14903     }
14904      
14905     var instance = {
14906         /**
14907          * Returns the size of the specified text based on the internal element's style and width properties
14908          * @memberOf Roo.util.TextMetrics.Instance#
14909          * @param {String} text The text to measure
14910          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14911          */
14912         getSize : function(text){
14913             ml.update(text);
14914             var s = ml.getSize();
14915             ml.update('');
14916             return s;
14917         },
14918
14919         /**
14920          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14921          * that can affect the size of the rendered text
14922          * @memberOf Roo.util.TextMetrics.Instance#
14923          * @param {String/HTMLElement} el The element, dom node or id
14924          */
14925         bind : function(el){
14926             ml.setStyle(
14927                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14928             );
14929         },
14930
14931         /**
14932          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14933          * to set a fixed width in order to accurately measure the text height.
14934          * @memberOf Roo.util.TextMetrics.Instance#
14935          * @param {Number} width The width to set on the element
14936          */
14937         setFixedWidth : function(width){
14938             ml.setWidth(width);
14939         },
14940
14941         /**
14942          * Returns the measured width of the specified text
14943          * @memberOf Roo.util.TextMetrics.Instance#
14944          * @param {String} text The text to measure
14945          * @return {Number} width The width in pixels
14946          */
14947         getWidth : function(text){
14948             ml.dom.style.width = 'auto';
14949             return this.getSize(text).width;
14950         },
14951
14952         /**
14953          * Returns the measured height of the specified text.  For multiline text, be sure to call
14954          * {@link #setFixedWidth} if necessary.
14955          * @memberOf Roo.util.TextMetrics.Instance#
14956          * @param {String} text The text to measure
14957          * @return {Number} height The height in pixels
14958          */
14959         getHeight : function(text){
14960             return this.getSize(text).height;
14961         }
14962     };
14963
14964     instance.bind(bindTo);
14965
14966     return instance;
14967 };
14968
14969 // backwards compat
14970 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14971  * Based on:
14972  * Ext JS Library 1.1.1
14973  * Copyright(c) 2006-2007, Ext JS, LLC.
14974  *
14975  * Originally Released Under LGPL - original licence link has changed is not relivant.
14976  *
14977  * Fork - LGPL
14978  * <script type="text/javascript">
14979  */
14980
14981 /**
14982  * @class Roo.state.Provider
14983  * Abstract base class for state provider implementations. This class provides methods
14984  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14985  * Provider interface.
14986  */
14987 Roo.state.Provider = function(){
14988     /**
14989      * @event statechange
14990      * Fires when a state change occurs.
14991      * @param {Provider} this This state provider
14992      * @param {String} key The state key which was changed
14993      * @param {String} value The encoded value for the state
14994      */
14995     this.addEvents({
14996         "statechange": true
14997     });
14998     this.state = {};
14999     Roo.state.Provider.superclass.constructor.call(this);
15000 };
15001 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
15002     /**
15003      * Returns the current value for a key
15004      * @param {String} name The key name
15005      * @param {Mixed} defaultValue A default value to return if the key's value is not found
15006      * @return {Mixed} The state data
15007      */
15008     get : function(name, defaultValue){
15009         return typeof this.state[name] == "undefined" ?
15010             defaultValue : this.state[name];
15011     },
15012     
15013     /**
15014      * Clears a value from the state
15015      * @param {String} name The key name
15016      */
15017     clear : function(name){
15018         delete this.state[name];
15019         this.fireEvent("statechange", this, name, null);
15020     },
15021     
15022     /**
15023      * Sets the value for a key
15024      * @param {String} name The key name
15025      * @param {Mixed} value The value to set
15026      */
15027     set : function(name, value){
15028         this.state[name] = value;
15029         this.fireEvent("statechange", this, name, value);
15030     },
15031     
15032     /**
15033      * Decodes a string previously encoded with {@link #encodeValue}.
15034      * @param {String} value The value to decode
15035      * @return {Mixed} The decoded value
15036      */
15037     decodeValue : function(cookie){
15038         var re = /^(a|n|d|b|s|o)\:(.*)$/;
15039         var matches = re.exec(unescape(cookie));
15040         if(!matches || !matches[1]) {
15041             return; // non state cookie
15042         }
15043         var type = matches[1];
15044         var v = matches[2];
15045         switch(type){
15046             case "n":
15047                 return parseFloat(v);
15048             case "d":
15049                 return new Date(Date.parse(v));
15050             case "b":
15051                 return (v == "1");
15052             case "a":
15053                 var all = [];
15054                 var values = v.split("^");
15055                 for(var i = 0, len = values.length; i < len; i++){
15056                     all.push(this.decodeValue(values[i]));
15057                 }
15058                 return all;
15059            case "o":
15060                 var all = {};
15061                 var values = v.split("^");
15062                 for(var i = 0, len = values.length; i < len; i++){
15063                     var kv = values[i].split("=");
15064                     all[kv[0]] = this.decodeValue(kv[1]);
15065                 }
15066                 return all;
15067            default:
15068                 return v;
15069         }
15070     },
15071     
15072     /**
15073      * Encodes a value including type information.  Decode with {@link #decodeValue}.
15074      * @param {Mixed} value The value to encode
15075      * @return {String} The encoded value
15076      */
15077     encodeValue : function(v){
15078         var enc;
15079         if(typeof v == "number"){
15080             enc = "n:" + v;
15081         }else if(typeof v == "boolean"){
15082             enc = "b:" + (v ? "1" : "0");
15083         }else if(v instanceof Date){
15084             enc = "d:" + v.toGMTString();
15085         }else if(v instanceof Array){
15086             var flat = "";
15087             for(var i = 0, len = v.length; i < len; i++){
15088                 flat += this.encodeValue(v[i]);
15089                 if(i != len-1) {
15090                     flat += "^";
15091                 }
15092             }
15093             enc = "a:" + flat;
15094         }else if(typeof v == "object"){
15095             var flat = "";
15096             for(var key in v){
15097                 if(typeof v[key] != "function"){
15098                     flat += key + "=" + this.encodeValue(v[key]) + "^";
15099                 }
15100             }
15101             enc = "o:" + flat.substring(0, flat.length-1);
15102         }else{
15103             enc = "s:" + v;
15104         }
15105         return escape(enc);        
15106     }
15107 });
15108
15109 /*
15110  * Based on:
15111  * Ext JS Library 1.1.1
15112  * Copyright(c) 2006-2007, Ext JS, LLC.
15113  *
15114  * Originally Released Under LGPL - original licence link has changed is not relivant.
15115  *
15116  * Fork - LGPL
15117  * <script type="text/javascript">
15118  */
15119 /**
15120  * @class Roo.state.Manager
15121  * This is the global state manager. By default all components that are "state aware" check this class
15122  * for state information if you don't pass them a custom state provider. In order for this class
15123  * to be useful, it must be initialized with a provider when your application initializes.
15124  <pre><code>
15125 // in your initialization function
15126 init : function(){
15127    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
15128    ...
15129    // supposed you have a {@link Roo.BorderLayout}
15130    var layout = new Roo.BorderLayout(...);
15131    layout.restoreState();
15132    // or a {Roo.BasicDialog}
15133    var dialog = new Roo.BasicDialog(...);
15134    dialog.restoreState();
15135  </code></pre>
15136  * @singleton
15137  */
15138 Roo.state.Manager = function(){
15139     var provider = new Roo.state.Provider();
15140     
15141     return {
15142         /**
15143          * Configures the default state provider for your application
15144          * @param {Provider} stateProvider The state provider to set
15145          */
15146         setProvider : function(stateProvider){
15147             provider = stateProvider;
15148         },
15149         
15150         /**
15151          * Returns the current value for a key
15152          * @param {String} name The key name
15153          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
15154          * @return {Mixed} The state data
15155          */
15156         get : function(key, defaultValue){
15157             return provider.get(key, defaultValue);
15158         },
15159         
15160         /**
15161          * Sets the value for a key
15162          * @param {String} name The key name
15163          * @param {Mixed} value The state data
15164          */
15165          set : function(key, value){
15166             provider.set(key, value);
15167         },
15168         
15169         /**
15170          * Clears a value from the state
15171          * @param {String} name The key name
15172          */
15173         clear : function(key){
15174             provider.clear(key);
15175         },
15176         
15177         /**
15178          * Gets the currently configured state provider
15179          * @return {Provider} The state provider
15180          */
15181         getProvider : function(){
15182             return provider;
15183         }
15184     };
15185 }();
15186 /*
15187  * Based on:
15188  * Ext JS Library 1.1.1
15189  * Copyright(c) 2006-2007, Ext JS, LLC.
15190  *
15191  * Originally Released Under LGPL - original licence link has changed is not relivant.
15192  *
15193  * Fork - LGPL
15194  * <script type="text/javascript">
15195  */
15196 /**
15197  * @class Roo.state.CookieProvider
15198  * @extends Roo.state.Provider
15199  * The default Provider implementation which saves state via cookies.
15200  * <br />Usage:
15201  <pre><code>
15202    var cp = new Roo.state.CookieProvider({
15203        path: "/cgi-bin/",
15204        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
15205        domain: "roojs.com"
15206    })
15207    Roo.state.Manager.setProvider(cp);
15208  </code></pre>
15209  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
15210  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
15211  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
15212  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
15213  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
15214  * domain the page is running on including the 'www' like 'www.roojs.com')
15215  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
15216  * @constructor
15217  * Create a new CookieProvider
15218  * @param {Object} config The configuration object
15219  */
15220 Roo.state.CookieProvider = function(config){
15221     Roo.state.CookieProvider.superclass.constructor.call(this);
15222     this.path = "/";
15223     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
15224     this.domain = null;
15225     this.secure = false;
15226     Roo.apply(this, config);
15227     this.state = this.readCookies();
15228 };
15229
15230 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
15231     // private
15232     set : function(name, value){
15233         if(typeof value == "undefined" || value === null){
15234             this.clear(name);
15235             return;
15236         }
15237         this.setCookie(name, value);
15238         Roo.state.CookieProvider.superclass.set.call(this, name, value);
15239     },
15240
15241     // private
15242     clear : function(name){
15243         this.clearCookie(name);
15244         Roo.state.CookieProvider.superclass.clear.call(this, name);
15245     },
15246
15247     // private
15248     readCookies : function(){
15249         var cookies = {};
15250         var c = document.cookie + ";";
15251         var re = /\s?(.*?)=(.*?);/g;
15252         var matches;
15253         while((matches = re.exec(c)) != null){
15254             var name = matches[1];
15255             var value = matches[2];
15256             if(name && name.substring(0,3) == "ys-"){
15257                 cookies[name.substr(3)] = this.decodeValue(value);
15258             }
15259         }
15260         return cookies;
15261     },
15262
15263     // private
15264     setCookie : function(name, value){
15265         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15266            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15267            ((this.path == null) ? "" : ("; path=" + this.path)) +
15268            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15269            ((this.secure == true) ? "; secure" : "");
15270     },
15271
15272     // private
15273     clearCookie : function(name){
15274         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15275            ((this.path == null) ? "" : ("; path=" + this.path)) +
15276            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15277            ((this.secure == true) ? "; secure" : "");
15278     }
15279 });/*
15280  * Based on:
15281  * Ext JS Library 1.1.1
15282  * Copyright(c) 2006-2007, Ext JS, LLC.
15283  *
15284  * Originally Released Under LGPL - original licence link has changed is not relivant.
15285  *
15286  * Fork - LGPL
15287  * <script type="text/javascript">
15288  */
15289  
15290
15291 /**
15292  * @class Roo.ComponentMgr
15293  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15294  * @singleton
15295  */
15296 Roo.ComponentMgr = function(){
15297     var all = new Roo.util.MixedCollection();
15298
15299     return {
15300         /**
15301          * Registers a component.
15302          * @param {Roo.Component} c The component
15303          */
15304         register : function(c){
15305             all.add(c);
15306         },
15307
15308         /**
15309          * Unregisters a component.
15310          * @param {Roo.Component} c The component
15311          */
15312         unregister : function(c){
15313             all.remove(c);
15314         },
15315
15316         /**
15317          * Returns a component by id
15318          * @param {String} id The component id
15319          */
15320         get : function(id){
15321             return all.get(id);
15322         },
15323
15324         /**
15325          * Registers a function that will be called when a specified component is added to ComponentMgr
15326          * @param {String} id The component id
15327          * @param {Funtction} fn The callback function
15328          * @param {Object} scope The scope of the callback
15329          */
15330         onAvailable : function(id, fn, scope){
15331             all.on("add", function(index, o){
15332                 if(o.id == id){
15333                     fn.call(scope || o, o);
15334                     all.un("add", fn, scope);
15335                 }
15336             });
15337         }
15338     };
15339 }();/*
15340  * Based on:
15341  * Ext JS Library 1.1.1
15342  * Copyright(c) 2006-2007, Ext JS, LLC.
15343  *
15344  * Originally Released Under LGPL - original licence link has changed is not relivant.
15345  *
15346  * Fork - LGPL
15347  * <script type="text/javascript">
15348  */
15349  
15350 /**
15351  * @class Roo.Component
15352  * @extends Roo.util.Observable
15353  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15354  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15355  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15356  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15357  * All visual components (widgets) that require rendering into a layout should subclass Component.
15358  * @constructor
15359  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15360  * 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
15361  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15362  */
15363 Roo.Component = function(config){
15364     config = config || {};
15365     if(config.tagName || config.dom || typeof config == "string"){ // element object
15366         config = {el: config, id: config.id || config};
15367     }
15368     this.initialConfig = config;
15369
15370     Roo.apply(this, config);
15371     this.addEvents({
15372         /**
15373          * @event disable
15374          * Fires after the component is disabled.
15375              * @param {Roo.Component} this
15376              */
15377         disable : true,
15378         /**
15379          * @event enable
15380          * Fires after the component is enabled.
15381              * @param {Roo.Component} this
15382              */
15383         enable : true,
15384         /**
15385          * @event beforeshow
15386          * Fires before the component is shown.  Return false to stop the show.
15387              * @param {Roo.Component} this
15388              */
15389         beforeshow : true,
15390         /**
15391          * @event show
15392          * Fires after the component is shown.
15393              * @param {Roo.Component} this
15394              */
15395         show : true,
15396         /**
15397          * @event beforehide
15398          * Fires before the component is hidden. Return false to stop the hide.
15399              * @param {Roo.Component} this
15400              */
15401         beforehide : true,
15402         /**
15403          * @event hide
15404          * Fires after the component is hidden.
15405              * @param {Roo.Component} this
15406              */
15407         hide : true,
15408         /**
15409          * @event beforerender
15410          * Fires before the component is rendered. Return false to stop the render.
15411              * @param {Roo.Component} this
15412              */
15413         beforerender : true,
15414         /**
15415          * @event render
15416          * Fires after the component is rendered.
15417              * @param {Roo.Component} this
15418              */
15419         render : true,
15420         /**
15421          * @event beforedestroy
15422          * Fires before the component is destroyed. Return false to stop the destroy.
15423              * @param {Roo.Component} this
15424              */
15425         beforedestroy : true,
15426         /**
15427          * @event destroy
15428          * Fires after the component is destroyed.
15429              * @param {Roo.Component} this
15430              */
15431         destroy : true
15432     });
15433     if(!this.id){
15434         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
15435     }
15436     Roo.ComponentMgr.register(this);
15437     Roo.Component.superclass.constructor.call(this);
15438     this.initComponent();
15439     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15440         this.render(this.renderTo);
15441         delete this.renderTo;
15442     }
15443 };
15444
15445 /** @private */
15446 Roo.Component.AUTO_ID = 1000;
15447
15448 Roo.extend(Roo.Component, Roo.util.Observable, {
15449     /**
15450      * @scope Roo.Component.prototype
15451      * @type {Boolean}
15452      * true if this component is hidden. Read-only.
15453      */
15454     hidden : false,
15455     /**
15456      * @type {Boolean}
15457      * true if this component is disabled. Read-only.
15458      */
15459     disabled : false,
15460     /**
15461      * @type {Boolean}
15462      * true if this component has been rendered. Read-only.
15463      */
15464     rendered : false,
15465     
15466     /** @cfg {String} disableClass
15467      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15468      */
15469     disabledClass : "x-item-disabled",
15470         /** @cfg {Boolean} allowDomMove
15471          * Whether the component can move the Dom node when rendering (defaults to true).
15472          */
15473     allowDomMove : true,
15474     /** @cfg {String} hideMode (display|visibility)
15475      * How this component should hidden. Supported values are
15476      * "visibility" (css visibility), "offsets" (negative offset position) and
15477      * "display" (css display) - defaults to "display".
15478      */
15479     hideMode: 'display',
15480
15481     /** @private */
15482     ctype : "Roo.Component",
15483
15484     /**
15485      * @cfg {String} actionMode 
15486      * which property holds the element that used for  hide() / show() / disable() / enable()
15487      * default is 'el' for forms you probably want to set this to fieldEl 
15488      */
15489     actionMode : "el",
15490
15491     /** @private */
15492     getActionEl : function(){
15493         return this[this.actionMode];
15494     },
15495
15496     initComponent : Roo.emptyFn,
15497     /**
15498      * If this is a lazy rendering component, render it to its container element.
15499      * @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.
15500      */
15501     render : function(container, position){
15502         
15503         if(this.rendered){
15504             return this;
15505         }
15506         
15507         if(this.fireEvent("beforerender", this) === false){
15508             return false;
15509         }
15510         
15511         if(!container && this.el){
15512             this.el = Roo.get(this.el);
15513             container = this.el.dom.parentNode;
15514             this.allowDomMove = false;
15515         }
15516         this.container = Roo.get(container);
15517         this.rendered = true;
15518         if(position !== undefined){
15519             if(typeof position == 'number'){
15520                 position = this.container.dom.childNodes[position];
15521             }else{
15522                 position = Roo.getDom(position);
15523             }
15524         }
15525         this.onRender(this.container, position || null);
15526         if(this.cls){
15527             this.el.addClass(this.cls);
15528             delete this.cls;
15529         }
15530         if(this.style){
15531             this.el.applyStyles(this.style);
15532             delete this.style;
15533         }
15534         this.fireEvent("render", this);
15535         this.afterRender(this.container);
15536         if(this.hidden){
15537             this.hide();
15538         }
15539         if(this.disabled){
15540             this.disable();
15541         }
15542
15543         return this;
15544         
15545     },
15546
15547     /** @private */
15548     // default function is not really useful
15549     onRender : function(ct, position){
15550         if(this.el){
15551             this.el = Roo.get(this.el);
15552             if(this.allowDomMove !== false){
15553                 ct.dom.insertBefore(this.el.dom, position);
15554             }
15555         }
15556     },
15557
15558     /** @private */
15559     getAutoCreate : function(){
15560         var cfg = typeof this.autoCreate == "object" ?
15561                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15562         if(this.id && !cfg.id){
15563             cfg.id = this.id;
15564         }
15565         return cfg;
15566     },
15567
15568     /** @private */
15569     afterRender : Roo.emptyFn,
15570
15571     /**
15572      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15573      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15574      */
15575     destroy : function(){
15576         if(this.fireEvent("beforedestroy", this) !== false){
15577             this.purgeListeners();
15578             this.beforeDestroy();
15579             if(this.rendered){
15580                 this.el.removeAllListeners();
15581                 this.el.remove();
15582                 if(this.actionMode == "container"){
15583                     this.container.remove();
15584                 }
15585             }
15586             this.onDestroy();
15587             Roo.ComponentMgr.unregister(this);
15588             this.fireEvent("destroy", this);
15589         }
15590     },
15591
15592         /** @private */
15593     beforeDestroy : function(){
15594
15595     },
15596
15597         /** @private */
15598         onDestroy : function(){
15599
15600     },
15601
15602     /**
15603      * Returns the underlying {@link Roo.Element}.
15604      * @return {Roo.Element} The element
15605      */
15606     getEl : function(){
15607         return this.el;
15608     },
15609
15610     /**
15611      * Returns the id of this component.
15612      * @return {String}
15613      */
15614     getId : function(){
15615         return this.id;
15616     },
15617
15618     /**
15619      * Try to focus this component.
15620      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15621      * @return {Roo.Component} this
15622      */
15623     focus : function(selectText){
15624         if(this.rendered){
15625             this.el.focus();
15626             if(selectText === true){
15627                 this.el.dom.select();
15628             }
15629         }
15630         return this;
15631     },
15632
15633     /** @private */
15634     blur : function(){
15635         if(this.rendered){
15636             this.el.blur();
15637         }
15638         return this;
15639     },
15640
15641     /**
15642      * Disable this component.
15643      * @return {Roo.Component} this
15644      */
15645     disable : function(){
15646         if(this.rendered){
15647             this.onDisable();
15648         }
15649         this.disabled = true;
15650         this.fireEvent("disable", this);
15651         return this;
15652     },
15653
15654         // private
15655     onDisable : function(){
15656         this.getActionEl().addClass(this.disabledClass);
15657         this.el.dom.disabled = true;
15658     },
15659
15660     /**
15661      * Enable this component.
15662      * @return {Roo.Component} this
15663      */
15664     enable : function(){
15665         if(this.rendered){
15666             this.onEnable();
15667         }
15668         this.disabled = false;
15669         this.fireEvent("enable", this);
15670         return this;
15671     },
15672
15673         // private
15674     onEnable : function(){
15675         this.getActionEl().removeClass(this.disabledClass);
15676         this.el.dom.disabled = false;
15677     },
15678
15679     /**
15680      * Convenience function for setting disabled/enabled by boolean.
15681      * @param {Boolean} disabled
15682      */
15683     setDisabled : function(disabled){
15684         this[disabled ? "disable" : "enable"]();
15685     },
15686
15687     /**
15688      * Show this component.
15689      * @return {Roo.Component} this
15690      */
15691     show: function(){
15692         if(this.fireEvent("beforeshow", this) !== false){
15693             this.hidden = false;
15694             if(this.rendered){
15695                 this.onShow();
15696             }
15697             this.fireEvent("show", this);
15698         }
15699         return this;
15700     },
15701
15702     // private
15703     onShow : function(){
15704         var ae = this.getActionEl();
15705         if(this.hideMode == 'visibility'){
15706             ae.dom.style.visibility = "visible";
15707         }else if(this.hideMode == 'offsets'){
15708             ae.removeClass('x-hidden');
15709         }else{
15710             ae.dom.style.display = "";
15711         }
15712     },
15713
15714     /**
15715      * Hide this component.
15716      * @return {Roo.Component} this
15717      */
15718     hide: function(){
15719         if(this.fireEvent("beforehide", this) !== false){
15720             this.hidden = true;
15721             if(this.rendered){
15722                 this.onHide();
15723             }
15724             this.fireEvent("hide", this);
15725         }
15726         return this;
15727     },
15728
15729     // private
15730     onHide : function(){
15731         var ae = this.getActionEl();
15732         if(this.hideMode == 'visibility'){
15733             ae.dom.style.visibility = "hidden";
15734         }else if(this.hideMode == 'offsets'){
15735             ae.addClass('x-hidden');
15736         }else{
15737             ae.dom.style.display = "none";
15738         }
15739     },
15740
15741     /**
15742      * Convenience function to hide or show this component by boolean.
15743      * @param {Boolean} visible True to show, false to hide
15744      * @return {Roo.Component} this
15745      */
15746     setVisible: function(visible){
15747         if(visible) {
15748             this.show();
15749         }else{
15750             this.hide();
15751         }
15752         return this;
15753     },
15754
15755     /**
15756      * Returns true if this component is visible.
15757      */
15758     isVisible : function(){
15759         return this.getActionEl().isVisible();
15760     },
15761
15762     cloneConfig : function(overrides){
15763         overrides = overrides || {};
15764         var id = overrides.id || Roo.id();
15765         var cfg = Roo.applyIf(overrides, this.initialConfig);
15766         cfg.id = id; // prevent dup id
15767         return new this.constructor(cfg);
15768     }
15769 });/*
15770  * Based on:
15771  * Ext JS Library 1.1.1
15772  * Copyright(c) 2006-2007, Ext JS, LLC.
15773  *
15774  * Originally Released Under LGPL - original licence link has changed is not relivant.
15775  *
15776  * Fork - LGPL
15777  * <script type="text/javascript">
15778  */
15779
15780 /**
15781  * @class Roo.BoxComponent
15782  * @extends Roo.Component
15783  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15784  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15785  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15786  * layout containers.
15787  * @constructor
15788  * @param {Roo.Element/String/Object} config The configuration options.
15789  */
15790 Roo.BoxComponent = function(config){
15791     Roo.Component.call(this, config);
15792     this.addEvents({
15793         /**
15794          * @event resize
15795          * Fires after the component is resized.
15796              * @param {Roo.Component} this
15797              * @param {Number} adjWidth The box-adjusted width that was set
15798              * @param {Number} adjHeight The box-adjusted height that was set
15799              * @param {Number} rawWidth The width that was originally specified
15800              * @param {Number} rawHeight The height that was originally specified
15801              */
15802         resize : true,
15803         /**
15804          * @event move
15805          * Fires after the component is moved.
15806              * @param {Roo.Component} this
15807              * @param {Number} x The new x position
15808              * @param {Number} y The new y position
15809              */
15810         move : true
15811     });
15812 };
15813
15814 Roo.extend(Roo.BoxComponent, Roo.Component, {
15815     // private, set in afterRender to signify that the component has been rendered
15816     boxReady : false,
15817     // private, used to defer height settings to subclasses
15818     deferHeight: false,
15819     /** @cfg {Number} width
15820      * width (optional) size of component
15821      */
15822      /** @cfg {Number} height
15823      * height (optional) size of component
15824      */
15825      
15826     /**
15827      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15828      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15829      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15830      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15831      * @return {Roo.BoxComponent} this
15832      */
15833     setSize : function(w, h){
15834         // support for standard size objects
15835         if(typeof w == 'object'){
15836             h = w.height;
15837             w = w.width;
15838         }
15839         // not rendered
15840         if(!this.boxReady){
15841             this.width = w;
15842             this.height = h;
15843             return this;
15844         }
15845
15846         // prevent recalcs when not needed
15847         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15848             return this;
15849         }
15850         this.lastSize = {width: w, height: h};
15851
15852         var adj = this.adjustSize(w, h);
15853         var aw = adj.width, ah = adj.height;
15854         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15855             var rz = this.getResizeEl();
15856             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15857                 rz.setSize(aw, ah);
15858             }else if(!this.deferHeight && ah !== undefined){
15859                 rz.setHeight(ah);
15860             }else if(aw !== undefined){
15861                 rz.setWidth(aw);
15862             }
15863             this.onResize(aw, ah, w, h);
15864             this.fireEvent('resize', this, aw, ah, w, h);
15865         }
15866         return this;
15867     },
15868
15869     /**
15870      * Gets the current size of the component's underlying element.
15871      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15872      */
15873     getSize : function(){
15874         return this.el.getSize();
15875     },
15876
15877     /**
15878      * Gets the current XY position of the component's underlying element.
15879      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15880      * @return {Array} The XY position of the element (e.g., [100, 200])
15881      */
15882     getPosition : function(local){
15883         if(local === true){
15884             return [this.el.getLeft(true), this.el.getTop(true)];
15885         }
15886         return this.xy || this.el.getXY();
15887     },
15888
15889     /**
15890      * Gets the current box measurements of the component's underlying element.
15891      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15892      * @returns {Object} box An object in the format {x, y, width, height}
15893      */
15894     getBox : function(local){
15895         var s = this.el.getSize();
15896         if(local){
15897             s.x = this.el.getLeft(true);
15898             s.y = this.el.getTop(true);
15899         }else{
15900             var xy = this.xy || this.el.getXY();
15901             s.x = xy[0];
15902             s.y = xy[1];
15903         }
15904         return s;
15905     },
15906
15907     /**
15908      * Sets the current box measurements of the component's underlying element.
15909      * @param {Object} box An object in the format {x, y, width, height}
15910      * @returns {Roo.BoxComponent} this
15911      */
15912     updateBox : function(box){
15913         this.setSize(box.width, box.height);
15914         this.setPagePosition(box.x, box.y);
15915         return this;
15916     },
15917
15918     // protected
15919     getResizeEl : function(){
15920         return this.resizeEl || this.el;
15921     },
15922
15923     // protected
15924     getPositionEl : function(){
15925         return this.positionEl || this.el;
15926     },
15927
15928     /**
15929      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15930      * This method fires the move event.
15931      * @param {Number} left The new left
15932      * @param {Number} top The new top
15933      * @returns {Roo.BoxComponent} this
15934      */
15935     setPosition : function(x, y){
15936         this.x = x;
15937         this.y = y;
15938         if(!this.boxReady){
15939             return this;
15940         }
15941         var adj = this.adjustPosition(x, y);
15942         var ax = adj.x, ay = adj.y;
15943
15944         var el = this.getPositionEl();
15945         if(ax !== undefined || ay !== undefined){
15946             if(ax !== undefined && ay !== undefined){
15947                 el.setLeftTop(ax, ay);
15948             }else if(ax !== undefined){
15949                 el.setLeft(ax);
15950             }else if(ay !== undefined){
15951                 el.setTop(ay);
15952             }
15953             this.onPosition(ax, ay);
15954             this.fireEvent('move', this, ax, ay);
15955         }
15956         return this;
15957     },
15958
15959     /**
15960      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15961      * This method fires the move event.
15962      * @param {Number} x The new x position
15963      * @param {Number} y The new y position
15964      * @returns {Roo.BoxComponent} this
15965      */
15966     setPagePosition : function(x, y){
15967         this.pageX = x;
15968         this.pageY = y;
15969         if(!this.boxReady){
15970             return;
15971         }
15972         if(x === undefined || y === undefined){ // cannot translate undefined points
15973             return;
15974         }
15975         var p = this.el.translatePoints(x, y);
15976         this.setPosition(p.left, p.top);
15977         return this;
15978     },
15979
15980     // private
15981     onRender : function(ct, position){
15982         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15983         if(this.resizeEl){
15984             this.resizeEl = Roo.get(this.resizeEl);
15985         }
15986         if(this.positionEl){
15987             this.positionEl = Roo.get(this.positionEl);
15988         }
15989     },
15990
15991     // private
15992     afterRender : function(){
15993         Roo.BoxComponent.superclass.afterRender.call(this);
15994         this.boxReady = true;
15995         this.setSize(this.width, this.height);
15996         if(this.x || this.y){
15997             this.setPosition(this.x, this.y);
15998         }
15999         if(this.pageX || this.pageY){
16000             this.setPagePosition(this.pageX, this.pageY);
16001         }
16002     },
16003
16004     /**
16005      * Force the component's size to recalculate based on the underlying element's current height and width.
16006      * @returns {Roo.BoxComponent} this
16007      */
16008     syncSize : function(){
16009         delete this.lastSize;
16010         this.setSize(this.el.getWidth(), this.el.getHeight());
16011         return this;
16012     },
16013
16014     /**
16015      * Called after the component is resized, this method is empty by default but can be implemented by any
16016      * subclass that needs to perform custom logic after a resize occurs.
16017      * @param {Number} adjWidth The box-adjusted width that was set
16018      * @param {Number} adjHeight The box-adjusted height that was set
16019      * @param {Number} rawWidth The width that was originally specified
16020      * @param {Number} rawHeight The height that was originally specified
16021      */
16022     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
16023
16024     },
16025
16026     /**
16027      * Called after the component is moved, this method is empty by default but can be implemented by any
16028      * subclass that needs to perform custom logic after a move occurs.
16029      * @param {Number} x The new x position
16030      * @param {Number} y The new y position
16031      */
16032     onPosition : function(x, y){
16033
16034     },
16035
16036     // private
16037     adjustSize : function(w, h){
16038         if(this.autoWidth){
16039             w = 'auto';
16040         }
16041         if(this.autoHeight){
16042             h = 'auto';
16043         }
16044         return {width : w, height: h};
16045     },
16046
16047     // private
16048     adjustPosition : function(x, y){
16049         return {x : x, y: y};
16050     }
16051 });/*
16052  * Based on:
16053  * Ext JS Library 1.1.1
16054  * Copyright(c) 2006-2007, Ext JS, LLC.
16055  *
16056  * Originally Released Under LGPL - original licence link has changed is not relivant.
16057  *
16058  * Fork - LGPL
16059  * <script type="text/javascript">
16060  */
16061  (function(){ 
16062 /**
16063  * @class Roo.Layer
16064  * @extends Roo.Element
16065  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
16066  * automatic maintaining of shadow/shim positions.
16067  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
16068  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
16069  * you can pass a string with a CSS class name. False turns off the shadow.
16070  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
16071  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
16072  * @cfg {String} cls CSS class to add to the element
16073  * @cfg {Number} zindex Starting z-index (defaults to 11000)
16074  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
16075  * @constructor
16076  * @param {Object} config An object with config options.
16077  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
16078  */
16079
16080 Roo.Layer = function(config, existingEl){
16081     config = config || {};
16082     var dh = Roo.DomHelper;
16083     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
16084     if(existingEl){
16085         this.dom = Roo.getDom(existingEl);
16086     }
16087     if(!this.dom){
16088         var o = config.dh || {tag: "div", cls: "x-layer"};
16089         this.dom = dh.append(pel, o);
16090     }
16091     if(config.cls){
16092         this.addClass(config.cls);
16093     }
16094     this.constrain = config.constrain !== false;
16095     this.visibilityMode = Roo.Element.VISIBILITY;
16096     if(config.id){
16097         this.id = this.dom.id = config.id;
16098     }else{
16099         this.id = Roo.id(this.dom);
16100     }
16101     this.zindex = config.zindex || this.getZIndex();
16102     this.position("absolute", this.zindex);
16103     if(config.shadow){
16104         this.shadowOffset = config.shadowOffset || 4;
16105         this.shadow = new Roo.Shadow({
16106             offset : this.shadowOffset,
16107             mode : config.shadow
16108         });
16109     }else{
16110         this.shadowOffset = 0;
16111     }
16112     this.useShim = config.shim !== false && Roo.useShims;
16113     this.useDisplay = config.useDisplay;
16114     this.hide();
16115 };
16116
16117 var supr = Roo.Element.prototype;
16118
16119 // shims are shared among layer to keep from having 100 iframes
16120 var shims = [];
16121
16122 Roo.extend(Roo.Layer, Roo.Element, {
16123
16124     getZIndex : function(){
16125         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
16126     },
16127
16128     getShim : function(){
16129         if(!this.useShim){
16130             return null;
16131         }
16132         if(this.shim){
16133             return this.shim;
16134         }
16135         var shim = shims.shift();
16136         if(!shim){
16137             shim = this.createShim();
16138             shim.enableDisplayMode('block');
16139             shim.dom.style.display = 'none';
16140             shim.dom.style.visibility = 'visible';
16141         }
16142         var pn = this.dom.parentNode;
16143         if(shim.dom.parentNode != pn){
16144             pn.insertBefore(shim.dom, this.dom);
16145         }
16146         shim.setStyle('z-index', this.getZIndex()-2);
16147         this.shim = shim;
16148         return shim;
16149     },
16150
16151     hideShim : function(){
16152         if(this.shim){
16153             this.shim.setDisplayed(false);
16154             shims.push(this.shim);
16155             delete this.shim;
16156         }
16157     },
16158
16159     disableShadow : function(){
16160         if(this.shadow){
16161             this.shadowDisabled = true;
16162             this.shadow.hide();
16163             this.lastShadowOffset = this.shadowOffset;
16164             this.shadowOffset = 0;
16165         }
16166     },
16167
16168     enableShadow : function(show){
16169         if(this.shadow){
16170             this.shadowDisabled = false;
16171             this.shadowOffset = this.lastShadowOffset;
16172             delete this.lastShadowOffset;
16173             if(show){
16174                 this.sync(true);
16175             }
16176         }
16177     },
16178
16179     // private
16180     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
16181     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
16182     sync : function(doShow){
16183         var sw = this.shadow;
16184         if(!this.updating && this.isVisible() && (sw || this.useShim)){
16185             var sh = this.getShim();
16186
16187             var w = this.getWidth(),
16188                 h = this.getHeight();
16189
16190             var l = this.getLeft(true),
16191                 t = this.getTop(true);
16192
16193             if(sw && !this.shadowDisabled){
16194                 if(doShow && !sw.isVisible()){
16195                     sw.show(this);
16196                 }else{
16197                     sw.realign(l, t, w, h);
16198                 }
16199                 if(sh){
16200                     if(doShow){
16201                        sh.show();
16202                     }
16203                     // fit the shim behind the shadow, so it is shimmed too
16204                     var a = sw.adjusts, s = sh.dom.style;
16205                     s.left = (Math.min(l, l+a.l))+"px";
16206                     s.top = (Math.min(t, t+a.t))+"px";
16207                     s.width = (w+a.w)+"px";
16208                     s.height = (h+a.h)+"px";
16209                 }
16210             }else if(sh){
16211                 if(doShow){
16212                    sh.show();
16213                 }
16214                 sh.setSize(w, h);
16215                 sh.setLeftTop(l, t);
16216             }
16217             
16218         }
16219     },
16220
16221     // private
16222     destroy : function(){
16223         this.hideShim();
16224         if(this.shadow){
16225             this.shadow.hide();
16226         }
16227         this.removeAllListeners();
16228         var pn = this.dom.parentNode;
16229         if(pn){
16230             pn.removeChild(this.dom);
16231         }
16232         Roo.Element.uncache(this.id);
16233     },
16234
16235     remove : function(){
16236         this.destroy();
16237     },
16238
16239     // private
16240     beginUpdate : function(){
16241         this.updating = true;
16242     },
16243
16244     // private
16245     endUpdate : function(){
16246         this.updating = false;
16247         this.sync(true);
16248     },
16249
16250     // private
16251     hideUnders : function(negOffset){
16252         if(this.shadow){
16253             this.shadow.hide();
16254         }
16255         this.hideShim();
16256     },
16257
16258     // private
16259     constrainXY : function(){
16260         if(this.constrain){
16261             var vw = Roo.lib.Dom.getViewWidth(),
16262                 vh = Roo.lib.Dom.getViewHeight();
16263             var s = Roo.get(document).getScroll();
16264
16265             var xy = this.getXY();
16266             var x = xy[0], y = xy[1];   
16267             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
16268             // only move it if it needs it
16269             var moved = false;
16270             // first validate right/bottom
16271             if((x + w) > vw+s.left){
16272                 x = vw - w - this.shadowOffset;
16273                 moved = true;
16274             }
16275             if((y + h) > vh+s.top){
16276                 y = vh - h - this.shadowOffset;
16277                 moved = true;
16278             }
16279             // then make sure top/left isn't negative
16280             if(x < s.left){
16281                 x = s.left;
16282                 moved = true;
16283             }
16284             if(y < s.top){
16285                 y = s.top;
16286                 moved = true;
16287             }
16288             if(moved){
16289                 if(this.avoidY){
16290                     var ay = this.avoidY;
16291                     if(y <= ay && (y+h) >= ay){
16292                         y = ay-h-5;   
16293                     }
16294                 }
16295                 xy = [x, y];
16296                 this.storeXY(xy);
16297                 supr.setXY.call(this, xy);
16298                 this.sync();
16299             }
16300         }
16301     },
16302
16303     isVisible : function(){
16304         return this.visible;    
16305     },
16306
16307     // private
16308     showAction : function(){
16309         this.visible = true; // track visibility to prevent getStyle calls
16310         if(this.useDisplay === true){
16311             this.setDisplayed("");
16312         }else if(this.lastXY){
16313             supr.setXY.call(this, this.lastXY);
16314         }else if(this.lastLT){
16315             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
16316         }
16317     },
16318
16319     // private
16320     hideAction : function(){
16321         this.visible = false;
16322         if(this.useDisplay === true){
16323             this.setDisplayed(false);
16324         }else{
16325             this.setLeftTop(-10000,-10000);
16326         }
16327     },
16328
16329     // overridden Element method
16330     setVisible : function(v, a, d, c, e){
16331         if(v){
16332             this.showAction();
16333         }
16334         if(a && v){
16335             var cb = function(){
16336                 this.sync(true);
16337                 if(c){
16338                     c();
16339                 }
16340             }.createDelegate(this);
16341             supr.setVisible.call(this, true, true, d, cb, e);
16342         }else{
16343             if(!v){
16344                 this.hideUnders(true);
16345             }
16346             var cb = c;
16347             if(a){
16348                 cb = function(){
16349                     this.hideAction();
16350                     if(c){
16351                         c();
16352                     }
16353                 }.createDelegate(this);
16354             }
16355             supr.setVisible.call(this, v, a, d, cb, e);
16356             if(v){
16357                 this.sync(true);
16358             }else if(!a){
16359                 this.hideAction();
16360             }
16361         }
16362     },
16363
16364     storeXY : function(xy){
16365         delete this.lastLT;
16366         this.lastXY = xy;
16367     },
16368
16369     storeLeftTop : function(left, top){
16370         delete this.lastXY;
16371         this.lastLT = [left, top];
16372     },
16373
16374     // private
16375     beforeFx : function(){
16376         this.beforeAction();
16377         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
16378     },
16379
16380     // private
16381     afterFx : function(){
16382         Roo.Layer.superclass.afterFx.apply(this, arguments);
16383         this.sync(this.isVisible());
16384     },
16385
16386     // private
16387     beforeAction : function(){
16388         if(!this.updating && this.shadow){
16389             this.shadow.hide();
16390         }
16391     },
16392
16393     // overridden Element method
16394     setLeft : function(left){
16395         this.storeLeftTop(left, this.getTop(true));
16396         supr.setLeft.apply(this, arguments);
16397         this.sync();
16398     },
16399
16400     setTop : function(top){
16401         this.storeLeftTop(this.getLeft(true), top);
16402         supr.setTop.apply(this, arguments);
16403         this.sync();
16404     },
16405
16406     setLeftTop : function(left, top){
16407         this.storeLeftTop(left, top);
16408         supr.setLeftTop.apply(this, arguments);
16409         this.sync();
16410     },
16411
16412     setXY : function(xy, a, d, c, e){
16413         this.fixDisplay();
16414         this.beforeAction();
16415         this.storeXY(xy);
16416         var cb = this.createCB(c);
16417         supr.setXY.call(this, xy, a, d, cb, e);
16418         if(!a){
16419             cb();
16420         }
16421     },
16422
16423     // private
16424     createCB : function(c){
16425         var el = this;
16426         return function(){
16427             el.constrainXY();
16428             el.sync(true);
16429             if(c){
16430                 c();
16431             }
16432         };
16433     },
16434
16435     // overridden Element method
16436     setX : function(x, a, d, c, e){
16437         this.setXY([x, this.getY()], a, d, c, e);
16438     },
16439
16440     // overridden Element method
16441     setY : function(y, a, d, c, e){
16442         this.setXY([this.getX(), y], a, d, c, e);
16443     },
16444
16445     // overridden Element method
16446     setSize : function(w, h, a, d, c, e){
16447         this.beforeAction();
16448         var cb = this.createCB(c);
16449         supr.setSize.call(this, w, h, a, d, cb, e);
16450         if(!a){
16451             cb();
16452         }
16453     },
16454
16455     // overridden Element method
16456     setWidth : function(w, a, d, c, e){
16457         this.beforeAction();
16458         var cb = this.createCB(c);
16459         supr.setWidth.call(this, w, a, d, cb, e);
16460         if(!a){
16461             cb();
16462         }
16463     },
16464
16465     // overridden Element method
16466     setHeight : function(h, a, d, c, e){
16467         this.beforeAction();
16468         var cb = this.createCB(c);
16469         supr.setHeight.call(this, h, a, d, cb, e);
16470         if(!a){
16471             cb();
16472         }
16473     },
16474
16475     // overridden Element method
16476     setBounds : function(x, y, w, h, a, d, c, e){
16477         this.beforeAction();
16478         var cb = this.createCB(c);
16479         if(!a){
16480             this.storeXY([x, y]);
16481             supr.setXY.call(this, [x, y]);
16482             supr.setSize.call(this, w, h, a, d, cb, e);
16483             cb();
16484         }else{
16485             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
16486         }
16487         return this;
16488     },
16489     
16490     /**
16491      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
16492      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
16493      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
16494      * @param {Number} zindex The new z-index to set
16495      * @return {this} The Layer
16496      */
16497     setZIndex : function(zindex){
16498         this.zindex = zindex;
16499         this.setStyle("z-index", zindex + 2);
16500         if(this.shadow){
16501             this.shadow.setZIndex(zindex + 1);
16502         }
16503         if(this.shim){
16504             this.shim.setStyle("z-index", zindex);
16505         }
16506     }
16507 });
16508 })();/*
16509  * Original code for Roojs - LGPL
16510  * <script type="text/javascript">
16511  */
16512  
16513 /**
16514  * @class Roo.XComponent
16515  * A delayed Element creator...
16516  * Or a way to group chunks of interface together.
16517  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
16518  *  used in conjunction with XComponent.build() it will create an instance of each element,
16519  *  then call addxtype() to build the User interface.
16520  * 
16521  * Mypart.xyx = new Roo.XComponent({
16522
16523     parent : 'Mypart.xyz', // empty == document.element.!!
16524     order : '001',
16525     name : 'xxxx'
16526     region : 'xxxx'
16527     disabled : function() {} 
16528      
16529     tree : function() { // return an tree of xtype declared components
16530         var MODULE = this;
16531         return 
16532         {
16533             xtype : 'NestedLayoutPanel',
16534             // technicall
16535         }
16536      ]
16537  *})
16538  *
16539  *
16540  * It can be used to build a big heiracy, with parent etc.
16541  * or you can just use this to render a single compoent to a dom element
16542  * MYPART.render(Roo.Element | String(id) | dom_element )
16543  *
16544  *
16545  * Usage patterns.
16546  *
16547  * Classic Roo
16548  *
16549  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
16550  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
16551  *
16552  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
16553  *
16554  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
16555  * - if mulitple topModules exist, the last one is defined as the top module.
16556  *
16557  * Embeded Roo
16558  * 
16559  * When the top level or multiple modules are to embedded into a existing HTML page,
16560  * the parent element can container '#id' of the element where the module will be drawn.
16561  *
16562  * Bootstrap Roo
16563  *
16564  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
16565  * it relies more on a include mechanism, where sub modules are included into an outer page.
16566  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
16567  * 
16568  * Bootstrap Roo Included elements
16569  *
16570  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
16571  * hence confusing the component builder as it thinks there are multiple top level elements. 
16572  *
16573  * String Over-ride & Translations
16574  *
16575  * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
16576  * and also the 'overlaying of string values - needed when different versions of the same application with different text content
16577  * are needed. @see Roo.XComponent.overlayString  
16578  * 
16579  * 
16580  * 
16581  * @extends Roo.util.Observable
16582  * @constructor
16583  * @param cfg {Object} configuration of component
16584  * 
16585  */
16586 Roo.XComponent = function(cfg) {
16587     Roo.apply(this, cfg);
16588     this.addEvents({ 
16589         /**
16590              * @event built
16591              * Fires when this the componnt is built
16592              * @param {Roo.XComponent} c the component
16593              */
16594         'built' : true
16595         
16596     });
16597     this.region = this.region || 'center'; // default..
16598     Roo.XComponent.register(this);
16599     this.modules = false;
16600     this.el = false; // where the layout goes..
16601     
16602     
16603 }
16604 Roo.extend(Roo.XComponent, Roo.util.Observable, {
16605     /**
16606      * @property el
16607      * The created element (with Roo.factory())
16608      * @type {Roo.Layout}
16609      */
16610     el  : false,
16611     
16612     /**
16613      * @property el
16614      * for BC  - use el in new code
16615      * @type {Roo.Layout}
16616      */
16617     panel : false,
16618     
16619     /**
16620      * @property layout
16621      * for BC  - use el in new code
16622      * @type {Roo.Layout}
16623      */
16624     layout : false,
16625     
16626      /**
16627      * @cfg {Function|boolean} disabled
16628      * If this module is disabled by some rule, return true from the funtion
16629      */
16630     disabled : false,
16631     
16632     /**
16633      * @cfg {String} parent 
16634      * Name of parent element which it get xtype added to..
16635      */
16636     parent: false,
16637     
16638     /**
16639      * @cfg {String} order
16640      * Used to set the order in which elements are created (usefull for multiple tabs)
16641      */
16642     
16643     order : false,
16644     /**
16645      * @cfg {String} name
16646      * String to display while loading.
16647      */
16648     name : false,
16649     /**
16650      * @cfg {String} region
16651      * Region to render component to (defaults to center)
16652      */
16653     region : 'center',
16654     
16655     /**
16656      * @cfg {Array} items
16657      * A single item array - the first element is the root of the tree..
16658      * It's done this way to stay compatible with the Xtype system...
16659      */
16660     items : false,
16661     
16662     /**
16663      * @property _tree
16664      * The method that retuns the tree of parts that make up this compoennt 
16665      * @type {function}
16666      */
16667     _tree  : false,
16668     
16669      /**
16670      * render
16671      * render element to dom or tree
16672      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
16673      */
16674     
16675     render : function(el)
16676     {
16677         
16678         el = el || false;
16679         var hp = this.parent ? 1 : 0;
16680         Roo.debug &&  Roo.log(this);
16681         
16682         var tree = this._tree ? this._tree() : this.tree();
16683
16684         
16685         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
16686             // if parent is a '#.....' string, then let's use that..
16687             var ename = this.parent.substr(1);
16688             this.parent = false;
16689             Roo.debug && Roo.log(ename);
16690             switch (ename) {
16691                 case 'bootstrap-body':
16692                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
16693                         // this is the BorderLayout standard?
16694                        this.parent = { el : true };
16695                        break;
16696                     }
16697                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
16698                         // need to insert stuff...
16699                         this.parent =  {
16700                              el : new Roo.bootstrap.layout.Border({
16701                                  el : document.body, 
16702                      
16703                                  center: {
16704                                     titlebar: false,
16705                                     autoScroll:false,
16706                                     closeOnTab: true,
16707                                     tabPosition: 'top',
16708                                       //resizeTabs: true,
16709                                     alwaysShowTabs: true,
16710                                     hideTabs: false
16711                                      //minTabWidth: 140
16712                                  }
16713                              })
16714                         
16715                          };
16716                          break;
16717                     }
16718                          
16719                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
16720                         this.parent = { el :  new  Roo.bootstrap.Body() };
16721                         Roo.debug && Roo.log("setting el to doc body");
16722                          
16723                     } else {
16724                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
16725                     }
16726                     break;
16727                 case 'bootstrap':
16728                     this.parent = { el : true};
16729                     // fall through
16730                 default:
16731                     el = Roo.get(ename);
16732                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
16733                         this.parent = { el : true};
16734                     }
16735                     
16736                     break;
16737             }
16738                 
16739             
16740             if (!el && !this.parent) {
16741                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
16742                 return;
16743             }
16744         }
16745         
16746         Roo.debug && Roo.log("EL:");
16747         Roo.debug && Roo.log(el);
16748         Roo.debug && Roo.log("this.parent.el:");
16749         Roo.debug && Roo.log(this.parent.el);
16750         
16751
16752         // altertive root elements ??? - we need a better way to indicate these.
16753         var is_alt = Roo.XComponent.is_alt ||
16754                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
16755                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
16756                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
16757         
16758         
16759         
16760         if (!this.parent && is_alt) {
16761             //el = Roo.get(document.body);
16762             this.parent = { el : true };
16763         }
16764             
16765             
16766         
16767         if (!this.parent) {
16768             
16769             Roo.debug && Roo.log("no parent - creating one");
16770             
16771             el = el ? Roo.get(el) : false;      
16772             
16773             if (typeof(Roo.BorderLayout) == 'undefined' ) {
16774                 
16775                 this.parent =  {
16776                     el : new Roo.bootstrap.layout.Border({
16777                         el: el || document.body,
16778                     
16779                         center: {
16780                             titlebar: false,
16781                             autoScroll:false,
16782                             closeOnTab: true,
16783                             tabPosition: 'top',
16784                              //resizeTabs: true,
16785                             alwaysShowTabs: false,
16786                             hideTabs: true,
16787                             minTabWidth: 140,
16788                             overflow: 'visible'
16789                          }
16790                      })
16791                 };
16792             } else {
16793             
16794                 // it's a top level one..
16795                 this.parent =  {
16796                     el : new Roo.BorderLayout(el || document.body, {
16797                         center: {
16798                             titlebar: false,
16799                             autoScroll:false,
16800                             closeOnTab: true,
16801                             tabPosition: 'top',
16802                              //resizeTabs: true,
16803                             alwaysShowTabs: el && hp? false :  true,
16804                             hideTabs: el || !hp ? true :  false,
16805                             minTabWidth: 140
16806                          }
16807                     })
16808                 };
16809             }
16810         }
16811         
16812         if (!this.parent.el) {
16813                 // probably an old style ctor, which has been disabled.
16814                 return;
16815
16816         }
16817                 // The 'tree' method is  '_tree now' 
16818             
16819         tree.region = tree.region || this.region;
16820         var is_body = false;
16821         if (this.parent.el === true) {
16822             // bootstrap... - body..
16823             if (el) {
16824                 tree.el = el;
16825             }
16826             this.parent.el = Roo.factory(tree);
16827             is_body = true;
16828         }
16829         
16830         this.el = this.parent.el.addxtype(tree, undefined, is_body);
16831         this.fireEvent('built', this);
16832         
16833         this.panel = this.el;
16834         this.layout = this.panel.layout;
16835         this.parentLayout = this.parent.layout  || false;  
16836          
16837     }
16838     
16839 });
16840
16841 Roo.apply(Roo.XComponent, {
16842     /**
16843      * @property  hideProgress
16844      * true to disable the building progress bar.. usefull on single page renders.
16845      * @type Boolean
16846      */
16847     hideProgress : false,
16848     /**
16849      * @property  buildCompleted
16850      * True when the builder has completed building the interface.
16851      * @type Boolean
16852      */
16853     buildCompleted : false,
16854      
16855     /**
16856      * @property  topModule
16857      * the upper most module - uses document.element as it's constructor.
16858      * @type Object
16859      */
16860      
16861     topModule  : false,
16862       
16863     /**
16864      * @property  modules
16865      * array of modules to be created by registration system.
16866      * @type {Array} of Roo.XComponent
16867      */
16868     
16869     modules : [],
16870     /**
16871      * @property  elmodules
16872      * array of modules to be created by which use #ID 
16873      * @type {Array} of Roo.XComponent
16874      */
16875      
16876     elmodules : [],
16877
16878      /**
16879      * @property  is_alt
16880      * Is an alternative Root - normally used by bootstrap or other systems,
16881      *    where the top element in the tree can wrap 'body' 
16882      * @type {boolean}  (default false)
16883      */
16884      
16885     is_alt : false,
16886     /**
16887      * @property  build_from_html
16888      * Build elements from html - used by bootstrap HTML stuff 
16889      *    - this is cleared after build is completed
16890      * @type {boolean}    (default false)
16891      */
16892      
16893     build_from_html : false,
16894     /**
16895      * Register components to be built later.
16896      *
16897      * This solves the following issues
16898      * - Building is not done on page load, but after an authentication process has occured.
16899      * - Interface elements are registered on page load
16900      * - Parent Interface elements may not be loaded before child, so this handles that..
16901      * 
16902      *
16903      * example:
16904      * 
16905      * MyApp.register({
16906           order : '000001',
16907           module : 'Pman.Tab.projectMgr',
16908           region : 'center',
16909           parent : 'Pman.layout',
16910           disabled : false,  // or use a function..
16911         })
16912      
16913      * * @param {Object} details about module
16914      */
16915     register : function(obj) {
16916                 
16917         Roo.XComponent.event.fireEvent('register', obj);
16918         switch(typeof(obj.disabled) ) {
16919                 
16920             case 'undefined':
16921                 break;
16922             
16923             case 'function':
16924                 if ( obj.disabled() ) {
16925                         return;
16926                 }
16927                 break;
16928             
16929             default:
16930                 if (obj.disabled || obj.region == '#disabled') {
16931                         return;
16932                 }
16933                 break;
16934         }
16935                 
16936         this.modules.push(obj);
16937          
16938     },
16939     /**
16940      * convert a string to an object..
16941      * eg. 'AAA.BBB' -> finds AAA.BBB
16942
16943      */
16944     
16945     toObject : function(str)
16946     {
16947         if (!str || typeof(str) == 'object') {
16948             return str;
16949         }
16950         if (str.substring(0,1) == '#') {
16951             return str;
16952         }
16953
16954         var ar = str.split('.');
16955         var rt, o;
16956         rt = ar.shift();
16957             /** eval:var:o */
16958         try {
16959             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16960         } catch (e) {
16961             throw "Module not found : " + str;
16962         }
16963         
16964         if (o === false) {
16965             throw "Module not found : " + str;
16966         }
16967         Roo.each(ar, function(e) {
16968             if (typeof(o[e]) == 'undefined') {
16969                 throw "Module not found : " + str;
16970             }
16971             o = o[e];
16972         });
16973         
16974         return o;
16975         
16976     },
16977     
16978     
16979     /**
16980      * move modules into their correct place in the tree..
16981      * 
16982      */
16983     preBuild : function ()
16984     {
16985         var _t = this;
16986         Roo.each(this.modules , function (obj)
16987         {
16988             Roo.XComponent.event.fireEvent('beforebuild', obj);
16989             
16990             var opar = obj.parent;
16991             try { 
16992                 obj.parent = this.toObject(opar);
16993             } catch(e) {
16994                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
16995                 return;
16996             }
16997             
16998             if (!obj.parent) {
16999                 Roo.debug && Roo.log("GOT top level module");
17000                 Roo.debug && Roo.log(obj);
17001                 obj.modules = new Roo.util.MixedCollection(false, 
17002                     function(o) { return o.order + '' }
17003                 );
17004                 this.topModule = obj;
17005                 return;
17006             }
17007                         // parent is a string (usually a dom element name..)
17008             if (typeof(obj.parent) == 'string') {
17009                 this.elmodules.push(obj);
17010                 return;
17011             }
17012             if (obj.parent.constructor != Roo.XComponent) {
17013                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
17014             }
17015             if (!obj.parent.modules) {
17016                 obj.parent.modules = new Roo.util.MixedCollection(false, 
17017                     function(o) { return o.order + '' }
17018                 );
17019             }
17020             if (obj.parent.disabled) {
17021                 obj.disabled = true;
17022             }
17023             obj.parent.modules.add(obj);
17024         }, this);
17025     },
17026     
17027      /**
17028      * make a list of modules to build.
17029      * @return {Array} list of modules. 
17030      */ 
17031     
17032     buildOrder : function()
17033     {
17034         var _this = this;
17035         var cmp = function(a,b) {   
17036             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
17037         };
17038         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
17039             throw "No top level modules to build";
17040         }
17041         
17042         // make a flat list in order of modules to build.
17043         var mods = this.topModule ? [ this.topModule ] : [];
17044                 
17045         
17046         // elmodules (is a list of DOM based modules )
17047         Roo.each(this.elmodules, function(e) {
17048             mods.push(e);
17049             if (!this.topModule &&
17050                 typeof(e.parent) == 'string' &&
17051                 e.parent.substring(0,1) == '#' &&
17052                 Roo.get(e.parent.substr(1))
17053                ) {
17054                 
17055                 _this.topModule = e;
17056             }
17057             
17058         });
17059
17060         
17061         // add modules to their parents..
17062         var addMod = function(m) {
17063             Roo.debug && Roo.log("build Order: add: " + m.name);
17064                 
17065             mods.push(m);
17066             if (m.modules && !m.disabled) {
17067                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
17068                 m.modules.keySort('ASC',  cmp );
17069                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
17070     
17071                 m.modules.each(addMod);
17072             } else {
17073                 Roo.debug && Roo.log("build Order: no child modules");
17074             }
17075             // not sure if this is used any more..
17076             if (m.finalize) {
17077                 m.finalize.name = m.name + " (clean up) ";
17078                 mods.push(m.finalize);
17079             }
17080             
17081         }
17082         if (this.topModule && this.topModule.modules) { 
17083             this.topModule.modules.keySort('ASC',  cmp );
17084             this.topModule.modules.each(addMod);
17085         } 
17086         return mods;
17087     },
17088     
17089      /**
17090      * Build the registered modules.
17091      * @param {Object} parent element.
17092      * @param {Function} optional method to call after module has been added.
17093      * 
17094      */ 
17095    
17096     build : function(opts) 
17097     {
17098         
17099         if (typeof(opts) != 'undefined') {
17100             Roo.apply(this,opts);
17101         }
17102         
17103         this.preBuild();
17104         var mods = this.buildOrder();
17105       
17106         //this.allmods = mods;
17107         //Roo.debug && Roo.log(mods);
17108         //return;
17109         if (!mods.length) { // should not happen
17110             throw "NO modules!!!";
17111         }
17112         
17113         
17114         var msg = "Building Interface...";
17115         // flash it up as modal - so we store the mask!?
17116         if (!this.hideProgress && Roo.MessageBox) {
17117             Roo.MessageBox.show({ title: 'loading' });
17118             Roo.MessageBox.show({
17119                title: "Please wait...",
17120                msg: msg,
17121                width:450,
17122                progress:true,
17123                buttons : false,
17124                closable:false,
17125                modal: false
17126               
17127             });
17128         }
17129         var total = mods.length;
17130         
17131         var _this = this;
17132         var progressRun = function() {
17133             if (!mods.length) {
17134                 Roo.debug && Roo.log('hide?');
17135                 if (!this.hideProgress && Roo.MessageBox) {
17136                     Roo.MessageBox.hide();
17137                 }
17138                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
17139                 
17140                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
17141                 
17142                 // THE END...
17143                 return false;   
17144             }
17145             
17146             var m = mods.shift();
17147             
17148             
17149             Roo.debug && Roo.log(m);
17150             // not sure if this is supported any more.. - modules that are are just function
17151             if (typeof(m) == 'function') { 
17152                 m.call(this);
17153                 return progressRun.defer(10, _this);
17154             } 
17155             
17156             
17157             msg = "Building Interface " + (total  - mods.length) + 
17158                     " of " + total + 
17159                     (m.name ? (' - ' + m.name) : '');
17160                         Roo.debug && Roo.log(msg);
17161             if (!_this.hideProgress &&  Roo.MessageBox) { 
17162                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
17163             }
17164             
17165          
17166             // is the module disabled?
17167             var disabled = (typeof(m.disabled) == 'function') ?
17168                 m.disabled.call(m.module.disabled) : m.disabled;    
17169             
17170             
17171             if (disabled) {
17172                 return progressRun(); // we do not update the display!
17173             }
17174             
17175             // now build 
17176             
17177                         
17178                         
17179             m.render();
17180             // it's 10 on top level, and 1 on others??? why...
17181             return progressRun.defer(10, _this);
17182              
17183         }
17184         progressRun.defer(1, _this);
17185      
17186         
17187         
17188     },
17189     /**
17190      * Overlay a set of modified strings onto a component
17191      * This is dependant on our builder exporting the strings and 'named strings' elements.
17192      * 
17193      * @param {Object} element to overlay on - eg. Pman.Dialog.Login
17194      * @param {Object} associative array of 'named' string and it's new value.
17195      * 
17196      */
17197         overlayStrings : function( component, strings )
17198     {
17199         if (typeof(component['_named_strings']) == 'undefined') {
17200             throw "ERROR: component does not have _named_strings";
17201         }
17202         for ( var k in strings ) {
17203             var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
17204             if (md !== false) {
17205                 component['_strings'][md] = strings[k];
17206             } else {
17207                 Roo.log('could not find named string: ' + k + ' in');
17208                 Roo.log(component);
17209             }
17210             
17211         }
17212         
17213     },
17214     
17215         
17216         /**
17217          * Event Object.
17218          *
17219          *
17220          */
17221         event: false, 
17222     /**
17223          * wrapper for event.on - aliased later..  
17224          * Typically use to register a event handler for register:
17225          *
17226          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
17227          *
17228          */
17229     on : false
17230    
17231     
17232     
17233 });
17234
17235 Roo.XComponent.event = new Roo.util.Observable({
17236                 events : { 
17237                         /**
17238                          * @event register
17239                          * Fires when an Component is registered,
17240                          * set the disable property on the Component to stop registration.
17241                          * @param {Roo.XComponent} c the component being registerd.
17242                          * 
17243                          */
17244                         'register' : true,
17245             /**
17246                          * @event beforebuild
17247                          * Fires before each Component is built
17248                          * can be used to apply permissions.
17249                          * @param {Roo.XComponent} c the component being registerd.
17250                          * 
17251                          */
17252                         'beforebuild' : true,
17253                         /**
17254                          * @event buildcomplete
17255                          * Fires on the top level element when all elements have been built
17256                          * @param {Roo.XComponent} the top level component.
17257                          */
17258                         'buildcomplete' : true
17259                         
17260                 }
17261 });
17262
17263 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
17264  //
17265  /**
17266  * marked - a markdown parser
17267  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
17268  * https://github.com/chjj/marked
17269  */
17270
17271
17272 /**
17273  *
17274  * Roo.Markdown - is a very crude wrapper around marked..
17275  *
17276  * usage:
17277  * 
17278  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
17279  * 
17280  * Note: move the sample code to the bottom of this
17281  * file before uncommenting it.
17282  *
17283  */
17284
17285 Roo.Markdown = {};
17286 Roo.Markdown.toHtml = function(text) {
17287     
17288     var c = new Roo.Markdown.marked.setOptions({
17289             renderer: new Roo.Markdown.marked.Renderer(),
17290             gfm: true,
17291             tables: true,
17292             breaks: false,
17293             pedantic: false,
17294             sanitize: false,
17295             smartLists: true,
17296             smartypants: false
17297           });
17298     // A FEW HACKS!!?
17299     
17300     text = text.replace(/\\\n/g,' ');
17301     return Roo.Markdown.marked(text);
17302 };
17303 //
17304 // converter
17305 //
17306 // Wraps all "globals" so that the only thing
17307 // exposed is makeHtml().
17308 //
17309 (function() {
17310     
17311      /**
17312          * eval:var:escape
17313          * eval:var:unescape
17314          * eval:var:replace
17315          */
17316       
17317     /**
17318      * Helpers
17319      */
17320     
17321     var escape = function (html, encode) {
17322       return html
17323         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
17324         .replace(/</g, '&lt;')
17325         .replace(/>/g, '&gt;')
17326         .replace(/"/g, '&quot;')
17327         .replace(/'/g, '&#39;');
17328     }
17329     
17330     var unescape = function (html) {
17331         // explicitly match decimal, hex, and named HTML entities 
17332       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
17333         n = n.toLowerCase();
17334         if (n === 'colon') { return ':'; }
17335         if (n.charAt(0) === '#') {
17336           return n.charAt(1) === 'x'
17337             ? String.fromCharCode(parseInt(n.substring(2), 16))
17338             : String.fromCharCode(+n.substring(1));
17339         }
17340         return '';
17341       });
17342     }
17343     
17344     var replace = function (regex, opt) {
17345       regex = regex.source;
17346       opt = opt || '';
17347       return function self(name, val) {
17348         if (!name) { return new RegExp(regex, opt); }
17349         val = val.source || val;
17350         val = val.replace(/(^|[^\[])\^/g, '$1');
17351         regex = regex.replace(name, val);
17352         return self;
17353       };
17354     }
17355
17356
17357          /**
17358          * eval:var:noop
17359     */
17360     var noop = function () {}
17361     noop.exec = noop;
17362     
17363          /**
17364          * eval:var:merge
17365     */
17366     var merge = function (obj) {
17367       var i = 1
17368         , target
17369         , key;
17370     
17371       for (; i < arguments.length; i++) {
17372         target = arguments[i];
17373         for (key in target) {
17374           if (Object.prototype.hasOwnProperty.call(target, key)) {
17375             obj[key] = target[key];
17376           }
17377         }
17378       }
17379     
17380       return obj;
17381     }
17382     
17383     
17384     /**
17385      * Block-Level Grammar
17386      */
17387     
17388     
17389     
17390     
17391     var block = {
17392       newline: /^\n+/,
17393       code: /^( {4}[^\n]+\n*)+/,
17394       fences: noop,
17395       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
17396       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
17397       nptable: noop,
17398       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
17399       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
17400       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
17401       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
17402       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
17403       table: noop,
17404       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
17405       text: /^[^\n]+/
17406     };
17407     
17408     block.bullet = /(?:[*+-]|\d+\.)/;
17409     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
17410     block.item = replace(block.item, 'gm')
17411       (/bull/g, block.bullet)
17412       ();
17413     
17414     block.list = replace(block.list)
17415       (/bull/g, block.bullet)
17416       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
17417       ('def', '\\n+(?=' + block.def.source + ')')
17418       ();
17419     
17420     block.blockquote = replace(block.blockquote)
17421       ('def', block.def)
17422       ();
17423     
17424     block._tag = '(?!(?:'
17425       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
17426       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
17427       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
17428     
17429     block.html = replace(block.html)
17430       ('comment', /<!--[\s\S]*?-->/)
17431       ('closed', /<(tag)[\s\S]+?<\/\1>/)
17432       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
17433       (/tag/g, block._tag)
17434       ();
17435     
17436     block.paragraph = replace(block.paragraph)
17437       ('hr', block.hr)
17438       ('heading', block.heading)
17439       ('lheading', block.lheading)
17440       ('blockquote', block.blockquote)
17441       ('tag', '<' + block._tag)
17442       ('def', block.def)
17443       ();
17444     
17445     /**
17446      * Normal Block Grammar
17447      */
17448     
17449     block.normal = merge({}, block);
17450     
17451     /**
17452      * GFM Block Grammar
17453      */
17454     
17455     block.gfm = merge({}, block.normal, {
17456       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
17457       paragraph: /^/,
17458       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
17459     });
17460     
17461     block.gfm.paragraph = replace(block.paragraph)
17462       ('(?!', '(?!'
17463         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
17464         + block.list.source.replace('\\1', '\\3') + '|')
17465       ();
17466     
17467     /**
17468      * GFM + Tables Block Grammar
17469      */
17470     
17471     block.tables = merge({}, block.gfm, {
17472       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
17473       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
17474     });
17475     
17476     /**
17477      * Block Lexer
17478      */
17479     
17480     var Lexer = function (options) {
17481       this.tokens = [];
17482       this.tokens.links = {};
17483       this.options = options || marked.defaults;
17484       this.rules = block.normal;
17485     
17486       if (this.options.gfm) {
17487         if (this.options.tables) {
17488           this.rules = block.tables;
17489         } else {
17490           this.rules = block.gfm;
17491         }
17492       }
17493     }
17494     
17495     /**
17496      * Expose Block Rules
17497      */
17498     
17499     Lexer.rules = block;
17500     
17501     /**
17502      * Static Lex Method
17503      */
17504     
17505     Lexer.lex = function(src, options) {
17506       var lexer = new Lexer(options);
17507       return lexer.lex(src);
17508     };
17509     
17510     /**
17511      * Preprocessing
17512      */
17513     
17514     Lexer.prototype.lex = function(src) {
17515       src = src
17516         .replace(/\r\n|\r/g, '\n')
17517         .replace(/\t/g, '    ')
17518         .replace(/\u00a0/g, ' ')
17519         .replace(/\u2424/g, '\n');
17520     
17521       return this.token(src, true);
17522     };
17523     
17524     /**
17525      * Lexing
17526      */
17527     
17528     Lexer.prototype.token = function(src, top, bq) {
17529       var src = src.replace(/^ +$/gm, '')
17530         , next
17531         , loose
17532         , cap
17533         , bull
17534         , b
17535         , item
17536         , space
17537         , i
17538         , l;
17539     
17540       while (src) {
17541         // newline
17542         if (cap = this.rules.newline.exec(src)) {
17543           src = src.substring(cap[0].length);
17544           if (cap[0].length > 1) {
17545             this.tokens.push({
17546               type: 'space'
17547             });
17548           }
17549         }
17550     
17551         // code
17552         if (cap = this.rules.code.exec(src)) {
17553           src = src.substring(cap[0].length);
17554           cap = cap[0].replace(/^ {4}/gm, '');
17555           this.tokens.push({
17556             type: 'code',
17557             text: !this.options.pedantic
17558               ? cap.replace(/\n+$/, '')
17559               : cap
17560           });
17561           continue;
17562         }
17563     
17564         // fences (gfm)
17565         if (cap = this.rules.fences.exec(src)) {
17566           src = src.substring(cap[0].length);
17567           this.tokens.push({
17568             type: 'code',
17569             lang: cap[2],
17570             text: cap[3] || ''
17571           });
17572           continue;
17573         }
17574     
17575         // heading
17576         if (cap = this.rules.heading.exec(src)) {
17577           src = src.substring(cap[0].length);
17578           this.tokens.push({
17579             type: 'heading',
17580             depth: cap[1].length,
17581             text: cap[2]
17582           });
17583           continue;
17584         }
17585     
17586         // table no leading pipe (gfm)
17587         if (top && (cap = this.rules.nptable.exec(src))) {
17588           src = src.substring(cap[0].length);
17589     
17590           item = {
17591             type: 'table',
17592             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17593             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17594             cells: cap[3].replace(/\n$/, '').split('\n')
17595           };
17596     
17597           for (i = 0; i < item.align.length; i++) {
17598             if (/^ *-+: *$/.test(item.align[i])) {
17599               item.align[i] = 'right';
17600             } else if (/^ *:-+: *$/.test(item.align[i])) {
17601               item.align[i] = 'center';
17602             } else if (/^ *:-+ *$/.test(item.align[i])) {
17603               item.align[i] = 'left';
17604             } else {
17605               item.align[i] = null;
17606             }
17607           }
17608     
17609           for (i = 0; i < item.cells.length; i++) {
17610             item.cells[i] = item.cells[i].split(/ *\| */);
17611           }
17612     
17613           this.tokens.push(item);
17614     
17615           continue;
17616         }
17617     
17618         // lheading
17619         if (cap = this.rules.lheading.exec(src)) {
17620           src = src.substring(cap[0].length);
17621           this.tokens.push({
17622             type: 'heading',
17623             depth: cap[2] === '=' ? 1 : 2,
17624             text: cap[1]
17625           });
17626           continue;
17627         }
17628     
17629         // hr
17630         if (cap = this.rules.hr.exec(src)) {
17631           src = src.substring(cap[0].length);
17632           this.tokens.push({
17633             type: 'hr'
17634           });
17635           continue;
17636         }
17637     
17638         // blockquote
17639         if (cap = this.rules.blockquote.exec(src)) {
17640           src = src.substring(cap[0].length);
17641     
17642           this.tokens.push({
17643             type: 'blockquote_start'
17644           });
17645     
17646           cap = cap[0].replace(/^ *> ?/gm, '');
17647     
17648           // Pass `top` to keep the current
17649           // "toplevel" state. This is exactly
17650           // how markdown.pl works.
17651           this.token(cap, top, true);
17652     
17653           this.tokens.push({
17654             type: 'blockquote_end'
17655           });
17656     
17657           continue;
17658         }
17659     
17660         // list
17661         if (cap = this.rules.list.exec(src)) {
17662           src = src.substring(cap[0].length);
17663           bull = cap[2];
17664     
17665           this.tokens.push({
17666             type: 'list_start',
17667             ordered: bull.length > 1
17668           });
17669     
17670           // Get each top-level item.
17671           cap = cap[0].match(this.rules.item);
17672     
17673           next = false;
17674           l = cap.length;
17675           i = 0;
17676     
17677           for (; i < l; i++) {
17678             item = cap[i];
17679     
17680             // Remove the list item's bullet
17681             // so it is seen as the next token.
17682             space = item.length;
17683             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
17684     
17685             // Outdent whatever the
17686             // list item contains. Hacky.
17687             if (~item.indexOf('\n ')) {
17688               space -= item.length;
17689               item = !this.options.pedantic
17690                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
17691                 : item.replace(/^ {1,4}/gm, '');
17692             }
17693     
17694             // Determine whether the next list item belongs here.
17695             // Backpedal if it does not belong in this list.
17696             if (this.options.smartLists && i !== l - 1) {
17697               b = block.bullet.exec(cap[i + 1])[0];
17698               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
17699                 src = cap.slice(i + 1).join('\n') + src;
17700                 i = l - 1;
17701               }
17702             }
17703     
17704             // Determine whether item is loose or not.
17705             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
17706             // for discount behavior.
17707             loose = next || /\n\n(?!\s*$)/.test(item);
17708             if (i !== l - 1) {
17709               next = item.charAt(item.length - 1) === '\n';
17710               if (!loose) { loose = next; }
17711             }
17712     
17713             this.tokens.push({
17714               type: loose
17715                 ? 'loose_item_start'
17716                 : 'list_item_start'
17717             });
17718     
17719             // Recurse.
17720             this.token(item, false, bq);
17721     
17722             this.tokens.push({
17723               type: 'list_item_end'
17724             });
17725           }
17726     
17727           this.tokens.push({
17728             type: 'list_end'
17729           });
17730     
17731           continue;
17732         }
17733     
17734         // html
17735         if (cap = this.rules.html.exec(src)) {
17736           src = src.substring(cap[0].length);
17737           this.tokens.push({
17738             type: this.options.sanitize
17739               ? 'paragraph'
17740               : 'html',
17741             pre: !this.options.sanitizer
17742               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
17743             text: cap[0]
17744           });
17745           continue;
17746         }
17747     
17748         // def
17749         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
17750           src = src.substring(cap[0].length);
17751           this.tokens.links[cap[1].toLowerCase()] = {
17752             href: cap[2],
17753             title: cap[3]
17754           };
17755           continue;
17756         }
17757     
17758         // table (gfm)
17759         if (top && (cap = this.rules.table.exec(src))) {
17760           src = src.substring(cap[0].length);
17761     
17762           item = {
17763             type: 'table',
17764             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17765             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17766             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
17767           };
17768     
17769           for (i = 0; i < item.align.length; i++) {
17770             if (/^ *-+: *$/.test(item.align[i])) {
17771               item.align[i] = 'right';
17772             } else if (/^ *:-+: *$/.test(item.align[i])) {
17773               item.align[i] = 'center';
17774             } else if (/^ *:-+ *$/.test(item.align[i])) {
17775               item.align[i] = 'left';
17776             } else {
17777               item.align[i] = null;
17778             }
17779           }
17780     
17781           for (i = 0; i < item.cells.length; i++) {
17782             item.cells[i] = item.cells[i]
17783               .replace(/^ *\| *| *\| *$/g, '')
17784               .split(/ *\| */);
17785           }
17786     
17787           this.tokens.push(item);
17788     
17789           continue;
17790         }
17791     
17792         // top-level paragraph
17793         if (top && (cap = this.rules.paragraph.exec(src))) {
17794           src = src.substring(cap[0].length);
17795           this.tokens.push({
17796             type: 'paragraph',
17797             text: cap[1].charAt(cap[1].length - 1) === '\n'
17798               ? cap[1].slice(0, -1)
17799               : cap[1]
17800           });
17801           continue;
17802         }
17803     
17804         // text
17805         if (cap = this.rules.text.exec(src)) {
17806           // Top-level should never reach here.
17807           src = src.substring(cap[0].length);
17808           this.tokens.push({
17809             type: 'text',
17810             text: cap[0]
17811           });
17812           continue;
17813         }
17814     
17815         if (src) {
17816           throw new
17817             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17818         }
17819       }
17820     
17821       return this.tokens;
17822     };
17823     
17824     /**
17825      * Inline-Level Grammar
17826      */
17827     
17828     var inline = {
17829       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
17830       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
17831       url: noop,
17832       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
17833       link: /^!?\[(inside)\]\(href\)/,
17834       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
17835       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
17836       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
17837       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
17838       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
17839       br: /^ {2,}\n(?!\s*$)/,
17840       del: noop,
17841       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
17842     };
17843     
17844     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
17845     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
17846     
17847     inline.link = replace(inline.link)
17848       ('inside', inline._inside)
17849       ('href', inline._href)
17850       ();
17851     
17852     inline.reflink = replace(inline.reflink)
17853       ('inside', inline._inside)
17854       ();
17855     
17856     /**
17857      * Normal Inline Grammar
17858      */
17859     
17860     inline.normal = merge({}, inline);
17861     
17862     /**
17863      * Pedantic Inline Grammar
17864      */
17865     
17866     inline.pedantic = merge({}, inline.normal, {
17867       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
17868       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
17869     });
17870     
17871     /**
17872      * GFM Inline Grammar
17873      */
17874     
17875     inline.gfm = merge({}, inline.normal, {
17876       escape: replace(inline.escape)('])', '~|])')(),
17877       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
17878       del: /^~~(?=\S)([\s\S]*?\S)~~/,
17879       text: replace(inline.text)
17880         (']|', '~]|')
17881         ('|', '|https?://|')
17882         ()
17883     });
17884     
17885     /**
17886      * GFM + Line Breaks Inline Grammar
17887      */
17888     
17889     inline.breaks = merge({}, inline.gfm, {
17890       br: replace(inline.br)('{2,}', '*')(),
17891       text: replace(inline.gfm.text)('{2,}', '*')()
17892     });
17893     
17894     /**
17895      * Inline Lexer & Compiler
17896      */
17897     
17898     var InlineLexer  = function (links, options) {
17899       this.options = options || marked.defaults;
17900       this.links = links;
17901       this.rules = inline.normal;
17902       this.renderer = this.options.renderer || new Renderer;
17903       this.renderer.options = this.options;
17904     
17905       if (!this.links) {
17906         throw new
17907           Error('Tokens array requires a `links` property.');
17908       }
17909     
17910       if (this.options.gfm) {
17911         if (this.options.breaks) {
17912           this.rules = inline.breaks;
17913         } else {
17914           this.rules = inline.gfm;
17915         }
17916       } else if (this.options.pedantic) {
17917         this.rules = inline.pedantic;
17918       }
17919     }
17920     
17921     /**
17922      * Expose Inline Rules
17923      */
17924     
17925     InlineLexer.rules = inline;
17926     
17927     /**
17928      * Static Lexing/Compiling Method
17929      */
17930     
17931     InlineLexer.output = function(src, links, options) {
17932       var inline = new InlineLexer(links, options);
17933       return inline.output(src);
17934     };
17935     
17936     /**
17937      * Lexing/Compiling
17938      */
17939     
17940     InlineLexer.prototype.output = function(src) {
17941       var out = ''
17942         , link
17943         , text
17944         , href
17945         , cap;
17946     
17947       while (src) {
17948         // escape
17949         if (cap = this.rules.escape.exec(src)) {
17950           src = src.substring(cap[0].length);
17951           out += cap[1];
17952           continue;
17953         }
17954     
17955         // autolink
17956         if (cap = this.rules.autolink.exec(src)) {
17957           src = src.substring(cap[0].length);
17958           if (cap[2] === '@') {
17959             text = cap[1].charAt(6) === ':'
17960               ? this.mangle(cap[1].substring(7))
17961               : this.mangle(cap[1]);
17962             href = this.mangle('mailto:') + text;
17963           } else {
17964             text = escape(cap[1]);
17965             href = text;
17966           }
17967           out += this.renderer.link(href, null, text);
17968           continue;
17969         }
17970     
17971         // url (gfm)
17972         if (!this.inLink && (cap = this.rules.url.exec(src))) {
17973           src = src.substring(cap[0].length);
17974           text = escape(cap[1]);
17975           href = text;
17976           out += this.renderer.link(href, null, text);
17977           continue;
17978         }
17979     
17980         // tag
17981         if (cap = this.rules.tag.exec(src)) {
17982           if (!this.inLink && /^<a /i.test(cap[0])) {
17983             this.inLink = true;
17984           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
17985             this.inLink = false;
17986           }
17987           src = src.substring(cap[0].length);
17988           out += this.options.sanitize
17989             ? this.options.sanitizer
17990               ? this.options.sanitizer(cap[0])
17991               : escape(cap[0])
17992             : cap[0];
17993           continue;
17994         }
17995     
17996         // link
17997         if (cap = this.rules.link.exec(src)) {
17998           src = src.substring(cap[0].length);
17999           this.inLink = true;
18000           out += this.outputLink(cap, {
18001             href: cap[2],
18002             title: cap[3]
18003           });
18004           this.inLink = false;
18005           continue;
18006         }
18007     
18008         // reflink, nolink
18009         if ((cap = this.rules.reflink.exec(src))
18010             || (cap = this.rules.nolink.exec(src))) {
18011           src = src.substring(cap[0].length);
18012           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
18013           link = this.links[link.toLowerCase()];
18014           if (!link || !link.href) {
18015             out += cap[0].charAt(0);
18016             src = cap[0].substring(1) + src;
18017             continue;
18018           }
18019           this.inLink = true;
18020           out += this.outputLink(cap, link);
18021           this.inLink = false;
18022           continue;
18023         }
18024     
18025         // strong
18026         if (cap = this.rules.strong.exec(src)) {
18027           src = src.substring(cap[0].length);
18028           out += this.renderer.strong(this.output(cap[2] || cap[1]));
18029           continue;
18030         }
18031     
18032         // em
18033         if (cap = this.rules.em.exec(src)) {
18034           src = src.substring(cap[0].length);
18035           out += this.renderer.em(this.output(cap[2] || cap[1]));
18036           continue;
18037         }
18038     
18039         // code
18040         if (cap = this.rules.code.exec(src)) {
18041           src = src.substring(cap[0].length);
18042           out += this.renderer.codespan(escape(cap[2], true));
18043           continue;
18044         }
18045     
18046         // br
18047         if (cap = this.rules.br.exec(src)) {
18048           src = src.substring(cap[0].length);
18049           out += this.renderer.br();
18050           continue;
18051         }
18052     
18053         // del (gfm)
18054         if (cap = this.rules.del.exec(src)) {
18055           src = src.substring(cap[0].length);
18056           out += this.renderer.del(this.output(cap[1]));
18057           continue;
18058         }
18059     
18060         // text
18061         if (cap = this.rules.text.exec(src)) {
18062           src = src.substring(cap[0].length);
18063           out += this.renderer.text(escape(this.smartypants(cap[0])));
18064           continue;
18065         }
18066     
18067         if (src) {
18068           throw new
18069             Error('Infinite loop on byte: ' + src.charCodeAt(0));
18070         }
18071       }
18072     
18073       return out;
18074     };
18075     
18076     /**
18077      * Compile Link
18078      */
18079     
18080     InlineLexer.prototype.outputLink = function(cap, link) {
18081       var href = escape(link.href)
18082         , title = link.title ? escape(link.title) : null;
18083     
18084       return cap[0].charAt(0) !== '!'
18085         ? this.renderer.link(href, title, this.output(cap[1]))
18086         : this.renderer.image(href, title, escape(cap[1]));
18087     };
18088     
18089     /**
18090      * Smartypants Transformations
18091      */
18092     
18093     InlineLexer.prototype.smartypants = function(text) {
18094       if (!this.options.smartypants)  { return text; }
18095       return text
18096         // em-dashes
18097         .replace(/---/g, '\u2014')
18098         // en-dashes
18099         .replace(/--/g, '\u2013')
18100         // opening singles
18101         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
18102         // closing singles & apostrophes
18103         .replace(/'/g, '\u2019')
18104         // opening doubles
18105         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
18106         // closing doubles
18107         .replace(/"/g, '\u201d')
18108         // ellipses
18109         .replace(/\.{3}/g, '\u2026');
18110     };
18111     
18112     /**
18113      * Mangle Links
18114      */
18115     
18116     InlineLexer.prototype.mangle = function(text) {
18117       if (!this.options.mangle) { return text; }
18118       var out = ''
18119         , l = text.length
18120         , i = 0
18121         , ch;
18122     
18123       for (; i < l; i++) {
18124         ch = text.charCodeAt(i);
18125         if (Math.random() > 0.5) {
18126           ch = 'x' + ch.toString(16);
18127         }
18128         out += '&#' + ch + ';';
18129       }
18130     
18131       return out;
18132     };
18133     
18134     /**
18135      * Renderer
18136      */
18137     
18138      /**
18139          * eval:var:Renderer
18140     */
18141     
18142     var Renderer   = function (options) {
18143       this.options = options || {};
18144     }
18145     
18146     Renderer.prototype.code = function(code, lang, escaped) {
18147       if (this.options.highlight) {
18148         var out = this.options.highlight(code, lang);
18149         if (out != null && out !== code) {
18150           escaped = true;
18151           code = out;
18152         }
18153       } else {
18154             // hack!!! - it's already escapeD?
18155             escaped = true;
18156       }
18157     
18158       if (!lang) {
18159         return '<pre><code>'
18160           + (escaped ? code : escape(code, true))
18161           + '\n</code></pre>';
18162       }
18163     
18164       return '<pre><code class="'
18165         + this.options.langPrefix
18166         + escape(lang, true)
18167         + '">'
18168         + (escaped ? code : escape(code, true))
18169         + '\n</code></pre>\n';
18170     };
18171     
18172     Renderer.prototype.blockquote = function(quote) {
18173       return '<blockquote>\n' + quote + '</blockquote>\n';
18174     };
18175     
18176     Renderer.prototype.html = function(html) {
18177       return html;
18178     };
18179     
18180     Renderer.prototype.heading = function(text, level, raw) {
18181       return '<h'
18182         + level
18183         + ' id="'
18184         + this.options.headerPrefix
18185         + raw.toLowerCase().replace(/[^\w]+/g, '-')
18186         + '">'
18187         + text
18188         + '</h'
18189         + level
18190         + '>\n';
18191     };
18192     
18193     Renderer.prototype.hr = function() {
18194       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
18195     };
18196     
18197     Renderer.prototype.list = function(body, ordered) {
18198       var type = ordered ? 'ol' : 'ul';
18199       return '<' + type + '>\n' + body + '</' + type + '>\n';
18200     };
18201     
18202     Renderer.prototype.listitem = function(text) {
18203       return '<li>' + text + '</li>\n';
18204     };
18205     
18206     Renderer.prototype.paragraph = function(text) {
18207       return '<p>' + text + '</p>\n';
18208     };
18209     
18210     Renderer.prototype.table = function(header, body) {
18211       return '<table class="table table-striped">\n'
18212         + '<thead>\n'
18213         + header
18214         + '</thead>\n'
18215         + '<tbody>\n'
18216         + body
18217         + '</tbody>\n'
18218         + '</table>\n';
18219     };
18220     
18221     Renderer.prototype.tablerow = function(content) {
18222       return '<tr>\n' + content + '</tr>\n';
18223     };
18224     
18225     Renderer.prototype.tablecell = function(content, flags) {
18226       var type = flags.header ? 'th' : 'td';
18227       var tag = flags.align
18228         ? '<' + type + ' style="text-align:' + flags.align + '">'
18229         : '<' + type + '>';
18230       return tag + content + '</' + type + '>\n';
18231     };
18232     
18233     // span level renderer
18234     Renderer.prototype.strong = function(text) {
18235       return '<strong>' + text + '</strong>';
18236     };
18237     
18238     Renderer.prototype.em = function(text) {
18239       return '<em>' + text + '</em>';
18240     };
18241     
18242     Renderer.prototype.codespan = function(text) {
18243       return '<code>' + text + '</code>';
18244     };
18245     
18246     Renderer.prototype.br = function() {
18247       return this.options.xhtml ? '<br/>' : '<br>';
18248     };
18249     
18250     Renderer.prototype.del = function(text) {
18251       return '<del>' + text + '</del>';
18252     };
18253     
18254     Renderer.prototype.link = function(href, title, text) {
18255       if (this.options.sanitize) {
18256         try {
18257           var prot = decodeURIComponent(unescape(href))
18258             .replace(/[^\w:]/g, '')
18259             .toLowerCase();
18260         } catch (e) {
18261           return '';
18262         }
18263         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
18264           return '';
18265         }
18266       }
18267       var out = '<a href="' + href + '"';
18268       if (title) {
18269         out += ' title="' + title + '"';
18270       }
18271       out += '>' + text + '</a>';
18272       return out;
18273     };
18274     
18275     Renderer.prototype.image = function(href, title, text) {
18276       var out = '<img src="' + href + '" alt="' + text + '"';
18277       if (title) {
18278         out += ' title="' + title + '"';
18279       }
18280       out += this.options.xhtml ? '/>' : '>';
18281       return out;
18282     };
18283     
18284     Renderer.prototype.text = function(text) {
18285       return text;
18286     };
18287     
18288     /**
18289      * Parsing & Compiling
18290      */
18291          /**
18292          * eval:var:Parser
18293     */
18294     
18295     var Parser= function (options) {
18296       this.tokens = [];
18297       this.token = null;
18298       this.options = options || marked.defaults;
18299       this.options.renderer = this.options.renderer || new Renderer;
18300       this.renderer = this.options.renderer;
18301       this.renderer.options = this.options;
18302     }
18303     
18304     /**
18305      * Static Parse Method
18306      */
18307     
18308     Parser.parse = function(src, options, renderer) {
18309       var parser = new Parser(options, renderer);
18310       return parser.parse(src);
18311     };
18312     
18313     /**
18314      * Parse Loop
18315      */
18316     
18317     Parser.prototype.parse = function(src) {
18318       this.inline = new InlineLexer(src.links, this.options, this.renderer);
18319       this.tokens = src.reverse();
18320     
18321       var out = '';
18322       while (this.next()) {
18323         out += this.tok();
18324       }
18325     
18326       return out;
18327     };
18328     
18329     /**
18330      * Next Token
18331      */
18332     
18333     Parser.prototype.next = function() {
18334       return this.token = this.tokens.pop();
18335     };
18336     
18337     /**
18338      * Preview Next Token
18339      */
18340     
18341     Parser.prototype.peek = function() {
18342       return this.tokens[this.tokens.length - 1] || 0;
18343     };
18344     
18345     /**
18346      * Parse Text Tokens
18347      */
18348     
18349     Parser.prototype.parseText = function() {
18350       var body = this.token.text;
18351     
18352       while (this.peek().type === 'text') {
18353         body += '\n' + this.next().text;
18354       }
18355     
18356       return this.inline.output(body);
18357     };
18358     
18359     /**
18360      * Parse Current Token
18361      */
18362     
18363     Parser.prototype.tok = function() {
18364       switch (this.token.type) {
18365         case 'space': {
18366           return '';
18367         }
18368         case 'hr': {
18369           return this.renderer.hr();
18370         }
18371         case 'heading': {
18372           return this.renderer.heading(
18373             this.inline.output(this.token.text),
18374             this.token.depth,
18375             this.token.text);
18376         }
18377         case 'code': {
18378           return this.renderer.code(this.token.text,
18379             this.token.lang,
18380             this.token.escaped);
18381         }
18382         case 'table': {
18383           var header = ''
18384             , body = ''
18385             , i
18386             , row
18387             , cell
18388             , flags
18389             , j;
18390     
18391           // header
18392           cell = '';
18393           for (i = 0; i < this.token.header.length; i++) {
18394             flags = { header: true, align: this.token.align[i] };
18395             cell += this.renderer.tablecell(
18396               this.inline.output(this.token.header[i]),
18397               { header: true, align: this.token.align[i] }
18398             );
18399           }
18400           header += this.renderer.tablerow(cell);
18401     
18402           for (i = 0; i < this.token.cells.length; i++) {
18403             row = this.token.cells[i];
18404     
18405             cell = '';
18406             for (j = 0; j < row.length; j++) {
18407               cell += this.renderer.tablecell(
18408                 this.inline.output(row[j]),
18409                 { header: false, align: this.token.align[j] }
18410               );
18411             }
18412     
18413             body += this.renderer.tablerow(cell);
18414           }
18415           return this.renderer.table(header, body);
18416         }
18417         case 'blockquote_start': {
18418           var body = '';
18419     
18420           while (this.next().type !== 'blockquote_end') {
18421             body += this.tok();
18422           }
18423     
18424           return this.renderer.blockquote(body);
18425         }
18426         case 'list_start': {
18427           var body = ''
18428             , ordered = this.token.ordered;
18429     
18430           while (this.next().type !== 'list_end') {
18431             body += this.tok();
18432           }
18433     
18434           return this.renderer.list(body, ordered);
18435         }
18436         case 'list_item_start': {
18437           var body = '';
18438     
18439           while (this.next().type !== 'list_item_end') {
18440             body += this.token.type === 'text'
18441               ? this.parseText()
18442               : this.tok();
18443           }
18444     
18445           return this.renderer.listitem(body);
18446         }
18447         case 'loose_item_start': {
18448           var body = '';
18449     
18450           while (this.next().type !== 'list_item_end') {
18451             body += this.tok();
18452           }
18453     
18454           return this.renderer.listitem(body);
18455         }
18456         case 'html': {
18457           var html = !this.token.pre && !this.options.pedantic
18458             ? this.inline.output(this.token.text)
18459             : this.token.text;
18460           return this.renderer.html(html);
18461         }
18462         case 'paragraph': {
18463           return this.renderer.paragraph(this.inline.output(this.token.text));
18464         }
18465         case 'text': {
18466           return this.renderer.paragraph(this.parseText());
18467         }
18468       }
18469     };
18470   
18471     
18472     /**
18473      * Marked
18474      */
18475          /**
18476          * eval:var:marked
18477     */
18478     var marked = function (src, opt, callback) {
18479       if (callback || typeof opt === 'function') {
18480         if (!callback) {
18481           callback = opt;
18482           opt = null;
18483         }
18484     
18485         opt = merge({}, marked.defaults, opt || {});
18486     
18487         var highlight = opt.highlight
18488           , tokens
18489           , pending
18490           , i = 0;
18491     
18492         try {
18493           tokens = Lexer.lex(src, opt)
18494         } catch (e) {
18495           return callback(e);
18496         }
18497     
18498         pending = tokens.length;
18499          /**
18500          * eval:var:done
18501     */
18502         var done = function(err) {
18503           if (err) {
18504             opt.highlight = highlight;
18505             return callback(err);
18506           }
18507     
18508           var out;
18509     
18510           try {
18511             out = Parser.parse(tokens, opt);
18512           } catch (e) {
18513             err = e;
18514           }
18515     
18516           opt.highlight = highlight;
18517     
18518           return err
18519             ? callback(err)
18520             : callback(null, out);
18521         };
18522     
18523         if (!highlight || highlight.length < 3) {
18524           return done();
18525         }
18526     
18527         delete opt.highlight;
18528     
18529         if (!pending) { return done(); }
18530     
18531         for (; i < tokens.length; i++) {
18532           (function(token) {
18533             if (token.type !== 'code') {
18534               return --pending || done();
18535             }
18536             return highlight(token.text, token.lang, function(err, code) {
18537               if (err) { return done(err); }
18538               if (code == null || code === token.text) {
18539                 return --pending || done();
18540               }
18541               token.text = code;
18542               token.escaped = true;
18543               --pending || done();
18544             });
18545           })(tokens[i]);
18546         }
18547     
18548         return;
18549       }
18550       try {
18551         if (opt) { opt = merge({}, marked.defaults, opt); }
18552         return Parser.parse(Lexer.lex(src, opt), opt);
18553       } catch (e) {
18554         e.message += '\nPlease report this to https://github.com/chjj/marked.';
18555         if ((opt || marked.defaults).silent) {
18556           return '<p>An error occured:</p><pre>'
18557             + escape(e.message + '', true)
18558             + '</pre>';
18559         }
18560         throw e;
18561       }
18562     }
18563     
18564     /**
18565      * Options
18566      */
18567     
18568     marked.options =
18569     marked.setOptions = function(opt) {
18570       merge(marked.defaults, opt);
18571       return marked;
18572     };
18573     
18574     marked.defaults = {
18575       gfm: true,
18576       tables: true,
18577       breaks: false,
18578       pedantic: false,
18579       sanitize: false,
18580       sanitizer: null,
18581       mangle: true,
18582       smartLists: false,
18583       silent: false,
18584       highlight: null,
18585       langPrefix: 'lang-',
18586       smartypants: false,
18587       headerPrefix: '',
18588       renderer: new Renderer,
18589       xhtml: false
18590     };
18591     
18592     /**
18593      * Expose
18594      */
18595     
18596     marked.Parser = Parser;
18597     marked.parser = Parser.parse;
18598     
18599     marked.Renderer = Renderer;
18600     
18601     marked.Lexer = Lexer;
18602     marked.lexer = Lexer.lex;
18603     
18604     marked.InlineLexer = InlineLexer;
18605     marked.inlineLexer = InlineLexer.output;
18606     
18607     marked.parse = marked;
18608     
18609     Roo.Markdown.marked = marked;
18610
18611 })();/*
18612  * Based on:
18613  * Ext JS Library 1.1.1
18614  * Copyright(c) 2006-2007, Ext JS, LLC.
18615  *
18616  * Originally Released Under LGPL - original licence link has changed is not relivant.
18617  *
18618  * Fork - LGPL
18619  * <script type="text/javascript">
18620  */
18621
18622
18623
18624 /*
18625  * These classes are derivatives of the similarly named classes in the YUI Library.
18626  * The original license:
18627  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18628  * Code licensed under the BSD License:
18629  * http://developer.yahoo.net/yui/license.txt
18630  */
18631
18632 (function() {
18633
18634 var Event=Roo.EventManager;
18635 var Dom=Roo.lib.Dom;
18636
18637 /**
18638  * @class Roo.dd.DragDrop
18639  * @extends Roo.util.Observable
18640  * Defines the interface and base operation of items that that can be
18641  * dragged or can be drop targets.  It was designed to be extended, overriding
18642  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
18643  * Up to three html elements can be associated with a DragDrop instance:
18644  * <ul>
18645  * <li>linked element: the element that is passed into the constructor.
18646  * This is the element which defines the boundaries for interaction with
18647  * other DragDrop objects.</li>
18648  * <li>handle element(s): The drag operation only occurs if the element that
18649  * was clicked matches a handle element.  By default this is the linked
18650  * element, but there are times that you will want only a portion of the
18651  * linked element to initiate the drag operation, and the setHandleElId()
18652  * method provides a way to define this.</li>
18653  * <li>drag element: this represents the element that would be moved along
18654  * with the cursor during a drag operation.  By default, this is the linked
18655  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
18656  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
18657  * </li>
18658  * </ul>
18659  * This class should not be instantiated until the onload event to ensure that
18660  * the associated elements are available.
18661  * The following would define a DragDrop obj that would interact with any
18662  * other DragDrop obj in the "group1" group:
18663  * <pre>
18664  *  dd = new Roo.dd.DragDrop("div1", "group1");
18665  * </pre>
18666  * Since none of the event handlers have been implemented, nothing would
18667  * actually happen if you were to run the code above.  Normally you would
18668  * override this class or one of the default implementations, but you can
18669  * also override the methods you want on an instance of the class...
18670  * <pre>
18671  *  dd.onDragDrop = function(e, id) {
18672  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
18673  *  }
18674  * </pre>
18675  * @constructor
18676  * @param {String} id of the element that is linked to this instance
18677  * @param {String} sGroup the group of related DragDrop objects
18678  * @param {object} config an object containing configurable attributes
18679  *                Valid properties for DragDrop:
18680  *                    padding, isTarget, maintainOffset, primaryButtonOnly
18681  */
18682 Roo.dd.DragDrop = function(id, sGroup, config) {
18683     if (id) {
18684         this.init(id, sGroup, config);
18685     }
18686     
18687 };
18688
18689 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
18690
18691     /**
18692      * The id of the element associated with this object.  This is what we
18693      * refer to as the "linked element" because the size and position of
18694      * this element is used to determine when the drag and drop objects have
18695      * interacted.
18696      * @property id
18697      * @type String
18698      */
18699     id: null,
18700
18701     /**
18702      * Configuration attributes passed into the constructor
18703      * @property config
18704      * @type object
18705      */
18706     config: null,
18707
18708     /**
18709      * The id of the element that will be dragged.  By default this is same
18710      * as the linked element , but could be changed to another element. Ex:
18711      * Roo.dd.DDProxy
18712      * @property dragElId
18713      * @type String
18714      * @private
18715      */
18716     dragElId: null,
18717
18718     /**
18719      * the id of the element that initiates the drag operation.  By default
18720      * this is the linked element, but could be changed to be a child of this
18721      * element.  This lets us do things like only starting the drag when the
18722      * header element within the linked html element is clicked.
18723      * @property handleElId
18724      * @type String
18725      * @private
18726      */
18727     handleElId: null,
18728
18729     /**
18730      * An associative array of HTML tags that will be ignored if clicked.
18731      * @property invalidHandleTypes
18732      * @type {string: string}
18733      */
18734     invalidHandleTypes: null,
18735
18736     /**
18737      * An associative array of ids for elements that will be ignored if clicked
18738      * @property invalidHandleIds
18739      * @type {string: string}
18740      */
18741     invalidHandleIds: null,
18742
18743     /**
18744      * An indexted array of css class names for elements that will be ignored
18745      * if clicked.
18746      * @property invalidHandleClasses
18747      * @type string[]
18748      */
18749     invalidHandleClasses: null,
18750
18751     /**
18752      * The linked element's absolute X position at the time the drag was
18753      * started
18754      * @property startPageX
18755      * @type int
18756      * @private
18757      */
18758     startPageX: 0,
18759
18760     /**
18761      * The linked element's absolute X position at the time the drag was
18762      * started
18763      * @property startPageY
18764      * @type int
18765      * @private
18766      */
18767     startPageY: 0,
18768
18769     /**
18770      * The group defines a logical collection of DragDrop objects that are
18771      * related.  Instances only get events when interacting with other
18772      * DragDrop object in the same group.  This lets us define multiple
18773      * groups using a single DragDrop subclass if we want.
18774      * @property groups
18775      * @type {string: string}
18776      */
18777     groups: null,
18778
18779     /**
18780      * Individual drag/drop instances can be locked.  This will prevent
18781      * onmousedown start drag.
18782      * @property locked
18783      * @type boolean
18784      * @private
18785      */
18786     locked: false,
18787
18788     /**
18789      * Lock this instance
18790      * @method lock
18791      */
18792     lock: function() { this.locked = true; },
18793
18794     /**
18795      * Unlock this instace
18796      * @method unlock
18797      */
18798     unlock: function() { this.locked = false; },
18799
18800     /**
18801      * By default, all insances can be a drop target.  This can be disabled by
18802      * setting isTarget to false.
18803      * @method isTarget
18804      * @type boolean
18805      */
18806     isTarget: true,
18807
18808     /**
18809      * The padding configured for this drag and drop object for calculating
18810      * the drop zone intersection with this object.
18811      * @method padding
18812      * @type int[]
18813      */
18814     padding: null,
18815
18816     /**
18817      * Cached reference to the linked element
18818      * @property _domRef
18819      * @private
18820      */
18821     _domRef: null,
18822
18823     /**
18824      * Internal typeof flag
18825      * @property __ygDragDrop
18826      * @private
18827      */
18828     __ygDragDrop: true,
18829
18830     /**
18831      * Set to true when horizontal contraints are applied
18832      * @property constrainX
18833      * @type boolean
18834      * @private
18835      */
18836     constrainX: false,
18837
18838     /**
18839      * Set to true when vertical contraints are applied
18840      * @property constrainY
18841      * @type boolean
18842      * @private
18843      */
18844     constrainY: false,
18845
18846     /**
18847      * The left constraint
18848      * @property minX
18849      * @type int
18850      * @private
18851      */
18852     minX: 0,
18853
18854     /**
18855      * The right constraint
18856      * @property maxX
18857      * @type int
18858      * @private
18859      */
18860     maxX: 0,
18861
18862     /**
18863      * The up constraint
18864      * @property minY
18865      * @type int
18866      * @type int
18867      * @private
18868      */
18869     minY: 0,
18870
18871     /**
18872      * The down constraint
18873      * @property maxY
18874      * @type int
18875      * @private
18876      */
18877     maxY: 0,
18878
18879     /**
18880      * Maintain offsets when we resetconstraints.  Set to true when you want
18881      * the position of the element relative to its parent to stay the same
18882      * when the page changes
18883      *
18884      * @property maintainOffset
18885      * @type boolean
18886      */
18887     maintainOffset: false,
18888
18889     /**
18890      * Array of pixel locations the element will snap to if we specified a
18891      * horizontal graduation/interval.  This array is generated automatically
18892      * when you define a tick interval.
18893      * @property xTicks
18894      * @type int[]
18895      */
18896     xTicks: null,
18897
18898     /**
18899      * Array of pixel locations the element will snap to if we specified a
18900      * vertical graduation/interval.  This array is generated automatically
18901      * when you define a tick interval.
18902      * @property yTicks
18903      * @type int[]
18904      */
18905     yTicks: null,
18906
18907     /**
18908      * By default the drag and drop instance will only respond to the primary
18909      * button click (left button for a right-handed mouse).  Set to true to
18910      * allow drag and drop to start with any mouse click that is propogated
18911      * by the browser
18912      * @property primaryButtonOnly
18913      * @type boolean
18914      */
18915     primaryButtonOnly: true,
18916
18917     /**
18918      * The availabe property is false until the linked dom element is accessible.
18919      * @property available
18920      * @type boolean
18921      */
18922     available: false,
18923
18924     /**
18925      * By default, drags can only be initiated if the mousedown occurs in the
18926      * region the linked element is.  This is done in part to work around a
18927      * bug in some browsers that mis-report the mousedown if the previous
18928      * mouseup happened outside of the window.  This property is set to true
18929      * if outer handles are defined.
18930      *
18931      * @property hasOuterHandles
18932      * @type boolean
18933      * @default false
18934      */
18935     hasOuterHandles: false,
18936
18937     /**
18938      * Code that executes immediately before the startDrag event
18939      * @method b4StartDrag
18940      * @private
18941      */
18942     b4StartDrag: function(x, y) { },
18943
18944     /**
18945      * Abstract method called after a drag/drop object is clicked
18946      * and the drag or mousedown time thresholds have beeen met.
18947      * @method startDrag
18948      * @param {int} X click location
18949      * @param {int} Y click location
18950      */
18951     startDrag: function(x, y) { /* override this */ },
18952
18953     /**
18954      * Code that executes immediately before the onDrag event
18955      * @method b4Drag
18956      * @private
18957      */
18958     b4Drag: function(e) { },
18959
18960     /**
18961      * Abstract method called during the onMouseMove event while dragging an
18962      * object.
18963      * @method onDrag
18964      * @param {Event} e the mousemove event
18965      */
18966     onDrag: function(e) { /* override this */ },
18967
18968     /**
18969      * Abstract method called when this element fist begins hovering over
18970      * another DragDrop obj
18971      * @method onDragEnter
18972      * @param {Event} e the mousemove event
18973      * @param {String|DragDrop[]} id In POINT mode, the element
18974      * id this is hovering over.  In INTERSECT mode, an array of one or more
18975      * dragdrop items being hovered over.
18976      */
18977     onDragEnter: function(e, id) { /* override this */ },
18978
18979     /**
18980      * Code that executes immediately before the onDragOver event
18981      * @method b4DragOver
18982      * @private
18983      */
18984     b4DragOver: function(e) { },
18985
18986     /**
18987      * Abstract method called when this element is hovering over another
18988      * DragDrop obj
18989      * @method onDragOver
18990      * @param {Event} e the mousemove event
18991      * @param {String|DragDrop[]} id In POINT mode, the element
18992      * id this is hovering over.  In INTERSECT mode, an array of dd items
18993      * being hovered over.
18994      */
18995     onDragOver: function(e, id) { /* override this */ },
18996
18997     /**
18998      * Code that executes immediately before the onDragOut event
18999      * @method b4DragOut
19000      * @private
19001      */
19002     b4DragOut: function(e) { },
19003
19004     /**
19005      * Abstract method called when we are no longer hovering over an element
19006      * @method onDragOut
19007      * @param {Event} e the mousemove event
19008      * @param {String|DragDrop[]} id In POINT mode, the element
19009      * id this was hovering over.  In INTERSECT mode, an array of dd items
19010      * that the mouse is no longer over.
19011      */
19012     onDragOut: function(e, id) { /* override this */ },
19013
19014     /**
19015      * Code that executes immediately before the onDragDrop event
19016      * @method b4DragDrop
19017      * @private
19018      */
19019     b4DragDrop: function(e) { },
19020
19021     /**
19022      * Abstract method called when this item is dropped on another DragDrop
19023      * obj
19024      * @method onDragDrop
19025      * @param {Event} e the mouseup event
19026      * @param {String|DragDrop[]} id In POINT mode, the element
19027      * id this was dropped on.  In INTERSECT mode, an array of dd items this
19028      * was dropped on.
19029      */
19030     onDragDrop: function(e, id) { /* override this */ },
19031
19032     /**
19033      * Abstract method called when this item is dropped on an area with no
19034      * drop target
19035      * @method onInvalidDrop
19036      * @param {Event} e the mouseup event
19037      */
19038     onInvalidDrop: function(e) { /* override this */ },
19039
19040     /**
19041      * Code that executes immediately before the endDrag event
19042      * @method b4EndDrag
19043      * @private
19044      */
19045     b4EndDrag: function(e) { },
19046
19047     /**
19048      * Fired when we are done dragging the object
19049      * @method endDrag
19050      * @param {Event} e the mouseup event
19051      */
19052     endDrag: function(e) { /* override this */ },
19053
19054     /**
19055      * Code executed immediately before the onMouseDown event
19056      * @method b4MouseDown
19057      * @param {Event} e the mousedown event
19058      * @private
19059      */
19060     b4MouseDown: function(e) {  },
19061
19062     /**
19063      * Event handler that fires when a drag/drop obj gets a mousedown
19064      * @method onMouseDown
19065      * @param {Event} e the mousedown event
19066      */
19067     onMouseDown: function(e) { /* override this */ },
19068
19069     /**
19070      * Event handler that fires when a drag/drop obj gets a mouseup
19071      * @method onMouseUp
19072      * @param {Event} e the mouseup event
19073      */
19074     onMouseUp: function(e) { /* override this */ },
19075
19076     /**
19077      * Override the onAvailable method to do what is needed after the initial
19078      * position was determined.
19079      * @method onAvailable
19080      */
19081     onAvailable: function () {
19082     },
19083
19084     /*
19085      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
19086      * @type Object
19087      */
19088     defaultPadding : {left:0, right:0, top:0, bottom:0},
19089
19090     /*
19091      * Initializes the drag drop object's constraints to restrict movement to a certain element.
19092  *
19093  * Usage:
19094  <pre><code>
19095  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
19096                 { dragElId: "existingProxyDiv" });
19097  dd.startDrag = function(){
19098      this.constrainTo("parent-id");
19099  };
19100  </code></pre>
19101  * Or you can initalize it using the {@link Roo.Element} object:
19102  <pre><code>
19103  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
19104      startDrag : function(){
19105          this.constrainTo("parent-id");
19106      }
19107  });
19108  </code></pre>
19109      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
19110      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
19111      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
19112      * an object containing the sides to pad. For example: {right:10, bottom:10}
19113      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
19114      */
19115     constrainTo : function(constrainTo, pad, inContent){
19116         if(typeof pad == "number"){
19117             pad = {left: pad, right:pad, top:pad, bottom:pad};
19118         }
19119         pad = pad || this.defaultPadding;
19120         var b = Roo.get(this.getEl()).getBox();
19121         var ce = Roo.get(constrainTo);
19122         var s = ce.getScroll();
19123         var c, cd = ce.dom;
19124         if(cd == document.body){
19125             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
19126         }else{
19127             xy = ce.getXY();
19128             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
19129         }
19130
19131
19132         var topSpace = b.y - c.y;
19133         var leftSpace = b.x - c.x;
19134
19135         this.resetConstraints();
19136         this.setXConstraint(leftSpace - (pad.left||0), // left
19137                 c.width - leftSpace - b.width - (pad.right||0) //right
19138         );
19139         this.setYConstraint(topSpace - (pad.top||0), //top
19140                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
19141         );
19142     },
19143
19144     /**
19145      * Returns a reference to the linked element
19146      * @method getEl
19147      * @return {HTMLElement} the html element
19148      */
19149     getEl: function() {
19150         if (!this._domRef) {
19151             this._domRef = Roo.getDom(this.id);
19152         }
19153
19154         return this._domRef;
19155     },
19156
19157     /**
19158      * Returns a reference to the actual element to drag.  By default this is
19159      * the same as the html element, but it can be assigned to another
19160      * element. An example of this can be found in Roo.dd.DDProxy
19161      * @method getDragEl
19162      * @return {HTMLElement} the html element
19163      */
19164     getDragEl: function() {
19165         return Roo.getDom(this.dragElId);
19166     },
19167
19168     /**
19169      * Sets up the DragDrop object.  Must be called in the constructor of any
19170      * Roo.dd.DragDrop subclass
19171      * @method init
19172      * @param id the id of the linked element
19173      * @param {String} sGroup the group of related items
19174      * @param {object} config configuration attributes
19175      */
19176     init: function(id, sGroup, config) {
19177         this.initTarget(id, sGroup, config);
19178         if (!Roo.isTouch) {
19179             Event.on(this.id, "mousedown", this.handleMouseDown, this);
19180         }
19181         Event.on(this.id, "touchstart", this.handleMouseDown, this);
19182         // Event.on(this.id, "selectstart", Event.preventDefault);
19183     },
19184
19185     /**
19186      * Initializes Targeting functionality only... the object does not
19187      * get a mousedown handler.
19188      * @method initTarget
19189      * @param id the id of the linked element
19190      * @param {String} sGroup the group of related items
19191      * @param {object} config configuration attributes
19192      */
19193     initTarget: function(id, sGroup, config) {
19194
19195         // configuration attributes
19196         this.config = config || {};
19197
19198         // create a local reference to the drag and drop manager
19199         this.DDM = Roo.dd.DDM;
19200         // initialize the groups array
19201         this.groups = {};
19202
19203         // assume that we have an element reference instead of an id if the
19204         // parameter is not a string
19205         if (typeof id !== "string") {
19206             id = Roo.id(id);
19207         }
19208
19209         // set the id
19210         this.id = id;
19211
19212         // add to an interaction group
19213         this.addToGroup((sGroup) ? sGroup : "default");
19214
19215         // We don't want to register this as the handle with the manager
19216         // so we just set the id rather than calling the setter.
19217         this.handleElId = id;
19218
19219         // the linked element is the element that gets dragged by default
19220         this.setDragElId(id);
19221
19222         // by default, clicked anchors will not start drag operations.
19223         this.invalidHandleTypes = { A: "A" };
19224         this.invalidHandleIds = {};
19225         this.invalidHandleClasses = [];
19226
19227         this.applyConfig();
19228
19229         this.handleOnAvailable();
19230     },
19231
19232     /**
19233      * Applies the configuration parameters that were passed into the constructor.
19234      * This is supposed to happen at each level through the inheritance chain.  So
19235      * a DDProxy implentation will execute apply config on DDProxy, DD, and
19236      * DragDrop in order to get all of the parameters that are available in
19237      * each object.
19238      * @method applyConfig
19239      */
19240     applyConfig: function() {
19241
19242         // configurable properties:
19243         //    padding, isTarget, maintainOffset, primaryButtonOnly
19244         this.padding           = this.config.padding || [0, 0, 0, 0];
19245         this.isTarget          = (this.config.isTarget !== false);
19246         this.maintainOffset    = (this.config.maintainOffset);
19247         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
19248
19249     },
19250
19251     /**
19252      * Executed when the linked element is available
19253      * @method handleOnAvailable
19254      * @private
19255      */
19256     handleOnAvailable: function() {
19257         this.available = true;
19258         this.resetConstraints();
19259         this.onAvailable();
19260     },
19261
19262      /**
19263      * Configures the padding for the target zone in px.  Effectively expands
19264      * (or reduces) the virtual object size for targeting calculations.
19265      * Supports css-style shorthand; if only one parameter is passed, all sides
19266      * will have that padding, and if only two are passed, the top and bottom
19267      * will have the first param, the left and right the second.
19268      * @method setPadding
19269      * @param {int} iTop    Top pad
19270      * @param {int} iRight  Right pad
19271      * @param {int} iBot    Bot pad
19272      * @param {int} iLeft   Left pad
19273      */
19274     setPadding: function(iTop, iRight, iBot, iLeft) {
19275         // this.padding = [iLeft, iRight, iTop, iBot];
19276         if (!iRight && 0 !== iRight) {
19277             this.padding = [iTop, iTop, iTop, iTop];
19278         } else if (!iBot && 0 !== iBot) {
19279             this.padding = [iTop, iRight, iTop, iRight];
19280         } else {
19281             this.padding = [iTop, iRight, iBot, iLeft];
19282         }
19283     },
19284
19285     /**
19286      * Stores the initial placement of the linked element.
19287      * @method setInitialPosition
19288      * @param {int} diffX   the X offset, default 0
19289      * @param {int} diffY   the Y offset, default 0
19290      */
19291     setInitPosition: function(diffX, diffY) {
19292         var el = this.getEl();
19293
19294         if (!this.DDM.verifyEl(el)) {
19295             return;
19296         }
19297
19298         var dx = diffX || 0;
19299         var dy = diffY || 0;
19300
19301         var p = Dom.getXY( el );
19302
19303         this.initPageX = p[0] - dx;
19304         this.initPageY = p[1] - dy;
19305
19306         this.lastPageX = p[0];
19307         this.lastPageY = p[1];
19308
19309
19310         this.setStartPosition(p);
19311     },
19312
19313     /**
19314      * Sets the start position of the element.  This is set when the obj
19315      * is initialized, the reset when a drag is started.
19316      * @method setStartPosition
19317      * @param pos current position (from previous lookup)
19318      * @private
19319      */
19320     setStartPosition: function(pos) {
19321         var p = pos || Dom.getXY( this.getEl() );
19322         this.deltaSetXY = null;
19323
19324         this.startPageX = p[0];
19325         this.startPageY = p[1];
19326     },
19327
19328     /**
19329      * Add this instance to a group of related drag/drop objects.  All
19330      * instances belong to at least one group, and can belong to as many
19331      * groups as needed.
19332      * @method addToGroup
19333      * @param sGroup {string} the name of the group
19334      */
19335     addToGroup: function(sGroup) {
19336         this.groups[sGroup] = true;
19337         this.DDM.regDragDrop(this, sGroup);
19338     },
19339
19340     /**
19341      * Remove's this instance from the supplied interaction group
19342      * @method removeFromGroup
19343      * @param {string}  sGroup  The group to drop
19344      */
19345     removeFromGroup: function(sGroup) {
19346         if (this.groups[sGroup]) {
19347             delete this.groups[sGroup];
19348         }
19349
19350         this.DDM.removeDDFromGroup(this, sGroup);
19351     },
19352
19353     /**
19354      * Allows you to specify that an element other than the linked element
19355      * will be moved with the cursor during a drag
19356      * @method setDragElId
19357      * @param id {string} the id of the element that will be used to initiate the drag
19358      */
19359     setDragElId: function(id) {
19360         this.dragElId = id;
19361     },
19362
19363     /**
19364      * Allows you to specify a child of the linked element that should be
19365      * used to initiate the drag operation.  An example of this would be if
19366      * you have a content div with text and links.  Clicking anywhere in the
19367      * content area would normally start the drag operation.  Use this method
19368      * to specify that an element inside of the content div is the element
19369      * that starts the drag operation.
19370      * @method setHandleElId
19371      * @param id {string} the id of the element that will be used to
19372      * initiate the drag.
19373      */
19374     setHandleElId: function(id) {
19375         if (typeof id !== "string") {
19376             id = Roo.id(id);
19377         }
19378         this.handleElId = id;
19379         this.DDM.regHandle(this.id, id);
19380     },
19381
19382     /**
19383      * Allows you to set an element outside of the linked element as a drag
19384      * handle
19385      * @method setOuterHandleElId
19386      * @param id the id of the element that will be used to initiate the drag
19387      */
19388     setOuterHandleElId: function(id) {
19389         if (typeof id !== "string") {
19390             id = Roo.id(id);
19391         }
19392         Event.on(id, "mousedown",
19393                 this.handleMouseDown, this);
19394         this.setHandleElId(id);
19395
19396         this.hasOuterHandles = true;
19397     },
19398
19399     /**
19400      * Remove all drag and drop hooks for this element
19401      * @method unreg
19402      */
19403     unreg: function() {
19404         Event.un(this.id, "mousedown",
19405                 this.handleMouseDown);
19406         Event.un(this.id, "touchstart",
19407                 this.handleMouseDown);
19408         this._domRef = null;
19409         this.DDM._remove(this);
19410     },
19411
19412     destroy : function(){
19413         this.unreg();
19414     },
19415
19416     /**
19417      * Returns true if this instance is locked, or the drag drop mgr is locked
19418      * (meaning that all drag/drop is disabled on the page.)
19419      * @method isLocked
19420      * @return {boolean} true if this obj or all drag/drop is locked, else
19421      * false
19422      */
19423     isLocked: function() {
19424         return (this.DDM.isLocked() || this.locked);
19425     },
19426
19427     /**
19428      * Fired when this object is clicked
19429      * @method handleMouseDown
19430      * @param {Event} e
19431      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
19432      * @private
19433      */
19434     handleMouseDown: function(e, oDD){
19435      
19436         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
19437             //Roo.log('not touch/ button !=0');
19438             return;
19439         }
19440         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
19441             return; // double touch..
19442         }
19443         
19444
19445         if (this.isLocked()) {
19446             //Roo.log('locked');
19447             return;
19448         }
19449
19450         this.DDM.refreshCache(this.groups);
19451 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
19452         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
19453         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
19454             //Roo.log('no outer handes or not over target');
19455                 // do nothing.
19456         } else {
19457 //            Roo.log('check validator');
19458             if (this.clickValidator(e)) {
19459 //                Roo.log('validate success');
19460                 // set the initial element position
19461                 this.setStartPosition();
19462
19463
19464                 this.b4MouseDown(e);
19465                 this.onMouseDown(e);
19466
19467                 this.DDM.handleMouseDown(e, this);
19468
19469                 this.DDM.stopEvent(e);
19470             } else {
19471
19472
19473             }
19474         }
19475     },
19476
19477     clickValidator: function(e) {
19478         var target = e.getTarget();
19479         return ( this.isValidHandleChild(target) &&
19480                     (this.id == this.handleElId ||
19481                         this.DDM.handleWasClicked(target, this.id)) );
19482     },
19483
19484     /**
19485      * Allows you to specify a tag name that should not start a drag operation
19486      * when clicked.  This is designed to facilitate embedding links within a
19487      * drag handle that do something other than start the drag.
19488      * @method addInvalidHandleType
19489      * @param {string} tagName the type of element to exclude
19490      */
19491     addInvalidHandleType: function(tagName) {
19492         var type = tagName.toUpperCase();
19493         this.invalidHandleTypes[type] = type;
19494     },
19495
19496     /**
19497      * Lets you to specify an element id for a child of a drag handle
19498      * that should not initiate a drag
19499      * @method addInvalidHandleId
19500      * @param {string} id the element id of the element you wish to ignore
19501      */
19502     addInvalidHandleId: function(id) {
19503         if (typeof id !== "string") {
19504             id = Roo.id(id);
19505         }
19506         this.invalidHandleIds[id] = id;
19507     },
19508
19509     /**
19510      * Lets you specify a css class of elements that will not initiate a drag
19511      * @method addInvalidHandleClass
19512      * @param {string} cssClass the class of the elements you wish to ignore
19513      */
19514     addInvalidHandleClass: function(cssClass) {
19515         this.invalidHandleClasses.push(cssClass);
19516     },
19517
19518     /**
19519      * Unsets an excluded tag name set by addInvalidHandleType
19520      * @method removeInvalidHandleType
19521      * @param {string} tagName the type of element to unexclude
19522      */
19523     removeInvalidHandleType: function(tagName) {
19524         var type = tagName.toUpperCase();
19525         // this.invalidHandleTypes[type] = null;
19526         delete this.invalidHandleTypes[type];
19527     },
19528
19529     /**
19530      * Unsets an invalid handle id
19531      * @method removeInvalidHandleId
19532      * @param {string} id the id of the element to re-enable
19533      */
19534     removeInvalidHandleId: function(id) {
19535         if (typeof id !== "string") {
19536             id = Roo.id(id);
19537         }
19538         delete this.invalidHandleIds[id];
19539     },
19540
19541     /**
19542      * Unsets an invalid css class
19543      * @method removeInvalidHandleClass
19544      * @param {string} cssClass the class of the element(s) you wish to
19545      * re-enable
19546      */
19547     removeInvalidHandleClass: function(cssClass) {
19548         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
19549             if (this.invalidHandleClasses[i] == cssClass) {
19550                 delete this.invalidHandleClasses[i];
19551             }
19552         }
19553     },
19554
19555     /**
19556      * Checks the tag exclusion list to see if this click should be ignored
19557      * @method isValidHandleChild
19558      * @param {HTMLElement} node the HTMLElement to evaluate
19559      * @return {boolean} true if this is a valid tag type, false if not
19560      */
19561     isValidHandleChild: function(node) {
19562
19563         var valid = true;
19564         // var n = (node.nodeName == "#text") ? node.parentNode : node;
19565         var nodeName;
19566         try {
19567             nodeName = node.nodeName.toUpperCase();
19568         } catch(e) {
19569             nodeName = node.nodeName;
19570         }
19571         valid = valid && !this.invalidHandleTypes[nodeName];
19572         valid = valid && !this.invalidHandleIds[node.id];
19573
19574         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
19575             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
19576         }
19577
19578
19579         return valid;
19580
19581     },
19582
19583     /**
19584      * Create the array of horizontal tick marks if an interval was specified
19585      * in setXConstraint().
19586      * @method setXTicks
19587      * @private
19588      */
19589     setXTicks: function(iStartX, iTickSize) {
19590         this.xTicks = [];
19591         this.xTickSize = iTickSize;
19592
19593         var tickMap = {};
19594
19595         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
19596             if (!tickMap[i]) {
19597                 this.xTicks[this.xTicks.length] = i;
19598                 tickMap[i] = true;
19599             }
19600         }
19601
19602         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
19603             if (!tickMap[i]) {
19604                 this.xTicks[this.xTicks.length] = i;
19605                 tickMap[i] = true;
19606             }
19607         }
19608
19609         this.xTicks.sort(this.DDM.numericSort) ;
19610     },
19611
19612     /**
19613      * Create the array of vertical tick marks if an interval was specified in
19614      * setYConstraint().
19615      * @method setYTicks
19616      * @private
19617      */
19618     setYTicks: function(iStartY, iTickSize) {
19619         this.yTicks = [];
19620         this.yTickSize = iTickSize;
19621
19622         var tickMap = {};
19623
19624         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
19625             if (!tickMap[i]) {
19626                 this.yTicks[this.yTicks.length] = i;
19627                 tickMap[i] = true;
19628             }
19629         }
19630
19631         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
19632             if (!tickMap[i]) {
19633                 this.yTicks[this.yTicks.length] = i;
19634                 tickMap[i] = true;
19635             }
19636         }
19637
19638         this.yTicks.sort(this.DDM.numericSort) ;
19639     },
19640
19641     /**
19642      * By default, the element can be dragged any place on the screen.  Use
19643      * this method to limit the horizontal travel of the element.  Pass in
19644      * 0,0 for the parameters if you want to lock the drag to the y axis.
19645      * @method setXConstraint
19646      * @param {int} iLeft the number of pixels the element can move to the left
19647      * @param {int} iRight the number of pixels the element can move to the
19648      * right
19649      * @param {int} iTickSize optional parameter for specifying that the
19650      * element
19651      * should move iTickSize pixels at a time.
19652      */
19653     setXConstraint: function(iLeft, iRight, iTickSize) {
19654         this.leftConstraint = iLeft;
19655         this.rightConstraint = iRight;
19656
19657         this.minX = this.initPageX - iLeft;
19658         this.maxX = this.initPageX + iRight;
19659         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
19660
19661         this.constrainX = true;
19662     },
19663
19664     /**
19665      * Clears any constraints applied to this instance.  Also clears ticks
19666      * since they can't exist independent of a constraint at this time.
19667      * @method clearConstraints
19668      */
19669     clearConstraints: function() {
19670         this.constrainX = false;
19671         this.constrainY = false;
19672         this.clearTicks();
19673     },
19674
19675     /**
19676      * Clears any tick interval defined for this instance
19677      * @method clearTicks
19678      */
19679     clearTicks: function() {
19680         this.xTicks = null;
19681         this.yTicks = null;
19682         this.xTickSize = 0;
19683         this.yTickSize = 0;
19684     },
19685
19686     /**
19687      * By default, the element can be dragged any place on the screen.  Set
19688      * this to limit the vertical travel of the element.  Pass in 0,0 for the
19689      * parameters if you want to lock the drag to the x axis.
19690      * @method setYConstraint
19691      * @param {int} iUp the number of pixels the element can move up
19692      * @param {int} iDown the number of pixels the element can move down
19693      * @param {int} iTickSize optional parameter for specifying that the
19694      * element should move iTickSize pixels at a time.
19695      */
19696     setYConstraint: function(iUp, iDown, iTickSize) {
19697         this.topConstraint = iUp;
19698         this.bottomConstraint = iDown;
19699
19700         this.minY = this.initPageY - iUp;
19701         this.maxY = this.initPageY + iDown;
19702         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
19703
19704         this.constrainY = true;
19705
19706     },
19707
19708     /**
19709      * resetConstraints must be called if you manually reposition a dd element.
19710      * @method resetConstraints
19711      * @param {boolean} maintainOffset
19712      */
19713     resetConstraints: function() {
19714
19715
19716         // Maintain offsets if necessary
19717         if (this.initPageX || this.initPageX === 0) {
19718             // figure out how much this thing has moved
19719             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
19720             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
19721
19722             this.setInitPosition(dx, dy);
19723
19724         // This is the first time we have detected the element's position
19725         } else {
19726             this.setInitPosition();
19727         }
19728
19729         if (this.constrainX) {
19730             this.setXConstraint( this.leftConstraint,
19731                                  this.rightConstraint,
19732                                  this.xTickSize        );
19733         }
19734
19735         if (this.constrainY) {
19736             this.setYConstraint( this.topConstraint,
19737                                  this.bottomConstraint,
19738                                  this.yTickSize         );
19739         }
19740     },
19741
19742     /**
19743      * Normally the drag element is moved pixel by pixel, but we can specify
19744      * that it move a number of pixels at a time.  This method resolves the
19745      * location when we have it set up like this.
19746      * @method getTick
19747      * @param {int} val where we want to place the object
19748      * @param {int[]} tickArray sorted array of valid points
19749      * @return {int} the closest tick
19750      * @private
19751      */
19752     getTick: function(val, tickArray) {
19753
19754         if (!tickArray) {
19755             // If tick interval is not defined, it is effectively 1 pixel,
19756             // so we return the value passed to us.
19757             return val;
19758         } else if (tickArray[0] >= val) {
19759             // The value is lower than the first tick, so we return the first
19760             // tick.
19761             return tickArray[0];
19762         } else {
19763             for (var i=0, len=tickArray.length; i<len; ++i) {
19764                 var next = i + 1;
19765                 if (tickArray[next] && tickArray[next] >= val) {
19766                     var diff1 = val - tickArray[i];
19767                     var diff2 = tickArray[next] - val;
19768                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
19769                 }
19770             }
19771
19772             // The value is larger than the last tick, so we return the last
19773             // tick.
19774             return tickArray[tickArray.length - 1];
19775         }
19776     },
19777
19778     /**
19779      * toString method
19780      * @method toString
19781      * @return {string} string representation of the dd obj
19782      */
19783     toString: function() {
19784         return ("DragDrop " + this.id);
19785     }
19786
19787 });
19788
19789 })();
19790 /*
19791  * Based on:
19792  * Ext JS Library 1.1.1
19793  * Copyright(c) 2006-2007, Ext JS, LLC.
19794  *
19795  * Originally Released Under LGPL - original licence link has changed is not relivant.
19796  *
19797  * Fork - LGPL
19798  * <script type="text/javascript">
19799  */
19800
19801
19802 /**
19803  * The drag and drop utility provides a framework for building drag and drop
19804  * applications.  In addition to enabling drag and drop for specific elements,
19805  * the drag and drop elements are tracked by the manager class, and the
19806  * interactions between the various elements are tracked during the drag and
19807  * the implementing code is notified about these important moments.
19808  */
19809
19810 // Only load the library once.  Rewriting the manager class would orphan
19811 // existing drag and drop instances.
19812 if (!Roo.dd.DragDropMgr) {
19813
19814 /**
19815  * @class Roo.dd.DragDropMgr
19816  * DragDropMgr is a singleton that tracks the element interaction for
19817  * all DragDrop items in the window.  Generally, you will not call
19818  * this class directly, but it does have helper methods that could
19819  * be useful in your DragDrop implementations.
19820  * @singleton
19821  */
19822 Roo.dd.DragDropMgr = function() {
19823
19824     var Event = Roo.EventManager;
19825
19826     return {
19827
19828         /**
19829          * Two dimensional Array of registered DragDrop objects.  The first
19830          * dimension is the DragDrop item group, the second the DragDrop
19831          * object.
19832          * @property ids
19833          * @type {string: string}
19834          * @private
19835          * @static
19836          */
19837         ids: {},
19838
19839         /**
19840          * Array of element ids defined as drag handles.  Used to determine
19841          * if the element that generated the mousedown event is actually the
19842          * handle and not the html element itself.
19843          * @property handleIds
19844          * @type {string: string}
19845          * @private
19846          * @static
19847          */
19848         handleIds: {},
19849
19850         /**
19851          * the DragDrop object that is currently being dragged
19852          * @property dragCurrent
19853          * @type DragDrop
19854          * @private
19855          * @static
19856          **/
19857         dragCurrent: null,
19858
19859         /**
19860          * the DragDrop object(s) that are being hovered over
19861          * @property dragOvers
19862          * @type Array
19863          * @private
19864          * @static
19865          */
19866         dragOvers: {},
19867
19868         /**
19869          * the X distance between the cursor and the object being dragged
19870          * @property deltaX
19871          * @type int
19872          * @private
19873          * @static
19874          */
19875         deltaX: 0,
19876
19877         /**
19878          * the Y distance between the cursor and the object being dragged
19879          * @property deltaY
19880          * @type int
19881          * @private
19882          * @static
19883          */
19884         deltaY: 0,
19885
19886         /**
19887          * Flag to determine if we should prevent the default behavior of the
19888          * events we define. By default this is true, but this can be set to
19889          * false if you need the default behavior (not recommended)
19890          * @property preventDefault
19891          * @type boolean
19892          * @static
19893          */
19894         preventDefault: true,
19895
19896         /**
19897          * Flag to determine if we should stop the propagation of the events
19898          * we generate. This is true by default but you may want to set it to
19899          * false if the html element contains other features that require the
19900          * mouse click.
19901          * @property stopPropagation
19902          * @type boolean
19903          * @static
19904          */
19905         stopPropagation: true,
19906
19907         /**
19908          * Internal flag that is set to true when drag and drop has been
19909          * intialized
19910          * @property initialized
19911          * @private
19912          * @static
19913          */
19914         initalized: false,
19915
19916         /**
19917          * All drag and drop can be disabled.
19918          * @property locked
19919          * @private
19920          * @static
19921          */
19922         locked: false,
19923
19924         /**
19925          * Called the first time an element is registered.
19926          * @method init
19927          * @private
19928          * @static
19929          */
19930         init: function() {
19931             this.initialized = true;
19932         },
19933
19934         /**
19935          * In point mode, drag and drop interaction is defined by the
19936          * location of the cursor during the drag/drop
19937          * @property POINT
19938          * @type int
19939          * @static
19940          */
19941         POINT: 0,
19942
19943         /**
19944          * In intersect mode, drag and drop interactio nis defined by the
19945          * overlap of two or more drag and drop objects.
19946          * @property INTERSECT
19947          * @type int
19948          * @static
19949          */
19950         INTERSECT: 1,
19951
19952         /**
19953          * The current drag and drop mode.  Default: POINT
19954          * @property mode
19955          * @type int
19956          * @static
19957          */
19958         mode: 0,
19959
19960         /**
19961          * Runs method on all drag and drop objects
19962          * @method _execOnAll
19963          * @private
19964          * @static
19965          */
19966         _execOnAll: function(sMethod, args) {
19967             for (var i in this.ids) {
19968                 for (var j in this.ids[i]) {
19969                     var oDD = this.ids[i][j];
19970                     if (! this.isTypeOfDD(oDD)) {
19971                         continue;
19972                     }
19973                     oDD[sMethod].apply(oDD, args);
19974                 }
19975             }
19976         },
19977
19978         /**
19979          * Drag and drop initialization.  Sets up the global event handlers
19980          * @method _onLoad
19981          * @private
19982          * @static
19983          */
19984         _onLoad: function() {
19985
19986             this.init();
19987
19988             if (!Roo.isTouch) {
19989                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
19990                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
19991             }
19992             Event.on(document, "touchend",   this.handleMouseUp, this, true);
19993             Event.on(document, "touchmove", this.handleMouseMove, this, true);
19994             
19995             Event.on(window,   "unload",    this._onUnload, this, true);
19996             Event.on(window,   "resize",    this._onResize, this, true);
19997             // Event.on(window,   "mouseout",    this._test);
19998
19999         },
20000
20001         /**
20002          * Reset constraints on all drag and drop objs
20003          * @method _onResize
20004          * @private
20005          * @static
20006          */
20007         _onResize: function(e) {
20008             this._execOnAll("resetConstraints", []);
20009         },
20010
20011         /**
20012          * Lock all drag and drop functionality
20013          * @method lock
20014          * @static
20015          */
20016         lock: function() { this.locked = true; },
20017
20018         /**
20019          * Unlock all drag and drop functionality
20020          * @method unlock
20021          * @static
20022          */
20023         unlock: function() { this.locked = false; },
20024
20025         /**
20026          * Is drag and drop locked?
20027          * @method isLocked
20028          * @return {boolean} True if drag and drop is locked, false otherwise.
20029          * @static
20030          */
20031         isLocked: function() { return this.locked; },
20032
20033         /**
20034          * Location cache that is set for all drag drop objects when a drag is
20035          * initiated, cleared when the drag is finished.
20036          * @property locationCache
20037          * @private
20038          * @static
20039          */
20040         locationCache: {},
20041
20042         /**
20043          * Set useCache to false if you want to force object the lookup of each
20044          * drag and drop linked element constantly during a drag.
20045          * @property useCache
20046          * @type boolean
20047          * @static
20048          */
20049         useCache: true,
20050
20051         /**
20052          * The number of pixels that the mouse needs to move after the
20053          * mousedown before the drag is initiated.  Default=3;
20054          * @property clickPixelThresh
20055          * @type int
20056          * @static
20057          */
20058         clickPixelThresh: 3,
20059
20060         /**
20061          * The number of milliseconds after the mousedown event to initiate the
20062          * drag if we don't get a mouseup event. Default=1000
20063          * @property clickTimeThresh
20064          * @type int
20065          * @static
20066          */
20067         clickTimeThresh: 350,
20068
20069         /**
20070          * Flag that indicates that either the drag pixel threshold or the
20071          * mousdown time threshold has been met
20072          * @property dragThreshMet
20073          * @type boolean
20074          * @private
20075          * @static
20076          */
20077         dragThreshMet: false,
20078
20079         /**
20080          * Timeout used for the click time threshold
20081          * @property clickTimeout
20082          * @type Object
20083          * @private
20084          * @static
20085          */
20086         clickTimeout: null,
20087
20088         /**
20089          * The X position of the mousedown event stored for later use when a
20090          * drag threshold is met.
20091          * @property startX
20092          * @type int
20093          * @private
20094          * @static
20095          */
20096         startX: 0,
20097
20098         /**
20099          * The Y position of the mousedown event stored for later use when a
20100          * drag threshold is met.
20101          * @property startY
20102          * @type int
20103          * @private
20104          * @static
20105          */
20106         startY: 0,
20107
20108         /**
20109          * Each DragDrop instance must be registered with the DragDropMgr.
20110          * This is executed in DragDrop.init()
20111          * @method regDragDrop
20112          * @param {DragDrop} oDD the DragDrop object to register
20113          * @param {String} sGroup the name of the group this element belongs to
20114          * @static
20115          */
20116         regDragDrop: function(oDD, sGroup) {
20117             if (!this.initialized) { this.init(); }
20118
20119             if (!this.ids[sGroup]) {
20120                 this.ids[sGroup] = {};
20121             }
20122             this.ids[sGroup][oDD.id] = oDD;
20123         },
20124
20125         /**
20126          * Removes the supplied dd instance from the supplied group. Executed
20127          * by DragDrop.removeFromGroup, so don't call this function directly.
20128          * @method removeDDFromGroup
20129          * @private
20130          * @static
20131          */
20132         removeDDFromGroup: function(oDD, sGroup) {
20133             if (!this.ids[sGroup]) {
20134                 this.ids[sGroup] = {};
20135             }
20136
20137             var obj = this.ids[sGroup];
20138             if (obj && obj[oDD.id]) {
20139                 delete obj[oDD.id];
20140             }
20141         },
20142
20143         /**
20144          * Unregisters a drag and drop item.  This is executed in
20145          * DragDrop.unreg, use that method instead of calling this directly.
20146          * @method _remove
20147          * @private
20148          * @static
20149          */
20150         _remove: function(oDD) {
20151             for (var g in oDD.groups) {
20152                 if (g && this.ids[g][oDD.id]) {
20153                     delete this.ids[g][oDD.id];
20154                 }
20155             }
20156             delete this.handleIds[oDD.id];
20157         },
20158
20159         /**
20160          * Each DragDrop handle element must be registered.  This is done
20161          * automatically when executing DragDrop.setHandleElId()
20162          * @method regHandle
20163          * @param {String} sDDId the DragDrop id this element is a handle for
20164          * @param {String} sHandleId the id of the element that is the drag
20165          * handle
20166          * @static
20167          */
20168         regHandle: function(sDDId, sHandleId) {
20169             if (!this.handleIds[sDDId]) {
20170                 this.handleIds[sDDId] = {};
20171             }
20172             this.handleIds[sDDId][sHandleId] = sHandleId;
20173         },
20174
20175         /**
20176          * Utility function to determine if a given element has been
20177          * registered as a drag drop item.
20178          * @method isDragDrop
20179          * @param {String} id the element id to check
20180          * @return {boolean} true if this element is a DragDrop item,
20181          * false otherwise
20182          * @static
20183          */
20184         isDragDrop: function(id) {
20185             return ( this.getDDById(id) ) ? true : false;
20186         },
20187
20188         /**
20189          * Returns the drag and drop instances that are in all groups the
20190          * passed in instance belongs to.
20191          * @method getRelated
20192          * @param {DragDrop} p_oDD the obj to get related data for
20193          * @param {boolean} bTargetsOnly if true, only return targetable objs
20194          * @return {DragDrop[]} the related instances
20195          * @static
20196          */
20197         getRelated: function(p_oDD, bTargetsOnly) {
20198             var oDDs = [];
20199             for (var i in p_oDD.groups) {
20200                 for (j in this.ids[i]) {
20201                     var dd = this.ids[i][j];
20202                     if (! this.isTypeOfDD(dd)) {
20203                         continue;
20204                     }
20205                     if (!bTargetsOnly || dd.isTarget) {
20206                         oDDs[oDDs.length] = dd;
20207                     }
20208                 }
20209             }
20210
20211             return oDDs;
20212         },
20213
20214         /**
20215          * Returns true if the specified dd target is a legal target for
20216          * the specifice drag obj
20217          * @method isLegalTarget
20218          * @param {DragDrop} the drag obj
20219          * @param {DragDrop} the target
20220          * @return {boolean} true if the target is a legal target for the
20221          * dd obj
20222          * @static
20223          */
20224         isLegalTarget: function (oDD, oTargetDD) {
20225             var targets = this.getRelated(oDD, true);
20226             for (var i=0, len=targets.length;i<len;++i) {
20227                 if (targets[i].id == oTargetDD.id) {
20228                     return true;
20229                 }
20230             }
20231
20232             return false;
20233         },
20234
20235         /**
20236          * My goal is to be able to transparently determine if an object is
20237          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
20238          * returns "object", oDD.constructor.toString() always returns
20239          * "DragDrop" and not the name of the subclass.  So for now it just
20240          * evaluates a well-known variable in DragDrop.
20241          * @method isTypeOfDD
20242          * @param {Object} the object to evaluate
20243          * @return {boolean} true if typeof oDD = DragDrop
20244          * @static
20245          */
20246         isTypeOfDD: function (oDD) {
20247             return (oDD && oDD.__ygDragDrop);
20248         },
20249
20250         /**
20251          * Utility function to determine if a given element has been
20252          * registered as a drag drop handle for the given Drag Drop object.
20253          * @method isHandle
20254          * @param {String} id the element id to check
20255          * @return {boolean} true if this element is a DragDrop handle, false
20256          * otherwise
20257          * @static
20258          */
20259         isHandle: function(sDDId, sHandleId) {
20260             return ( this.handleIds[sDDId] &&
20261                             this.handleIds[sDDId][sHandleId] );
20262         },
20263
20264         /**
20265          * Returns the DragDrop instance for a given id
20266          * @method getDDById
20267          * @param {String} id the id of the DragDrop object
20268          * @return {DragDrop} the drag drop object, null if it is not found
20269          * @static
20270          */
20271         getDDById: function(id) {
20272             for (var i in this.ids) {
20273                 if (this.ids[i][id]) {
20274                     return this.ids[i][id];
20275                 }
20276             }
20277             return null;
20278         },
20279
20280         /**
20281          * Fired after a registered DragDrop object gets the mousedown event.
20282          * Sets up the events required to track the object being dragged
20283          * @method handleMouseDown
20284          * @param {Event} e the event
20285          * @param oDD the DragDrop object being dragged
20286          * @private
20287          * @static
20288          */
20289         handleMouseDown: function(e, oDD) {
20290             if(Roo.QuickTips){
20291                 Roo.QuickTips.disable();
20292             }
20293             this.currentTarget = e.getTarget();
20294
20295             this.dragCurrent = oDD;
20296
20297             var el = oDD.getEl();
20298
20299             // track start position
20300             this.startX = e.getPageX();
20301             this.startY = e.getPageY();
20302
20303             this.deltaX = this.startX - el.offsetLeft;
20304             this.deltaY = this.startY - el.offsetTop;
20305
20306             this.dragThreshMet = false;
20307
20308             this.clickTimeout = setTimeout(
20309                     function() {
20310                         var DDM = Roo.dd.DDM;
20311                         DDM.startDrag(DDM.startX, DDM.startY);
20312                     },
20313                     this.clickTimeThresh );
20314         },
20315
20316         /**
20317          * Fired when either the drag pixel threshol or the mousedown hold
20318          * time threshold has been met.
20319          * @method startDrag
20320          * @param x {int} the X position of the original mousedown
20321          * @param y {int} the Y position of the original mousedown
20322          * @static
20323          */
20324         startDrag: function(x, y) {
20325             clearTimeout(this.clickTimeout);
20326             if (this.dragCurrent) {
20327                 this.dragCurrent.b4StartDrag(x, y);
20328                 this.dragCurrent.startDrag(x, y);
20329             }
20330             this.dragThreshMet = true;
20331         },
20332
20333         /**
20334          * Internal function to handle the mouseup event.  Will be invoked
20335          * from the context of the document.
20336          * @method handleMouseUp
20337          * @param {Event} e the event
20338          * @private
20339          * @static
20340          */
20341         handleMouseUp: function(e) {
20342
20343             if(Roo.QuickTips){
20344                 Roo.QuickTips.enable();
20345             }
20346             if (! this.dragCurrent) {
20347                 return;
20348             }
20349
20350             clearTimeout(this.clickTimeout);
20351
20352             if (this.dragThreshMet) {
20353                 this.fireEvents(e, true);
20354             } else {
20355             }
20356
20357             this.stopDrag(e);
20358
20359             this.stopEvent(e);
20360         },
20361
20362         /**
20363          * Utility to stop event propagation and event default, if these
20364          * features are turned on.
20365          * @method stopEvent
20366          * @param {Event} e the event as returned by this.getEvent()
20367          * @static
20368          */
20369         stopEvent: function(e){
20370             if(this.stopPropagation) {
20371                 e.stopPropagation();
20372             }
20373
20374             if (this.preventDefault) {
20375                 e.preventDefault();
20376             }
20377         },
20378
20379         /**
20380          * Internal function to clean up event handlers after the drag
20381          * operation is complete
20382          * @method stopDrag
20383          * @param {Event} e the event
20384          * @private
20385          * @static
20386          */
20387         stopDrag: function(e) {
20388             // Fire the drag end event for the item that was dragged
20389             if (this.dragCurrent) {
20390                 if (this.dragThreshMet) {
20391                     this.dragCurrent.b4EndDrag(e);
20392                     this.dragCurrent.endDrag(e);
20393                 }
20394
20395                 this.dragCurrent.onMouseUp(e);
20396             }
20397
20398             this.dragCurrent = null;
20399             this.dragOvers = {};
20400         },
20401
20402         /**
20403          * Internal function to handle the mousemove event.  Will be invoked
20404          * from the context of the html element.
20405          *
20406          * @TODO figure out what we can do about mouse events lost when the
20407          * user drags objects beyond the window boundary.  Currently we can
20408          * detect this in internet explorer by verifying that the mouse is
20409          * down during the mousemove event.  Firefox doesn't give us the
20410          * button state on the mousemove event.
20411          * @method handleMouseMove
20412          * @param {Event} e the event
20413          * @private
20414          * @static
20415          */
20416         handleMouseMove: function(e) {
20417             if (! this.dragCurrent) {
20418                 return true;
20419             }
20420
20421             // var button = e.which || e.button;
20422
20423             // check for IE mouseup outside of page boundary
20424             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
20425                 this.stopEvent(e);
20426                 return this.handleMouseUp(e);
20427             }
20428
20429             if (!this.dragThreshMet) {
20430                 var diffX = Math.abs(this.startX - e.getPageX());
20431                 var diffY = Math.abs(this.startY - e.getPageY());
20432                 if (diffX > this.clickPixelThresh ||
20433                             diffY > this.clickPixelThresh) {
20434                     this.startDrag(this.startX, this.startY);
20435                 }
20436             }
20437
20438             if (this.dragThreshMet) {
20439                 this.dragCurrent.b4Drag(e);
20440                 this.dragCurrent.onDrag(e);
20441                 if(!this.dragCurrent.moveOnly){
20442                     this.fireEvents(e, false);
20443                 }
20444             }
20445
20446             this.stopEvent(e);
20447
20448             return true;
20449         },
20450
20451         /**
20452          * Iterates over all of the DragDrop elements to find ones we are
20453          * hovering over or dropping on
20454          * @method fireEvents
20455          * @param {Event} e the event
20456          * @param {boolean} isDrop is this a drop op or a mouseover op?
20457          * @private
20458          * @static
20459          */
20460         fireEvents: function(e, isDrop) {
20461             var dc = this.dragCurrent;
20462
20463             // If the user did the mouse up outside of the window, we could
20464             // get here even though we have ended the drag.
20465             if (!dc || dc.isLocked()) {
20466                 return;
20467             }
20468
20469             var pt = e.getPoint();
20470
20471             // cache the previous dragOver array
20472             var oldOvers = [];
20473
20474             var outEvts   = [];
20475             var overEvts  = [];
20476             var dropEvts  = [];
20477             var enterEvts = [];
20478
20479             // Check to see if the object(s) we were hovering over is no longer
20480             // being hovered over so we can fire the onDragOut event
20481             for (var i in this.dragOvers) {
20482
20483                 var ddo = this.dragOvers[i];
20484
20485                 if (! this.isTypeOfDD(ddo)) {
20486                     continue;
20487                 }
20488
20489                 if (! this.isOverTarget(pt, ddo, this.mode)) {
20490                     outEvts.push( ddo );
20491                 }
20492
20493                 oldOvers[i] = true;
20494                 delete this.dragOvers[i];
20495             }
20496
20497             for (var sGroup in dc.groups) {
20498
20499                 if ("string" != typeof sGroup) {
20500                     continue;
20501                 }
20502
20503                 for (i in this.ids[sGroup]) {
20504                     var oDD = this.ids[sGroup][i];
20505                     if (! this.isTypeOfDD(oDD)) {
20506                         continue;
20507                     }
20508
20509                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
20510                         if (this.isOverTarget(pt, oDD, this.mode)) {
20511                             // look for drop interactions
20512                             if (isDrop) {
20513                                 dropEvts.push( oDD );
20514                             // look for drag enter and drag over interactions
20515                             } else {
20516
20517                                 // initial drag over: dragEnter fires
20518                                 if (!oldOvers[oDD.id]) {
20519                                     enterEvts.push( oDD );
20520                                 // subsequent drag overs: dragOver fires
20521                                 } else {
20522                                     overEvts.push( oDD );
20523                                 }
20524
20525                                 this.dragOvers[oDD.id] = oDD;
20526                             }
20527                         }
20528                     }
20529                 }
20530             }
20531
20532             if (this.mode) {
20533                 if (outEvts.length) {
20534                     dc.b4DragOut(e, outEvts);
20535                     dc.onDragOut(e, outEvts);
20536                 }
20537
20538                 if (enterEvts.length) {
20539                     dc.onDragEnter(e, enterEvts);
20540                 }
20541
20542                 if (overEvts.length) {
20543                     dc.b4DragOver(e, overEvts);
20544                     dc.onDragOver(e, overEvts);
20545                 }
20546
20547                 if (dropEvts.length) {
20548                     dc.b4DragDrop(e, dropEvts);
20549                     dc.onDragDrop(e, dropEvts);
20550                 }
20551
20552             } else {
20553                 // fire dragout events
20554                 var len = 0;
20555                 for (i=0, len=outEvts.length; i<len; ++i) {
20556                     dc.b4DragOut(e, outEvts[i].id);
20557                     dc.onDragOut(e, outEvts[i].id);
20558                 }
20559
20560                 // fire enter events
20561                 for (i=0,len=enterEvts.length; i<len; ++i) {
20562                     // dc.b4DragEnter(e, oDD.id);
20563                     dc.onDragEnter(e, enterEvts[i].id);
20564                 }
20565
20566                 // fire over events
20567                 for (i=0,len=overEvts.length; i<len; ++i) {
20568                     dc.b4DragOver(e, overEvts[i].id);
20569                     dc.onDragOver(e, overEvts[i].id);
20570                 }
20571
20572                 // fire drop events
20573                 for (i=0, len=dropEvts.length; i<len; ++i) {
20574                     dc.b4DragDrop(e, dropEvts[i].id);
20575                     dc.onDragDrop(e, dropEvts[i].id);
20576                 }
20577
20578             }
20579
20580             // notify about a drop that did not find a target
20581             if (isDrop && !dropEvts.length) {
20582                 dc.onInvalidDrop(e);
20583             }
20584
20585         },
20586
20587         /**
20588          * Helper function for getting the best match from the list of drag
20589          * and drop objects returned by the drag and drop events when we are
20590          * in INTERSECT mode.  It returns either the first object that the
20591          * cursor is over, or the object that has the greatest overlap with
20592          * the dragged element.
20593          * @method getBestMatch
20594          * @param  {DragDrop[]} dds The array of drag and drop objects
20595          * targeted
20596          * @return {DragDrop}       The best single match
20597          * @static
20598          */
20599         getBestMatch: function(dds) {
20600             var winner = null;
20601             // Return null if the input is not what we expect
20602             //if (!dds || !dds.length || dds.length == 0) {
20603                // winner = null;
20604             // If there is only one item, it wins
20605             //} else if (dds.length == 1) {
20606
20607             var len = dds.length;
20608
20609             if (len == 1) {
20610                 winner = dds[0];
20611             } else {
20612                 // Loop through the targeted items
20613                 for (var i=0; i<len; ++i) {
20614                     var dd = dds[i];
20615                     // If the cursor is over the object, it wins.  If the
20616                     // cursor is over multiple matches, the first one we come
20617                     // to wins.
20618                     if (dd.cursorIsOver) {
20619                         winner = dd;
20620                         break;
20621                     // Otherwise the object with the most overlap wins
20622                     } else {
20623                         if (!winner ||
20624                             winner.overlap.getArea() < dd.overlap.getArea()) {
20625                             winner = dd;
20626                         }
20627                     }
20628                 }
20629             }
20630
20631             return winner;
20632         },
20633
20634         /**
20635          * Refreshes the cache of the top-left and bottom-right points of the
20636          * drag and drop objects in the specified group(s).  This is in the
20637          * format that is stored in the drag and drop instance, so typical
20638          * usage is:
20639          * <code>
20640          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
20641          * </code>
20642          * Alternatively:
20643          * <code>
20644          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
20645          * </code>
20646          * @TODO this really should be an indexed array.  Alternatively this
20647          * method could accept both.
20648          * @method refreshCache
20649          * @param {Object} groups an associative array of groups to refresh
20650          * @static
20651          */
20652         refreshCache: function(groups) {
20653             for (var sGroup in groups) {
20654                 if ("string" != typeof sGroup) {
20655                     continue;
20656                 }
20657                 for (var i in this.ids[sGroup]) {
20658                     var oDD = this.ids[sGroup][i];
20659
20660                     if (this.isTypeOfDD(oDD)) {
20661                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
20662                         var loc = this.getLocation(oDD);
20663                         if (loc) {
20664                             this.locationCache[oDD.id] = loc;
20665                         } else {
20666                             delete this.locationCache[oDD.id];
20667                             // this will unregister the drag and drop object if
20668                             // the element is not in a usable state
20669                             // oDD.unreg();
20670                         }
20671                     }
20672                 }
20673             }
20674         },
20675
20676         /**
20677          * This checks to make sure an element exists and is in the DOM.  The
20678          * main purpose is to handle cases where innerHTML is used to remove
20679          * drag and drop objects from the DOM.  IE provides an 'unspecified
20680          * error' when trying to access the offsetParent of such an element
20681          * @method verifyEl
20682          * @param {HTMLElement} el the element to check
20683          * @return {boolean} true if the element looks usable
20684          * @static
20685          */
20686         verifyEl: function(el) {
20687             if (el) {
20688                 var parent;
20689                 if(Roo.isIE){
20690                     try{
20691                         parent = el.offsetParent;
20692                     }catch(e){}
20693                 }else{
20694                     parent = el.offsetParent;
20695                 }
20696                 if (parent) {
20697                     return true;
20698                 }
20699             }
20700
20701             return false;
20702         },
20703
20704         /**
20705          * Returns a Region object containing the drag and drop element's position
20706          * and size, including the padding configured for it
20707          * @method getLocation
20708          * @param {DragDrop} oDD the drag and drop object to get the
20709          *                       location for
20710          * @return {Roo.lib.Region} a Region object representing the total area
20711          *                             the element occupies, including any padding
20712          *                             the instance is configured for.
20713          * @static
20714          */
20715         getLocation: function(oDD) {
20716             if (! this.isTypeOfDD(oDD)) {
20717                 return null;
20718             }
20719
20720             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
20721
20722             try {
20723                 pos= Roo.lib.Dom.getXY(el);
20724             } catch (e) { }
20725
20726             if (!pos) {
20727                 return null;
20728             }
20729
20730             x1 = pos[0];
20731             x2 = x1 + el.offsetWidth;
20732             y1 = pos[1];
20733             y2 = y1 + el.offsetHeight;
20734
20735             t = y1 - oDD.padding[0];
20736             r = x2 + oDD.padding[1];
20737             b = y2 + oDD.padding[2];
20738             l = x1 - oDD.padding[3];
20739
20740             return new Roo.lib.Region( t, r, b, l );
20741         },
20742
20743         /**
20744          * Checks the cursor location to see if it over the target
20745          * @method isOverTarget
20746          * @param {Roo.lib.Point} pt The point to evaluate
20747          * @param {DragDrop} oTarget the DragDrop object we are inspecting
20748          * @return {boolean} true if the mouse is over the target
20749          * @private
20750          * @static
20751          */
20752         isOverTarget: function(pt, oTarget, intersect) {
20753             // use cache if available
20754             var loc = this.locationCache[oTarget.id];
20755             if (!loc || !this.useCache) {
20756                 loc = this.getLocation(oTarget);
20757                 this.locationCache[oTarget.id] = loc;
20758
20759             }
20760
20761             if (!loc) {
20762                 return false;
20763             }
20764
20765             oTarget.cursorIsOver = loc.contains( pt );
20766
20767             // DragDrop is using this as a sanity check for the initial mousedown
20768             // in this case we are done.  In POINT mode, if the drag obj has no
20769             // contraints, we are also done. Otherwise we need to evaluate the
20770             // location of the target as related to the actual location of the
20771             // dragged element.
20772             var dc = this.dragCurrent;
20773             if (!dc || !dc.getTargetCoord ||
20774                     (!intersect && !dc.constrainX && !dc.constrainY)) {
20775                 return oTarget.cursorIsOver;
20776             }
20777
20778             oTarget.overlap = null;
20779
20780             // Get the current location of the drag element, this is the
20781             // location of the mouse event less the delta that represents
20782             // where the original mousedown happened on the element.  We
20783             // need to consider constraints and ticks as well.
20784             var pos = dc.getTargetCoord(pt.x, pt.y);
20785
20786             var el = dc.getDragEl();
20787             var curRegion = new Roo.lib.Region( pos.y,
20788                                                    pos.x + el.offsetWidth,
20789                                                    pos.y + el.offsetHeight,
20790                                                    pos.x );
20791
20792             var overlap = curRegion.intersect(loc);
20793
20794             if (overlap) {
20795                 oTarget.overlap = overlap;
20796                 return (intersect) ? true : oTarget.cursorIsOver;
20797             } else {
20798                 return false;
20799             }
20800         },
20801
20802         /**
20803          * unload event handler
20804          * @method _onUnload
20805          * @private
20806          * @static
20807          */
20808         _onUnload: function(e, me) {
20809             Roo.dd.DragDropMgr.unregAll();
20810         },
20811
20812         /**
20813          * Cleans up the drag and drop events and objects.
20814          * @method unregAll
20815          * @private
20816          * @static
20817          */
20818         unregAll: function() {
20819
20820             if (this.dragCurrent) {
20821                 this.stopDrag();
20822                 this.dragCurrent = null;
20823             }
20824
20825             this._execOnAll("unreg", []);
20826
20827             for (i in this.elementCache) {
20828                 delete this.elementCache[i];
20829             }
20830
20831             this.elementCache = {};
20832             this.ids = {};
20833         },
20834
20835         /**
20836          * A cache of DOM elements
20837          * @property elementCache
20838          * @private
20839          * @static
20840          */
20841         elementCache: {},
20842
20843         /**
20844          * Get the wrapper for the DOM element specified
20845          * @method getElWrapper
20846          * @param {String} id the id of the element to get
20847          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
20848          * @private
20849          * @deprecated This wrapper isn't that useful
20850          * @static
20851          */
20852         getElWrapper: function(id) {
20853             var oWrapper = this.elementCache[id];
20854             if (!oWrapper || !oWrapper.el) {
20855                 oWrapper = this.elementCache[id] =
20856                     new this.ElementWrapper(Roo.getDom(id));
20857             }
20858             return oWrapper;
20859         },
20860
20861         /**
20862          * Returns the actual DOM element
20863          * @method getElement
20864          * @param {String} id the id of the elment to get
20865          * @return {Object} The element
20866          * @deprecated use Roo.getDom instead
20867          * @static
20868          */
20869         getElement: function(id) {
20870             return Roo.getDom(id);
20871         },
20872
20873         /**
20874          * Returns the style property for the DOM element (i.e.,
20875          * document.getElById(id).style)
20876          * @method getCss
20877          * @param {String} id the id of the elment to get
20878          * @return {Object} The style property of the element
20879          * @deprecated use Roo.getDom instead
20880          * @static
20881          */
20882         getCss: function(id) {
20883             var el = Roo.getDom(id);
20884             return (el) ? el.style : null;
20885         },
20886
20887         /**
20888          * Inner class for cached elements
20889          * @class DragDropMgr.ElementWrapper
20890          * @for DragDropMgr
20891          * @private
20892          * @deprecated
20893          */
20894         ElementWrapper: function(el) {
20895                 /**
20896                  * The element
20897                  * @property el
20898                  */
20899                 this.el = el || null;
20900                 /**
20901                  * The element id
20902                  * @property id
20903                  */
20904                 this.id = this.el && el.id;
20905                 /**
20906                  * A reference to the style property
20907                  * @property css
20908                  */
20909                 this.css = this.el && el.style;
20910             },
20911
20912         /**
20913          * Returns the X position of an html element
20914          * @method getPosX
20915          * @param el the element for which to get the position
20916          * @return {int} the X coordinate
20917          * @for DragDropMgr
20918          * @deprecated use Roo.lib.Dom.getX instead
20919          * @static
20920          */
20921         getPosX: function(el) {
20922             return Roo.lib.Dom.getX(el);
20923         },
20924
20925         /**
20926          * Returns the Y position of an html element
20927          * @method getPosY
20928          * @param el the element for which to get the position
20929          * @return {int} the Y coordinate
20930          * @deprecated use Roo.lib.Dom.getY instead
20931          * @static
20932          */
20933         getPosY: function(el) {
20934             return Roo.lib.Dom.getY(el);
20935         },
20936
20937         /**
20938          * Swap two nodes.  In IE, we use the native method, for others we
20939          * emulate the IE behavior
20940          * @method swapNode
20941          * @param n1 the first node to swap
20942          * @param n2 the other node to swap
20943          * @static
20944          */
20945         swapNode: function(n1, n2) {
20946             if (n1.swapNode) {
20947                 n1.swapNode(n2);
20948             } else {
20949                 var p = n2.parentNode;
20950                 var s = n2.nextSibling;
20951
20952                 if (s == n1) {
20953                     p.insertBefore(n1, n2);
20954                 } else if (n2 == n1.nextSibling) {
20955                     p.insertBefore(n2, n1);
20956                 } else {
20957                     n1.parentNode.replaceChild(n2, n1);
20958                     p.insertBefore(n1, s);
20959                 }
20960             }
20961         },
20962
20963         /**
20964          * Returns the current scroll position
20965          * @method getScroll
20966          * @private
20967          * @static
20968          */
20969         getScroll: function () {
20970             var t, l, dde=document.documentElement, db=document.body;
20971             if (dde && (dde.scrollTop || dde.scrollLeft)) {
20972                 t = dde.scrollTop;
20973                 l = dde.scrollLeft;
20974             } else if (db) {
20975                 t = db.scrollTop;
20976                 l = db.scrollLeft;
20977             } else {
20978
20979             }
20980             return { top: t, left: l };
20981         },
20982
20983         /**
20984          * Returns the specified element style property
20985          * @method getStyle
20986          * @param {HTMLElement} el          the element
20987          * @param {string}      styleProp   the style property
20988          * @return {string} The value of the style property
20989          * @deprecated use Roo.lib.Dom.getStyle
20990          * @static
20991          */
20992         getStyle: function(el, styleProp) {
20993             return Roo.fly(el).getStyle(styleProp);
20994         },
20995
20996         /**
20997          * Gets the scrollTop
20998          * @method getScrollTop
20999          * @return {int} the document's scrollTop
21000          * @static
21001          */
21002         getScrollTop: function () { return this.getScroll().top; },
21003
21004         /**
21005          * Gets the scrollLeft
21006          * @method getScrollLeft
21007          * @return {int} the document's scrollTop
21008          * @static
21009          */
21010         getScrollLeft: function () { return this.getScroll().left; },
21011
21012         /**
21013          * Sets the x/y position of an element to the location of the
21014          * target element.
21015          * @method moveToEl
21016          * @param {HTMLElement} moveEl      The element to move
21017          * @param {HTMLElement} targetEl    The position reference element
21018          * @static
21019          */
21020         moveToEl: function (moveEl, targetEl) {
21021             var aCoord = Roo.lib.Dom.getXY(targetEl);
21022             Roo.lib.Dom.setXY(moveEl, aCoord);
21023         },
21024
21025         /**
21026          * Numeric array sort function
21027          * @method numericSort
21028          * @static
21029          */
21030         numericSort: function(a, b) { return (a - b); },
21031
21032         /**
21033          * Internal counter
21034          * @property _timeoutCount
21035          * @private
21036          * @static
21037          */
21038         _timeoutCount: 0,
21039
21040         /**
21041          * Trying to make the load order less important.  Without this we get
21042          * an error if this file is loaded before the Event Utility.
21043          * @method _addListeners
21044          * @private
21045          * @static
21046          */
21047         _addListeners: function() {
21048             var DDM = Roo.dd.DDM;
21049             if ( Roo.lib.Event && document ) {
21050                 DDM._onLoad();
21051             } else {
21052                 if (DDM._timeoutCount > 2000) {
21053                 } else {
21054                     setTimeout(DDM._addListeners, 10);
21055                     if (document && document.body) {
21056                         DDM._timeoutCount += 1;
21057                     }
21058                 }
21059             }
21060         },
21061
21062         /**
21063          * Recursively searches the immediate parent and all child nodes for
21064          * the handle element in order to determine wheter or not it was
21065          * clicked.
21066          * @method handleWasClicked
21067          * @param node the html element to inspect
21068          * @static
21069          */
21070         handleWasClicked: function(node, id) {
21071             if (this.isHandle(id, node.id)) {
21072                 return true;
21073             } else {
21074                 // check to see if this is a text node child of the one we want
21075                 var p = node.parentNode;
21076
21077                 while (p) {
21078                     if (this.isHandle(id, p.id)) {
21079                         return true;
21080                     } else {
21081                         p = p.parentNode;
21082                     }
21083                 }
21084             }
21085
21086             return false;
21087         }
21088
21089     };
21090
21091 }();
21092
21093 // shorter alias, save a few bytes
21094 Roo.dd.DDM = Roo.dd.DragDropMgr;
21095 Roo.dd.DDM._addListeners();
21096
21097 }/*
21098  * Based on:
21099  * Ext JS Library 1.1.1
21100  * Copyright(c) 2006-2007, Ext JS, LLC.
21101  *
21102  * Originally Released Under LGPL - original licence link has changed is not relivant.
21103  *
21104  * Fork - LGPL
21105  * <script type="text/javascript">
21106  */
21107
21108 /**
21109  * @class Roo.dd.DD
21110  * A DragDrop implementation where the linked element follows the
21111  * mouse cursor during a drag.
21112  * @extends Roo.dd.DragDrop
21113  * @constructor
21114  * @param {String} id the id of the linked element
21115  * @param {String} sGroup the group of related DragDrop items
21116  * @param {object} config an object containing configurable attributes
21117  *                Valid properties for DD:
21118  *                    scroll
21119  */
21120 Roo.dd.DD = function(id, sGroup, config) {
21121     if (id) {
21122         this.init(id, sGroup, config);
21123     }
21124 };
21125
21126 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
21127
21128     /**
21129      * When set to true, the utility automatically tries to scroll the browser
21130      * window wehn a drag and drop element is dragged near the viewport boundary.
21131      * Defaults to true.
21132      * @property scroll
21133      * @type boolean
21134      */
21135     scroll: true,
21136
21137     /**
21138      * Sets the pointer offset to the distance between the linked element's top
21139      * left corner and the location the element was clicked
21140      * @method autoOffset
21141      * @param {int} iPageX the X coordinate of the click
21142      * @param {int} iPageY the Y coordinate of the click
21143      */
21144     autoOffset: function(iPageX, iPageY) {
21145         var x = iPageX - this.startPageX;
21146         var y = iPageY - this.startPageY;
21147         this.setDelta(x, y);
21148     },
21149
21150     /**
21151      * Sets the pointer offset.  You can call this directly to force the
21152      * offset to be in a particular location (e.g., pass in 0,0 to set it
21153      * to the center of the object)
21154      * @method setDelta
21155      * @param {int} iDeltaX the distance from the left
21156      * @param {int} iDeltaY the distance from the top
21157      */
21158     setDelta: function(iDeltaX, iDeltaY) {
21159         this.deltaX = iDeltaX;
21160         this.deltaY = iDeltaY;
21161     },
21162
21163     /**
21164      * Sets the drag element to the location of the mousedown or click event,
21165      * maintaining the cursor location relative to the location on the element
21166      * that was clicked.  Override this if you want to place the element in a
21167      * location other than where the cursor is.
21168      * @method setDragElPos
21169      * @param {int} iPageX the X coordinate of the mousedown or drag event
21170      * @param {int} iPageY the Y coordinate of the mousedown or drag event
21171      */
21172     setDragElPos: function(iPageX, iPageY) {
21173         // the first time we do this, we are going to check to make sure
21174         // the element has css positioning
21175
21176         var el = this.getDragEl();
21177         this.alignElWithMouse(el, iPageX, iPageY);
21178     },
21179
21180     /**
21181      * Sets the element to the location of the mousedown or click event,
21182      * maintaining the cursor location relative to the location on the element
21183      * that was clicked.  Override this if you want to place the element in a
21184      * location other than where the cursor is.
21185      * @method alignElWithMouse
21186      * @param {HTMLElement} el the element to move
21187      * @param {int} iPageX the X coordinate of the mousedown or drag event
21188      * @param {int} iPageY the Y coordinate of the mousedown or drag event
21189      */
21190     alignElWithMouse: function(el, iPageX, iPageY) {
21191         var oCoord = this.getTargetCoord(iPageX, iPageY);
21192         var fly = el.dom ? el : Roo.fly(el);
21193         if (!this.deltaSetXY) {
21194             var aCoord = [oCoord.x, oCoord.y];
21195             fly.setXY(aCoord);
21196             var newLeft = fly.getLeft(true);
21197             var newTop  = fly.getTop(true);
21198             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
21199         } else {
21200             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
21201         }
21202
21203         this.cachePosition(oCoord.x, oCoord.y);
21204         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
21205         return oCoord;
21206     },
21207
21208     /**
21209      * Saves the most recent position so that we can reset the constraints and
21210      * tick marks on-demand.  We need to know this so that we can calculate the
21211      * number of pixels the element is offset from its original position.
21212      * @method cachePosition
21213      * @param iPageX the current x position (optional, this just makes it so we
21214      * don't have to look it up again)
21215      * @param iPageY the current y position (optional, this just makes it so we
21216      * don't have to look it up again)
21217      */
21218     cachePosition: function(iPageX, iPageY) {
21219         if (iPageX) {
21220             this.lastPageX = iPageX;
21221             this.lastPageY = iPageY;
21222         } else {
21223             var aCoord = Roo.lib.Dom.getXY(this.getEl());
21224             this.lastPageX = aCoord[0];
21225             this.lastPageY = aCoord[1];
21226         }
21227     },
21228
21229     /**
21230      * Auto-scroll the window if the dragged object has been moved beyond the
21231      * visible window boundary.
21232      * @method autoScroll
21233      * @param {int} x the drag element's x position
21234      * @param {int} y the drag element's y position
21235      * @param {int} h the height of the drag element
21236      * @param {int} w the width of the drag element
21237      * @private
21238      */
21239     autoScroll: function(x, y, h, w) {
21240
21241         if (this.scroll) {
21242             // The client height
21243             var clientH = Roo.lib.Dom.getViewWidth();
21244
21245             // The client width
21246             var clientW = Roo.lib.Dom.getViewHeight();
21247
21248             // The amt scrolled down
21249             var st = this.DDM.getScrollTop();
21250
21251             // The amt scrolled right
21252             var sl = this.DDM.getScrollLeft();
21253
21254             // Location of the bottom of the element
21255             var bot = h + y;
21256
21257             // Location of the right of the element
21258             var right = w + x;
21259
21260             // The distance from the cursor to the bottom of the visible area,
21261             // adjusted so that we don't scroll if the cursor is beyond the
21262             // element drag constraints
21263             var toBot = (clientH + st - y - this.deltaY);
21264
21265             // The distance from the cursor to the right of the visible area
21266             var toRight = (clientW + sl - x - this.deltaX);
21267
21268
21269             // How close to the edge the cursor must be before we scroll
21270             // var thresh = (document.all) ? 100 : 40;
21271             var thresh = 40;
21272
21273             // How many pixels to scroll per autoscroll op.  This helps to reduce
21274             // clunky scrolling. IE is more sensitive about this ... it needs this
21275             // value to be higher.
21276             var scrAmt = (document.all) ? 80 : 30;
21277
21278             // Scroll down if we are near the bottom of the visible page and the
21279             // obj extends below the crease
21280             if ( bot > clientH && toBot < thresh ) {
21281                 window.scrollTo(sl, st + scrAmt);
21282             }
21283
21284             // Scroll up if the window is scrolled down and the top of the object
21285             // goes above the top border
21286             if ( y < st && st > 0 && y - st < thresh ) {
21287                 window.scrollTo(sl, st - scrAmt);
21288             }
21289
21290             // Scroll right if the obj is beyond the right border and the cursor is
21291             // near the border.
21292             if ( right > clientW && toRight < thresh ) {
21293                 window.scrollTo(sl + scrAmt, st);
21294             }
21295
21296             // Scroll left if the window has been scrolled to the right and the obj
21297             // extends past the left border
21298             if ( x < sl && sl > 0 && x - sl < thresh ) {
21299                 window.scrollTo(sl - scrAmt, st);
21300             }
21301         }
21302     },
21303
21304     /**
21305      * Finds the location the element should be placed if we want to move
21306      * it to where the mouse location less the click offset would place us.
21307      * @method getTargetCoord
21308      * @param {int} iPageX the X coordinate of the click
21309      * @param {int} iPageY the Y coordinate of the click
21310      * @return an object that contains the coordinates (Object.x and Object.y)
21311      * @private
21312      */
21313     getTargetCoord: function(iPageX, iPageY) {
21314
21315
21316         var x = iPageX - this.deltaX;
21317         var y = iPageY - this.deltaY;
21318
21319         if (this.constrainX) {
21320             if (x < this.minX) { x = this.minX; }
21321             if (x > this.maxX) { x = this.maxX; }
21322         }
21323
21324         if (this.constrainY) {
21325             if (y < this.minY) { y = this.minY; }
21326             if (y > this.maxY) { y = this.maxY; }
21327         }
21328
21329         x = this.getTick(x, this.xTicks);
21330         y = this.getTick(y, this.yTicks);
21331
21332
21333         return {x:x, y:y};
21334     },
21335
21336     /*
21337      * Sets up config options specific to this class. Overrides
21338      * Roo.dd.DragDrop, but all versions of this method through the
21339      * inheritance chain are called
21340      */
21341     applyConfig: function() {
21342         Roo.dd.DD.superclass.applyConfig.call(this);
21343         this.scroll = (this.config.scroll !== false);
21344     },
21345
21346     /*
21347      * Event that fires prior to the onMouseDown event.  Overrides
21348      * Roo.dd.DragDrop.
21349      */
21350     b4MouseDown: function(e) {
21351         // this.resetConstraints();
21352         this.autoOffset(e.getPageX(),
21353                             e.getPageY());
21354     },
21355
21356     /*
21357      * Event that fires prior to the onDrag event.  Overrides
21358      * Roo.dd.DragDrop.
21359      */
21360     b4Drag: function(e) {
21361         this.setDragElPos(e.getPageX(),
21362                             e.getPageY());
21363     },
21364
21365     toString: function() {
21366         return ("DD " + this.id);
21367     }
21368
21369     //////////////////////////////////////////////////////////////////////////
21370     // Debugging ygDragDrop events that can be overridden
21371     //////////////////////////////////////////////////////////////////////////
21372     /*
21373     startDrag: function(x, y) {
21374     },
21375
21376     onDrag: function(e) {
21377     },
21378
21379     onDragEnter: function(e, id) {
21380     },
21381
21382     onDragOver: function(e, id) {
21383     },
21384
21385     onDragOut: function(e, id) {
21386     },
21387
21388     onDragDrop: function(e, id) {
21389     },
21390
21391     endDrag: function(e) {
21392     }
21393
21394     */
21395
21396 });/*
21397  * Based on:
21398  * Ext JS Library 1.1.1
21399  * Copyright(c) 2006-2007, Ext JS, LLC.
21400  *
21401  * Originally Released Under LGPL - original licence link has changed is not relivant.
21402  *
21403  * Fork - LGPL
21404  * <script type="text/javascript">
21405  */
21406
21407 /**
21408  * @class Roo.dd.DDProxy
21409  * A DragDrop implementation that inserts an empty, bordered div into
21410  * the document that follows the cursor during drag operations.  At the time of
21411  * the click, the frame div is resized to the dimensions of the linked html
21412  * element, and moved to the exact location of the linked element.
21413  *
21414  * References to the "frame" element refer to the single proxy element that
21415  * was created to be dragged in place of all DDProxy elements on the
21416  * page.
21417  *
21418  * @extends Roo.dd.DD
21419  * @constructor
21420  * @param {String} id the id of the linked html element
21421  * @param {String} sGroup the group of related DragDrop objects
21422  * @param {object} config an object containing configurable attributes
21423  *                Valid properties for DDProxy in addition to those in DragDrop:
21424  *                   resizeFrame, centerFrame, dragElId
21425  */
21426 Roo.dd.DDProxy = function(id, sGroup, config) {
21427     if (id) {
21428         this.init(id, sGroup, config);
21429         this.initFrame();
21430     }
21431 };
21432
21433 /**
21434  * The default drag frame div id
21435  * @property Roo.dd.DDProxy.dragElId
21436  * @type String
21437  * @static
21438  */
21439 Roo.dd.DDProxy.dragElId = "ygddfdiv";
21440
21441 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
21442
21443     /**
21444      * By default we resize the drag frame to be the same size as the element
21445      * we want to drag (this is to get the frame effect).  We can turn it off
21446      * if we want a different behavior.
21447      * @property resizeFrame
21448      * @type boolean
21449      */
21450     resizeFrame: true,
21451
21452     /**
21453      * By default the frame is positioned exactly where the drag element is, so
21454      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
21455      * you do not have constraints on the obj is to have the drag frame centered
21456      * around the cursor.  Set centerFrame to true for this effect.
21457      * @property centerFrame
21458      * @type boolean
21459      */
21460     centerFrame: false,
21461
21462     /**
21463      * Creates the proxy element if it does not yet exist
21464      * @method createFrame
21465      */
21466     createFrame: function() {
21467         var self = this;
21468         var body = document.body;
21469
21470         if (!body || !body.firstChild) {
21471             setTimeout( function() { self.createFrame(); }, 50 );
21472             return;
21473         }
21474
21475         var div = this.getDragEl();
21476
21477         if (!div) {
21478             div    = document.createElement("div");
21479             div.id = this.dragElId;
21480             var s  = div.style;
21481
21482             s.position   = "absolute";
21483             s.visibility = "hidden";
21484             s.cursor     = "move";
21485             s.border     = "2px solid #aaa";
21486             s.zIndex     = 999;
21487
21488             // appendChild can blow up IE if invoked prior to the window load event
21489             // while rendering a table.  It is possible there are other scenarios
21490             // that would cause this to happen as well.
21491             body.insertBefore(div, body.firstChild);
21492         }
21493     },
21494
21495     /**
21496      * Initialization for the drag frame element.  Must be called in the
21497      * constructor of all subclasses
21498      * @method initFrame
21499      */
21500     initFrame: function() {
21501         this.createFrame();
21502     },
21503
21504     applyConfig: function() {
21505         Roo.dd.DDProxy.superclass.applyConfig.call(this);
21506
21507         this.resizeFrame = (this.config.resizeFrame !== false);
21508         this.centerFrame = (this.config.centerFrame);
21509         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
21510     },
21511
21512     /**
21513      * Resizes the drag frame to the dimensions of the clicked object, positions
21514      * it over the object, and finally displays it
21515      * @method showFrame
21516      * @param {int} iPageX X click position
21517      * @param {int} iPageY Y click position
21518      * @private
21519      */
21520     showFrame: function(iPageX, iPageY) {
21521         var el = this.getEl();
21522         var dragEl = this.getDragEl();
21523         var s = dragEl.style;
21524
21525         this._resizeProxy();
21526
21527         if (this.centerFrame) {
21528             this.setDelta( Math.round(parseInt(s.width,  10)/2),
21529                            Math.round(parseInt(s.height, 10)/2) );
21530         }
21531
21532         this.setDragElPos(iPageX, iPageY);
21533
21534         Roo.fly(dragEl).show();
21535     },
21536
21537     /**
21538      * The proxy is automatically resized to the dimensions of the linked
21539      * element when a drag is initiated, unless resizeFrame is set to false
21540      * @method _resizeProxy
21541      * @private
21542      */
21543     _resizeProxy: function() {
21544         if (this.resizeFrame) {
21545             var el = this.getEl();
21546             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
21547         }
21548     },
21549
21550     // overrides Roo.dd.DragDrop
21551     b4MouseDown: function(e) {
21552         var x = e.getPageX();
21553         var y = e.getPageY();
21554         this.autoOffset(x, y);
21555         this.setDragElPos(x, y);
21556     },
21557
21558     // overrides Roo.dd.DragDrop
21559     b4StartDrag: function(x, y) {
21560         // show the drag frame
21561         this.showFrame(x, y);
21562     },
21563
21564     // overrides Roo.dd.DragDrop
21565     b4EndDrag: function(e) {
21566         Roo.fly(this.getDragEl()).hide();
21567     },
21568
21569     // overrides Roo.dd.DragDrop
21570     // By default we try to move the element to the last location of the frame.
21571     // This is so that the default behavior mirrors that of Roo.dd.DD.
21572     endDrag: function(e) {
21573
21574         var lel = this.getEl();
21575         var del = this.getDragEl();
21576
21577         // Show the drag frame briefly so we can get its position
21578         del.style.visibility = "";
21579
21580         this.beforeMove();
21581         // Hide the linked element before the move to get around a Safari
21582         // rendering bug.
21583         lel.style.visibility = "hidden";
21584         Roo.dd.DDM.moveToEl(lel, del);
21585         del.style.visibility = "hidden";
21586         lel.style.visibility = "";
21587
21588         this.afterDrag();
21589     },
21590
21591     beforeMove : function(){
21592
21593     },
21594
21595     afterDrag : function(){
21596
21597     },
21598
21599     toString: function() {
21600         return ("DDProxy " + this.id);
21601     }
21602
21603 });
21604 /*
21605  * Based on:
21606  * Ext JS Library 1.1.1
21607  * Copyright(c) 2006-2007, Ext JS, LLC.
21608  *
21609  * Originally Released Under LGPL - original licence link has changed is not relivant.
21610  *
21611  * Fork - LGPL
21612  * <script type="text/javascript">
21613  */
21614
21615  /**
21616  * @class Roo.dd.DDTarget
21617  * A DragDrop implementation that does not move, but can be a drop
21618  * target.  You would get the same result by simply omitting implementation
21619  * for the event callbacks, but this way we reduce the processing cost of the
21620  * event listener and the callbacks.
21621  * @extends Roo.dd.DragDrop
21622  * @constructor
21623  * @param {String} id the id of the element that is a drop target
21624  * @param {String} sGroup the group of related DragDrop objects
21625  * @param {object} config an object containing configurable attributes
21626  *                 Valid properties for DDTarget in addition to those in
21627  *                 DragDrop:
21628  *                    none
21629  */
21630 Roo.dd.DDTarget = function(id, sGroup, config) {
21631     if (id) {
21632         this.initTarget(id, sGroup, config);
21633     }
21634     if (config && (config.listeners || config.events)) { 
21635         Roo.dd.DragDrop.superclass.constructor.call(this,  { 
21636             listeners : config.listeners || {}, 
21637             events : config.events || {} 
21638         });    
21639     }
21640 };
21641
21642 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
21643 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
21644     toString: function() {
21645         return ("DDTarget " + this.id);
21646     }
21647 });
21648 /*
21649  * Based on:
21650  * Ext JS Library 1.1.1
21651  * Copyright(c) 2006-2007, Ext JS, LLC.
21652  *
21653  * Originally Released Under LGPL - original licence link has changed is not relivant.
21654  *
21655  * Fork - LGPL
21656  * <script type="text/javascript">
21657  */
21658  
21659
21660 /**
21661  * @class Roo.dd.ScrollManager
21662  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
21663  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
21664  * @singleton
21665  */
21666 Roo.dd.ScrollManager = function(){
21667     var ddm = Roo.dd.DragDropMgr;
21668     var els = {};
21669     var dragEl = null;
21670     var proc = {};
21671     
21672     
21673     
21674     var onStop = function(e){
21675         dragEl = null;
21676         clearProc();
21677     };
21678     
21679     var triggerRefresh = function(){
21680         if(ddm.dragCurrent){
21681              ddm.refreshCache(ddm.dragCurrent.groups);
21682         }
21683     };
21684     
21685     var doScroll = function(){
21686         if(ddm.dragCurrent){
21687             var dds = Roo.dd.ScrollManager;
21688             if(!dds.animate){
21689                 if(proc.el.scroll(proc.dir, dds.increment)){
21690                     triggerRefresh();
21691                 }
21692             }else{
21693                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
21694             }
21695         }
21696     };
21697     
21698     var clearProc = function(){
21699         if(proc.id){
21700             clearInterval(proc.id);
21701         }
21702         proc.id = 0;
21703         proc.el = null;
21704         proc.dir = "";
21705     };
21706     
21707     var startProc = function(el, dir){
21708          Roo.log('scroll startproc');
21709         clearProc();
21710         proc.el = el;
21711         proc.dir = dir;
21712         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
21713     };
21714     
21715     var onFire = function(e, isDrop){
21716        
21717         if(isDrop || !ddm.dragCurrent){ return; }
21718         var dds = Roo.dd.ScrollManager;
21719         if(!dragEl || dragEl != ddm.dragCurrent){
21720             dragEl = ddm.dragCurrent;
21721             // refresh regions on drag start
21722             dds.refreshCache();
21723         }
21724         
21725         var xy = Roo.lib.Event.getXY(e);
21726         var pt = new Roo.lib.Point(xy[0], xy[1]);
21727         for(var id in els){
21728             var el = els[id], r = el._region;
21729             if(r && r.contains(pt) && el.isScrollable()){
21730                 if(r.bottom - pt.y <= dds.thresh){
21731                     if(proc.el != el){
21732                         startProc(el, "down");
21733                     }
21734                     return;
21735                 }else if(r.right - pt.x <= dds.thresh){
21736                     if(proc.el != el){
21737                         startProc(el, "left");
21738                     }
21739                     return;
21740                 }else if(pt.y - r.top <= dds.thresh){
21741                     if(proc.el != el){
21742                         startProc(el, "up");
21743                     }
21744                     return;
21745                 }else if(pt.x - r.left <= dds.thresh){
21746                     if(proc.el != el){
21747                         startProc(el, "right");
21748                     }
21749                     return;
21750                 }
21751             }
21752         }
21753         clearProc();
21754     };
21755     
21756     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
21757     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
21758     
21759     return {
21760         /**
21761          * Registers new overflow element(s) to auto scroll
21762          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
21763          */
21764         register : function(el){
21765             if(el instanceof Array){
21766                 for(var i = 0, len = el.length; i < len; i++) {
21767                         this.register(el[i]);
21768                 }
21769             }else{
21770                 el = Roo.get(el);
21771                 els[el.id] = el;
21772             }
21773             Roo.dd.ScrollManager.els = els;
21774         },
21775         
21776         /**
21777          * Unregisters overflow element(s) so they are no longer scrolled
21778          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
21779          */
21780         unregister : function(el){
21781             if(el instanceof Array){
21782                 for(var i = 0, len = el.length; i < len; i++) {
21783                         this.unregister(el[i]);
21784                 }
21785             }else{
21786                 el = Roo.get(el);
21787                 delete els[el.id];
21788             }
21789         },
21790         
21791         /**
21792          * The number of pixels from the edge of a container the pointer needs to be to 
21793          * trigger scrolling (defaults to 25)
21794          * @type Number
21795          */
21796         thresh : 25,
21797         
21798         /**
21799          * The number of pixels to scroll in each scroll increment (defaults to 50)
21800          * @type Number
21801          */
21802         increment : 100,
21803         
21804         /**
21805          * The frequency of scrolls in milliseconds (defaults to 500)
21806          * @type Number
21807          */
21808         frequency : 500,
21809         
21810         /**
21811          * True to animate the scroll (defaults to true)
21812          * @type Boolean
21813          */
21814         animate: true,
21815         
21816         /**
21817          * The animation duration in seconds - 
21818          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
21819          * @type Number
21820          */
21821         animDuration: .4,
21822         
21823         /**
21824          * Manually trigger a cache refresh.
21825          */
21826         refreshCache : function(){
21827             for(var id in els){
21828                 if(typeof els[id] == 'object'){ // for people extending the object prototype
21829                     els[id]._region = els[id].getRegion();
21830                 }
21831             }
21832         }
21833     };
21834 }();/*
21835  * Based on:
21836  * Ext JS Library 1.1.1
21837  * Copyright(c) 2006-2007, Ext JS, LLC.
21838  *
21839  * Originally Released Under LGPL - original licence link has changed is not relivant.
21840  *
21841  * Fork - LGPL
21842  * <script type="text/javascript">
21843  */
21844  
21845
21846 /**
21847  * @class Roo.dd.Registry
21848  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
21849  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
21850  * @singleton
21851  */
21852 Roo.dd.Registry = function(){
21853     var elements = {}; 
21854     var handles = {}; 
21855     var autoIdSeed = 0;
21856
21857     var getId = function(el, autogen){
21858         if(typeof el == "string"){
21859             return el;
21860         }
21861         var id = el.id;
21862         if(!id && autogen !== false){
21863             id = "roodd-" + (++autoIdSeed);
21864             el.id = id;
21865         }
21866         return id;
21867     };
21868     
21869     return {
21870     /**
21871      * Register a drag drop element
21872      * @param {String|HTMLElement} element The id or DOM node to register
21873      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
21874      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
21875      * knows how to interpret, plus there are some specific properties known to the Registry that should be
21876      * populated in the data object (if applicable):
21877      * <pre>
21878 Value      Description<br />
21879 ---------  ------------------------------------------<br />
21880 handles    Array of DOM nodes that trigger dragging<br />
21881            for the element being registered<br />
21882 isHandle   True if the element passed in triggers<br />
21883            dragging itself, else false
21884 </pre>
21885      */
21886         register : function(el, data){
21887             data = data || {};
21888             if(typeof el == "string"){
21889                 el = document.getElementById(el);
21890             }
21891             data.ddel = el;
21892             elements[getId(el)] = data;
21893             if(data.isHandle !== false){
21894                 handles[data.ddel.id] = data;
21895             }
21896             if(data.handles){
21897                 var hs = data.handles;
21898                 for(var i = 0, len = hs.length; i < len; i++){
21899                         handles[getId(hs[i])] = data;
21900                 }
21901             }
21902         },
21903
21904     /**
21905      * Unregister a drag drop element
21906      * @param {String|HTMLElement}  element The id or DOM node to unregister
21907      */
21908         unregister : function(el){
21909             var id = getId(el, false);
21910             var data = elements[id];
21911             if(data){
21912                 delete elements[id];
21913                 if(data.handles){
21914                     var hs = data.handles;
21915                     for(var i = 0, len = hs.length; i < len; i++){
21916                         delete handles[getId(hs[i], false)];
21917                     }
21918                 }
21919             }
21920         },
21921
21922     /**
21923      * Returns the handle registered for a DOM Node by id
21924      * @param {String|HTMLElement} id The DOM node or id to look up
21925      * @return {Object} handle The custom handle data
21926      */
21927         getHandle : function(id){
21928             if(typeof id != "string"){ // must be element?
21929                 id = id.id;
21930             }
21931             return handles[id];
21932         },
21933
21934     /**
21935      * Returns the handle that is registered for the DOM node that is the target of the event
21936      * @param {Event} e The event
21937      * @return {Object} handle The custom handle data
21938      */
21939         getHandleFromEvent : function(e){
21940             var t = Roo.lib.Event.getTarget(e);
21941             return t ? handles[t.id] : null;
21942         },
21943
21944     /**
21945      * Returns a custom data object that is registered for a DOM node by id
21946      * @param {String|HTMLElement} id The DOM node or id to look up
21947      * @return {Object} data The custom data
21948      */
21949         getTarget : function(id){
21950             if(typeof id != "string"){ // must be element?
21951                 id = id.id;
21952             }
21953             return elements[id];
21954         },
21955
21956     /**
21957      * Returns a custom data object that is registered for the DOM node that is the target of the event
21958      * @param {Event} e The event
21959      * @return {Object} data The custom data
21960      */
21961         getTargetFromEvent : function(e){
21962             var t = Roo.lib.Event.getTarget(e);
21963             return t ? elements[t.id] || handles[t.id] : null;
21964         }
21965     };
21966 }();/*
21967  * Based on:
21968  * Ext JS Library 1.1.1
21969  * Copyright(c) 2006-2007, Ext JS, LLC.
21970  *
21971  * Originally Released Under LGPL - original licence link has changed is not relivant.
21972  *
21973  * Fork - LGPL
21974  * <script type="text/javascript">
21975  */
21976  
21977
21978 /**
21979  * @class Roo.dd.StatusProxy
21980  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
21981  * default drag proxy used by all Roo.dd components.
21982  * @constructor
21983  * @param {Object} config
21984  */
21985 Roo.dd.StatusProxy = function(config){
21986     Roo.apply(this, config);
21987     this.id = this.id || Roo.id();
21988     this.el = new Roo.Layer({
21989         dh: {
21990             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
21991                 {tag: "div", cls: "x-dd-drop-icon"},
21992                 {tag: "div", cls: "x-dd-drag-ghost"}
21993             ]
21994         }, 
21995         shadow: !config || config.shadow !== false
21996     });
21997     this.ghost = Roo.get(this.el.dom.childNodes[1]);
21998     this.dropStatus = this.dropNotAllowed;
21999 };
22000
22001 Roo.dd.StatusProxy.prototype = {
22002     /**
22003      * @cfg {String} dropAllowed
22004      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
22005      */
22006     dropAllowed : "x-dd-drop-ok",
22007     /**
22008      * @cfg {String} dropNotAllowed
22009      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
22010      */
22011     dropNotAllowed : "x-dd-drop-nodrop",
22012
22013     /**
22014      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
22015      * over the current target element.
22016      * @param {String} cssClass The css class for the new drop status indicator image
22017      */
22018     setStatus : function(cssClass){
22019         cssClass = cssClass || this.dropNotAllowed;
22020         if(this.dropStatus != cssClass){
22021             this.el.replaceClass(this.dropStatus, cssClass);
22022             this.dropStatus = cssClass;
22023         }
22024     },
22025
22026     /**
22027      * Resets the status indicator to the default dropNotAllowed value
22028      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
22029      */
22030     reset : function(clearGhost){
22031         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
22032         this.dropStatus = this.dropNotAllowed;
22033         if(clearGhost){
22034             this.ghost.update("");
22035         }
22036     },
22037
22038     /**
22039      * Updates the contents of the ghost element
22040      * @param {String} html The html that will replace the current innerHTML of the ghost element
22041      */
22042     update : function(html){
22043         if(typeof html == "string"){
22044             this.ghost.update(html);
22045         }else{
22046             this.ghost.update("");
22047             html.style.margin = "0";
22048             this.ghost.dom.appendChild(html);
22049         }
22050         // ensure float = none set?? cant remember why though.
22051         var el = this.ghost.dom.firstChild;
22052                 if(el){
22053                         Roo.fly(el).setStyle('float', 'none');
22054                 }
22055     },
22056     
22057     /**
22058      * Returns the underlying proxy {@link Roo.Layer}
22059      * @return {Roo.Layer} el
22060     */
22061     getEl : function(){
22062         return this.el;
22063     },
22064
22065     /**
22066      * Returns the ghost element
22067      * @return {Roo.Element} el
22068      */
22069     getGhost : function(){
22070         return this.ghost;
22071     },
22072
22073     /**
22074      * Hides the proxy
22075      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
22076      */
22077     hide : function(clear){
22078         this.el.hide();
22079         if(clear){
22080             this.reset(true);
22081         }
22082     },
22083
22084     /**
22085      * Stops the repair animation if it's currently running
22086      */
22087     stop : function(){
22088         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
22089             this.anim.stop();
22090         }
22091     },
22092
22093     /**
22094      * Displays this proxy
22095      */
22096     show : function(){
22097         this.el.show();
22098     },
22099
22100     /**
22101      * Force the Layer to sync its shadow and shim positions to the element
22102      */
22103     sync : function(){
22104         this.el.sync();
22105     },
22106
22107     /**
22108      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
22109      * invalid drop operation by the item being dragged.
22110      * @param {Array} xy The XY position of the element ([x, y])
22111      * @param {Function} callback The function to call after the repair is complete
22112      * @param {Object} scope The scope in which to execute the callback
22113      */
22114     repair : function(xy, callback, scope){
22115         this.callback = callback;
22116         this.scope = scope;
22117         if(xy && this.animRepair !== false){
22118             this.el.addClass("x-dd-drag-repair");
22119             this.el.hideUnders(true);
22120             this.anim = this.el.shift({
22121                 duration: this.repairDuration || .5,
22122                 easing: 'easeOut',
22123                 xy: xy,
22124                 stopFx: true,
22125                 callback: this.afterRepair,
22126                 scope: this
22127             });
22128         }else{
22129             this.afterRepair();
22130         }
22131     },
22132
22133     // private
22134     afterRepair : function(){
22135         this.hide(true);
22136         if(typeof this.callback == "function"){
22137             this.callback.call(this.scope || this);
22138         }
22139         this.callback = null;
22140         this.scope = null;
22141     }
22142 };/*
22143  * Based on:
22144  * Ext JS Library 1.1.1
22145  * Copyright(c) 2006-2007, Ext JS, LLC.
22146  *
22147  * Originally Released Under LGPL - original licence link has changed is not relivant.
22148  *
22149  * Fork - LGPL
22150  * <script type="text/javascript">
22151  */
22152
22153 /**
22154  * @class Roo.dd.DragSource
22155  * @extends Roo.dd.DDProxy
22156  * A simple class that provides the basic implementation needed to make any element draggable.
22157  * @constructor
22158  * @param {String/HTMLElement/Element} el The container element
22159  * @param {Object} config
22160  */
22161 Roo.dd.DragSource = function(el, config){
22162     this.el = Roo.get(el);
22163     this.dragData = {};
22164     
22165     Roo.apply(this, config);
22166     
22167     if(!this.proxy){
22168         this.proxy = new Roo.dd.StatusProxy();
22169     }
22170
22171     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
22172           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
22173     
22174     this.dragging = false;
22175 };
22176
22177 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
22178     /**
22179      * @cfg {String} dropAllowed
22180      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
22181      */
22182     dropAllowed : "x-dd-drop-ok",
22183     /**
22184      * @cfg {String} dropNotAllowed
22185      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
22186      */
22187     dropNotAllowed : "x-dd-drop-nodrop",
22188
22189     /**
22190      * Returns the data object associated with this drag source
22191      * @return {Object} data An object containing arbitrary data
22192      */
22193     getDragData : function(e){
22194         return this.dragData;
22195     },
22196
22197     // private
22198     onDragEnter : function(e, id){
22199         var target = Roo.dd.DragDropMgr.getDDById(id);
22200         this.cachedTarget = target;
22201         if(this.beforeDragEnter(target, e, id) !== false){
22202             if(target.isNotifyTarget){
22203                 var status = target.notifyEnter(this, e, this.dragData);
22204                 this.proxy.setStatus(status);
22205             }else{
22206                 this.proxy.setStatus(this.dropAllowed);
22207             }
22208             
22209             if(this.afterDragEnter){
22210                 /**
22211                  * An empty function by default, but provided so that you can perform a custom action
22212                  * when the dragged item enters the drop target by providing an implementation.
22213                  * @param {Roo.dd.DragDrop} target The drop target
22214                  * @param {Event} e The event object
22215                  * @param {String} id The id of the dragged element
22216                  * @method afterDragEnter
22217                  */
22218                 this.afterDragEnter(target, e, id);
22219             }
22220         }
22221     },
22222
22223     /**
22224      * An empty function by default, but provided so that you can perform a custom action
22225      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
22226      * @param {Roo.dd.DragDrop} target The drop target
22227      * @param {Event} e The event object
22228      * @param {String} id The id of the dragged element
22229      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
22230      */
22231     beforeDragEnter : function(target, e, id){
22232         return true;
22233     },
22234
22235     // private
22236     alignElWithMouse: function() {
22237         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
22238         this.proxy.sync();
22239     },
22240
22241     // private
22242     onDragOver : function(e, id){
22243         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
22244         if(this.beforeDragOver(target, e, id) !== false){
22245             if(target.isNotifyTarget){
22246                 var status = target.notifyOver(this, e, this.dragData);
22247                 this.proxy.setStatus(status);
22248             }
22249
22250             if(this.afterDragOver){
22251                 /**
22252                  * An empty function by default, but provided so that you can perform a custom action
22253                  * while the dragged item is over the drop target by providing an implementation.
22254                  * @param {Roo.dd.DragDrop} target The drop target
22255                  * @param {Event} e The event object
22256                  * @param {String} id The id of the dragged element
22257                  * @method afterDragOver
22258                  */
22259                 this.afterDragOver(target, e, id);
22260             }
22261         }
22262     },
22263
22264     /**
22265      * An empty function by default, but provided so that you can perform a custom action
22266      * while the dragged item is over the drop target and optionally cancel the onDragOver.
22267      * @param {Roo.dd.DragDrop} target The drop target
22268      * @param {Event} e The event object
22269      * @param {String} id The id of the dragged element
22270      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
22271      */
22272     beforeDragOver : function(target, e, id){
22273         return true;
22274     },
22275
22276     // private
22277     onDragOut : function(e, id){
22278         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
22279         if(this.beforeDragOut(target, e, id) !== false){
22280             if(target.isNotifyTarget){
22281                 target.notifyOut(this, e, this.dragData);
22282             }
22283             this.proxy.reset();
22284             if(this.afterDragOut){
22285                 /**
22286                  * An empty function by default, but provided so that you can perform a custom action
22287                  * after the dragged item is dragged out of the target without dropping.
22288                  * @param {Roo.dd.DragDrop} target The drop target
22289                  * @param {Event} e The event object
22290                  * @param {String} id The id of the dragged element
22291                  * @method afterDragOut
22292                  */
22293                 this.afterDragOut(target, e, id);
22294             }
22295         }
22296         this.cachedTarget = null;
22297     },
22298
22299     /**
22300      * An empty function by default, but provided so that you can perform a custom action before the dragged
22301      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
22302      * @param {Roo.dd.DragDrop} target The drop target
22303      * @param {Event} e The event object
22304      * @param {String} id The id of the dragged element
22305      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
22306      */
22307     beforeDragOut : function(target, e, id){
22308         return true;
22309     },
22310     
22311     // private
22312     onDragDrop : function(e, id){
22313         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
22314         if(this.beforeDragDrop(target, e, id) !== false){
22315             if(target.isNotifyTarget){
22316                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
22317                     this.onValidDrop(target, e, id);
22318                 }else{
22319                     this.onInvalidDrop(target, e, id);
22320                 }
22321             }else{
22322                 this.onValidDrop(target, e, id);
22323             }
22324             
22325             if(this.afterDragDrop){
22326                 /**
22327                  * An empty function by default, but provided so that you can perform a custom action
22328                  * after a valid drag drop has occurred by providing an implementation.
22329                  * @param {Roo.dd.DragDrop} target The drop target
22330                  * @param {Event} e The event object
22331                  * @param {String} id The id of the dropped element
22332                  * @method afterDragDrop
22333                  */
22334                 this.afterDragDrop(target, e, id);
22335             }
22336         }
22337         delete this.cachedTarget;
22338     },
22339
22340     /**
22341      * An empty function by default, but provided so that you can perform a custom action before the dragged
22342      * item is dropped onto the target and optionally cancel the onDragDrop.
22343      * @param {Roo.dd.DragDrop} target The drop target
22344      * @param {Event} e The event object
22345      * @param {String} id The id of the dragged element
22346      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
22347      */
22348     beforeDragDrop : function(target, e, id){
22349         return true;
22350     },
22351
22352     // private
22353     onValidDrop : function(target, e, id){
22354         this.hideProxy();
22355         if(this.afterValidDrop){
22356             /**
22357              * An empty function by default, but provided so that you can perform a custom action
22358              * after a valid drop has occurred by providing an implementation.
22359              * @param {Object} target The target DD 
22360              * @param {Event} e The event object
22361              * @param {String} id The id of the dropped element
22362              * @method afterInvalidDrop
22363              */
22364             this.afterValidDrop(target, e, id);
22365         }
22366     },
22367
22368     // private
22369     getRepairXY : function(e, data){
22370         return this.el.getXY();  
22371     },
22372
22373     // private
22374     onInvalidDrop : function(target, e, id){
22375         this.beforeInvalidDrop(target, e, id);
22376         if(this.cachedTarget){
22377             if(this.cachedTarget.isNotifyTarget){
22378                 this.cachedTarget.notifyOut(this, e, this.dragData);
22379             }
22380             this.cacheTarget = null;
22381         }
22382         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
22383
22384         if(this.afterInvalidDrop){
22385             /**
22386              * An empty function by default, but provided so that you can perform a custom action
22387              * after an invalid drop has occurred by providing an implementation.
22388              * @param {Event} e The event object
22389              * @param {String} id The id of the dropped element
22390              * @method afterInvalidDrop
22391              */
22392             this.afterInvalidDrop(e, id);
22393         }
22394     },
22395
22396     // private
22397     afterRepair : function(){
22398         if(Roo.enableFx){
22399             this.el.highlight(this.hlColor || "c3daf9");
22400         }
22401         this.dragging = false;
22402     },
22403
22404     /**
22405      * An empty function by default, but provided so that you can perform a custom action after an invalid
22406      * drop has occurred.
22407      * @param {Roo.dd.DragDrop} target The drop target
22408      * @param {Event} e The event object
22409      * @param {String} id The id of the dragged element
22410      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
22411      */
22412     beforeInvalidDrop : function(target, e, id){
22413         return true;
22414     },
22415
22416     // private
22417     handleMouseDown : function(e){
22418         if(this.dragging) {
22419             return;
22420         }
22421         var data = this.getDragData(e);
22422         if(data && this.onBeforeDrag(data, e) !== false){
22423             this.dragData = data;
22424             this.proxy.stop();
22425             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
22426         } 
22427     },
22428
22429     /**
22430      * An empty function by default, but provided so that you can perform a custom action before the initial
22431      * drag event begins and optionally cancel it.
22432      * @param {Object} data An object containing arbitrary data to be shared with drop targets
22433      * @param {Event} e The event object
22434      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
22435      */
22436     onBeforeDrag : function(data, e){
22437         return true;
22438     },
22439
22440     /**
22441      * An empty function by default, but provided so that you can perform a custom action once the initial
22442      * drag event has begun.  The drag cannot be canceled from this function.
22443      * @param {Number} x The x position of the click on the dragged object
22444      * @param {Number} y The y position of the click on the dragged object
22445      */
22446     onStartDrag : Roo.emptyFn,
22447
22448     // private - YUI override
22449     startDrag : function(x, y){
22450         this.proxy.reset();
22451         this.dragging = true;
22452         this.proxy.update("");
22453         this.onInitDrag(x, y);
22454         this.proxy.show();
22455     },
22456
22457     // private
22458     onInitDrag : function(x, y){
22459         var clone = this.el.dom.cloneNode(true);
22460         clone.id = Roo.id(); // prevent duplicate ids
22461         this.proxy.update(clone);
22462         this.onStartDrag(x, y);
22463         return true;
22464     },
22465
22466     /**
22467      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
22468      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
22469      */
22470     getProxy : function(){
22471         return this.proxy;  
22472     },
22473
22474     /**
22475      * Hides the drag source's {@link Roo.dd.StatusProxy}
22476      */
22477     hideProxy : function(){
22478         this.proxy.hide();  
22479         this.proxy.reset(true);
22480         this.dragging = false;
22481     },
22482
22483     // private
22484     triggerCacheRefresh : function(){
22485         Roo.dd.DDM.refreshCache(this.groups);
22486     },
22487
22488     // private - override to prevent hiding
22489     b4EndDrag: function(e) {
22490     },
22491
22492     // private - override to prevent moving
22493     endDrag : function(e){
22494         this.onEndDrag(this.dragData, e);
22495     },
22496
22497     // private
22498     onEndDrag : function(data, e){
22499     },
22500     
22501     // private - pin to cursor
22502     autoOffset : function(x, y) {
22503         this.setDelta(-12, -20);
22504     }    
22505 });/*
22506  * Based on:
22507  * Ext JS Library 1.1.1
22508  * Copyright(c) 2006-2007, Ext JS, LLC.
22509  *
22510  * Originally Released Under LGPL - original licence link has changed is not relivant.
22511  *
22512  * Fork - LGPL
22513  * <script type="text/javascript">
22514  */
22515
22516
22517 /**
22518  * @class Roo.dd.DropTarget
22519  * @extends Roo.dd.DDTarget
22520  * A simple class that provides the basic implementation needed to make any element a drop target that can have
22521  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
22522  * @constructor
22523  * @param {String/HTMLElement/Element} el The container element
22524  * @param {Object} config
22525  */
22526 Roo.dd.DropTarget = function(el, config){
22527     this.el = Roo.get(el);
22528     
22529     var listeners = false; ;
22530     if (config && config.listeners) {
22531         listeners= config.listeners;
22532         delete config.listeners;
22533     }
22534     Roo.apply(this, config);
22535     
22536     if(this.containerScroll){
22537         Roo.dd.ScrollManager.register(this.el);
22538     }
22539     this.addEvents( {
22540          /**
22541          * @scope Roo.dd.DropTarget
22542          */
22543          
22544          /**
22545          * @event enter
22546          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
22547          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
22548          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
22549          * 
22550          * IMPORTANT : it should set this.overClass and this.dropAllowed
22551          * 
22552          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22553          * @param {Event} e The event
22554          * @param {Object} data An object containing arbitrary data supplied by the drag source
22555          */
22556         "enter" : true,
22557         
22558          /**
22559          * @event over
22560          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
22561          * This method will be called on every mouse movement while the drag source is over the drop target.
22562          * This default implementation simply returns the dropAllowed config value.
22563          * 
22564          * IMPORTANT : it should set this.dropAllowed
22565          * 
22566          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22567          * @param {Event} e The event
22568          * @param {Object} data An object containing arbitrary data supplied by the drag source
22569          
22570          */
22571         "over" : true,
22572         /**
22573          * @event out
22574          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
22575          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
22576          * overClass (if any) from the drop element.
22577          * 
22578          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22579          * @param {Event} e The event
22580          * @param {Object} data An object containing arbitrary data supplied by the drag source
22581          */
22582          "out" : true,
22583          
22584         /**
22585          * @event drop
22586          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
22587          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
22588          * implementation that does something to process the drop event and returns true so that the drag source's
22589          * repair action does not run.
22590          * 
22591          * IMPORTANT : it should set this.success
22592          * 
22593          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22594          * @param {Event} e The event
22595          * @param {Object} data An object containing arbitrary data supplied by the drag source
22596         */
22597          "drop" : true
22598     });
22599             
22600      
22601     Roo.dd.DropTarget.superclass.constructor.call(  this, 
22602         this.el.dom, 
22603         this.ddGroup || this.group,
22604         {
22605             isTarget: true,
22606             listeners : listeners || {} 
22607            
22608         
22609         }
22610     );
22611
22612 };
22613
22614 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
22615     /**
22616      * @cfg {String} overClass
22617      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
22618      */
22619      /**
22620      * @cfg {String} ddGroup
22621      * The drag drop group to handle drop events for
22622      */
22623      
22624     /**
22625      * @cfg {String} dropAllowed
22626      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
22627      */
22628     dropAllowed : "x-dd-drop-ok",
22629     /**
22630      * @cfg {String} dropNotAllowed
22631      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
22632      */
22633     dropNotAllowed : "x-dd-drop-nodrop",
22634     /**
22635      * @cfg {boolean} success
22636      * set this after drop listener.. 
22637      */
22638     success : false,
22639     /**
22640      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
22641      * if the drop point is valid for over/enter..
22642      */
22643     valid : false,
22644     // private
22645     isTarget : true,
22646
22647     // private
22648     isNotifyTarget : true,
22649     
22650     /**
22651      * @hide
22652      */
22653     notifyEnter : function(dd, e, data)
22654     {
22655         this.valid = true;
22656         this.fireEvent('enter', dd, e, data);
22657         if(this.overClass){
22658             this.el.addClass(this.overClass);
22659         }
22660         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22661             this.valid ? this.dropAllowed : this.dropNotAllowed
22662         );
22663     },
22664
22665     /**
22666      * @hide
22667      */
22668     notifyOver : function(dd, e, data)
22669     {
22670         this.valid = true;
22671         this.fireEvent('over', dd, e, data);
22672         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22673             this.valid ? this.dropAllowed : this.dropNotAllowed
22674         );
22675     },
22676
22677     /**
22678      * @hide
22679      */
22680     notifyOut : function(dd, e, data)
22681     {
22682         this.fireEvent('out', dd, e, data);
22683         if(this.overClass){
22684             this.el.removeClass(this.overClass);
22685         }
22686     },
22687
22688     /**
22689      * @hide
22690      */
22691     notifyDrop : function(dd, e, data)
22692     {
22693         this.success = false;
22694         this.fireEvent('drop', dd, e, data);
22695         return this.success;
22696     }
22697 });/*
22698  * Based on:
22699  * Ext JS Library 1.1.1
22700  * Copyright(c) 2006-2007, Ext JS, LLC.
22701  *
22702  * Originally Released Under LGPL - original licence link has changed is not relivant.
22703  *
22704  * Fork - LGPL
22705  * <script type="text/javascript">
22706  */
22707
22708
22709 /**
22710  * @class Roo.dd.DragZone
22711  * @extends Roo.dd.DragSource
22712  * This class provides a container DD instance that proxies for multiple child node sources.<br />
22713  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
22714  * @constructor
22715  * @param {String/HTMLElement/Element} el The container element
22716  * @param {Object} config
22717  */
22718 Roo.dd.DragZone = function(el, config){
22719     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
22720     if(this.containerScroll){
22721         Roo.dd.ScrollManager.register(this.el);
22722     }
22723 };
22724
22725 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
22726     /**
22727      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
22728      * for auto scrolling during drag operations.
22729      */
22730     /**
22731      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
22732      * method after a failed drop (defaults to "c3daf9" - light blue)
22733      */
22734
22735     /**
22736      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
22737      * for a valid target to drag based on the mouse down. Override this method
22738      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
22739      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
22740      * @param {EventObject} e The mouse down event
22741      * @return {Object} The dragData
22742      */
22743     getDragData : function(e){
22744         return Roo.dd.Registry.getHandleFromEvent(e);
22745     },
22746     
22747     /**
22748      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
22749      * this.dragData.ddel
22750      * @param {Number} x The x position of the click on the dragged object
22751      * @param {Number} y The y position of the click on the dragged object
22752      * @return {Boolean} true to continue the drag, false to cancel
22753      */
22754     onInitDrag : function(x, y){
22755         this.proxy.update(this.dragData.ddel.cloneNode(true));
22756         this.onStartDrag(x, y);
22757         return true;
22758     },
22759     
22760     /**
22761      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
22762      */
22763     afterRepair : function(){
22764         if(Roo.enableFx){
22765             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
22766         }
22767         this.dragging = false;
22768     },
22769
22770     /**
22771      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
22772      * the XY of this.dragData.ddel
22773      * @param {EventObject} e The mouse up event
22774      * @return {Array} The xy location (e.g. [100, 200])
22775      */
22776     getRepairXY : function(e){
22777         return Roo.Element.fly(this.dragData.ddel).getXY();  
22778     }
22779 });/*
22780  * Based on:
22781  * Ext JS Library 1.1.1
22782  * Copyright(c) 2006-2007, Ext JS, LLC.
22783  *
22784  * Originally Released Under LGPL - original licence link has changed is not relivant.
22785  *
22786  * Fork - LGPL
22787  * <script type="text/javascript">
22788  */
22789 /**
22790  * @class Roo.dd.DropZone
22791  * @extends Roo.dd.DropTarget
22792  * This class provides a container DD instance that proxies for multiple child node targets.<br />
22793  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
22794  * @constructor
22795  * @param {String/HTMLElement/Element} el The container element
22796  * @param {Object} config
22797  */
22798 Roo.dd.DropZone = function(el, config){
22799     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
22800 };
22801
22802 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
22803     /**
22804      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
22805      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
22806      * provide your own custom lookup.
22807      * @param {Event} e The event
22808      * @return {Object} data The custom data
22809      */
22810     getTargetFromEvent : function(e){
22811         return Roo.dd.Registry.getTargetFromEvent(e);
22812     },
22813
22814     /**
22815      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
22816      * that it has registered.  This method has no default implementation and should be overridden to provide
22817      * node-specific processing if necessary.
22818      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
22819      * {@link #getTargetFromEvent} for this node)
22820      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22821      * @param {Event} e The event
22822      * @param {Object} data An object containing arbitrary data supplied by the drag source
22823      */
22824     onNodeEnter : function(n, dd, e, data){
22825         
22826     },
22827
22828     /**
22829      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
22830      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
22831      * overridden to provide the proper feedback.
22832      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22833      * {@link #getTargetFromEvent} for this node)
22834      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22835      * @param {Event} e The event
22836      * @param {Object} data An object containing arbitrary data supplied by the drag source
22837      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22838      * underlying {@link Roo.dd.StatusProxy} can be updated
22839      */
22840     onNodeOver : function(n, dd, e, data){
22841         return this.dropAllowed;
22842     },
22843
22844     /**
22845      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
22846      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
22847      * node-specific processing if necessary.
22848      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22849      * {@link #getTargetFromEvent} for this node)
22850      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22851      * @param {Event} e The event
22852      * @param {Object} data An object containing arbitrary data supplied by the drag source
22853      */
22854     onNodeOut : function(n, dd, e, data){
22855         
22856     },
22857
22858     /**
22859      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
22860      * the drop node.  The default implementation returns false, so it should be overridden to provide the
22861      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
22862      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22863      * {@link #getTargetFromEvent} for this node)
22864      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22865      * @param {Event} e The event
22866      * @param {Object} data An object containing arbitrary data supplied by the drag source
22867      * @return {Boolean} True if the drop was valid, else false
22868      */
22869     onNodeDrop : function(n, dd, e, data){
22870         return false;
22871     },
22872
22873     /**
22874      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
22875      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
22876      * it should be overridden to provide the proper feedback if necessary.
22877      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22878      * @param {Event} e The event
22879      * @param {Object} data An object containing arbitrary data supplied by the drag source
22880      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22881      * underlying {@link Roo.dd.StatusProxy} can be updated
22882      */
22883     onContainerOver : function(dd, e, data){
22884         return this.dropNotAllowed;
22885     },
22886
22887     /**
22888      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
22889      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
22890      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
22891      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
22892      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22893      * @param {Event} e The event
22894      * @param {Object} data An object containing arbitrary data supplied by the drag source
22895      * @return {Boolean} True if the drop was valid, else false
22896      */
22897     onContainerDrop : function(dd, e, data){
22898         return false;
22899     },
22900
22901     /**
22902      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
22903      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
22904      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
22905      * you should override this method and provide a custom implementation.
22906      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22907      * @param {Event} e The event
22908      * @param {Object} data An object containing arbitrary data supplied by the drag source
22909      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22910      * underlying {@link Roo.dd.StatusProxy} can be updated
22911      */
22912     notifyEnter : function(dd, e, data){
22913         return this.dropNotAllowed;
22914     },
22915
22916     /**
22917      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
22918      * This method will be called on every mouse movement while the drag source is over the drop zone.
22919      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
22920      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
22921      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
22922      * registered node, it will call {@link #onContainerOver}.
22923      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22924      * @param {Event} e The event
22925      * @param {Object} data An object containing arbitrary data supplied by the drag source
22926      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22927      * underlying {@link Roo.dd.StatusProxy} can be updated
22928      */
22929     notifyOver : function(dd, e, data){
22930         var n = this.getTargetFromEvent(e);
22931         if(!n){ // not over valid drop target
22932             if(this.lastOverNode){
22933                 this.onNodeOut(this.lastOverNode, dd, e, data);
22934                 this.lastOverNode = null;
22935             }
22936             return this.onContainerOver(dd, e, data);
22937         }
22938         if(this.lastOverNode != n){
22939             if(this.lastOverNode){
22940                 this.onNodeOut(this.lastOverNode, dd, e, data);
22941             }
22942             this.onNodeEnter(n, dd, e, data);
22943             this.lastOverNode = n;
22944         }
22945         return this.onNodeOver(n, dd, e, data);
22946     },
22947
22948     /**
22949      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
22950      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
22951      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
22952      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22953      * @param {Event} e The event
22954      * @param {Object} data An object containing arbitrary data supplied by the drag zone
22955      */
22956     notifyOut : function(dd, e, data){
22957         if(this.lastOverNode){
22958             this.onNodeOut(this.lastOverNode, dd, e, data);
22959             this.lastOverNode = null;
22960         }
22961     },
22962
22963     /**
22964      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
22965      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
22966      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
22967      * otherwise it will call {@link #onContainerDrop}.
22968      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22969      * @param {Event} e The event
22970      * @param {Object} data An object containing arbitrary data supplied by the drag source
22971      * @return {Boolean} True if the drop was valid, else false
22972      */
22973     notifyDrop : function(dd, e, data){
22974         if(this.lastOverNode){
22975             this.onNodeOut(this.lastOverNode, dd, e, data);
22976             this.lastOverNode = null;
22977         }
22978         var n = this.getTargetFromEvent(e);
22979         return n ?
22980             this.onNodeDrop(n, dd, e, data) :
22981             this.onContainerDrop(dd, e, data);
22982     },
22983
22984     // private
22985     triggerCacheRefresh : function(){
22986         Roo.dd.DDM.refreshCache(this.groups);
22987     }  
22988 });/*
22989  * Based on:
22990  * Ext JS Library 1.1.1
22991  * Copyright(c) 2006-2007, Ext JS, LLC.
22992  *
22993  * Originally Released Under LGPL - original licence link has changed is not relivant.
22994  *
22995  * Fork - LGPL
22996  * <script type="text/javascript">
22997  */
22998
22999
23000 /**
23001  * @class Roo.data.SortTypes
23002  * @singleton
23003  * Defines the default sorting (casting?) comparison functions used when sorting data.
23004  */
23005 Roo.data.SortTypes = {
23006     /**
23007      * Default sort that does nothing
23008      * @param {Mixed} s The value being converted
23009      * @return {Mixed} The comparison value
23010      */
23011     none : function(s){
23012         return s;
23013     },
23014     
23015     /**
23016      * The regular expression used to strip tags
23017      * @type {RegExp}
23018      * @property
23019      */
23020     stripTagsRE : /<\/?[^>]+>/gi,
23021     
23022     /**
23023      * Strips all HTML tags to sort on text only
23024      * @param {Mixed} s The value being converted
23025      * @return {String} The comparison value
23026      */
23027     asText : function(s){
23028         return String(s).replace(this.stripTagsRE, "");
23029     },
23030     
23031     /**
23032      * Strips all HTML tags to sort on text only - Case insensitive
23033      * @param {Mixed} s The value being converted
23034      * @return {String} The comparison value
23035      */
23036     asUCText : function(s){
23037         return String(s).toUpperCase().replace(this.stripTagsRE, "");
23038     },
23039     
23040     /**
23041      * Case insensitive string
23042      * @param {Mixed} s The value being converted
23043      * @return {String} The comparison value
23044      */
23045     asUCString : function(s) {
23046         return String(s).toUpperCase();
23047     },
23048     
23049     /**
23050      * Date sorting
23051      * @param {Mixed} s The value being converted
23052      * @return {Number} The comparison value
23053      */
23054     asDate : function(s) {
23055         if(!s){
23056             return 0;
23057         }
23058         if(s instanceof Date){
23059             return s.getTime();
23060         }
23061         return Date.parse(String(s));
23062     },
23063     
23064     /**
23065      * Float sorting
23066      * @param {Mixed} s The value being converted
23067      * @return {Float} The comparison value
23068      */
23069     asFloat : function(s) {
23070         var val = parseFloat(String(s).replace(/,/g, ""));
23071         if(isNaN(val)) {
23072             val = 0;
23073         }
23074         return val;
23075     },
23076     
23077     /**
23078      * Integer sorting
23079      * @param {Mixed} s The value being converted
23080      * @return {Number} The comparison value
23081      */
23082     asInt : function(s) {
23083         var val = parseInt(String(s).replace(/,/g, ""));
23084         if(isNaN(val)) {
23085             val = 0;
23086         }
23087         return val;
23088     }
23089 };/*
23090  * Based on:
23091  * Ext JS Library 1.1.1
23092  * Copyright(c) 2006-2007, Ext JS, LLC.
23093  *
23094  * Originally Released Under LGPL - original licence link has changed is not relivant.
23095  *
23096  * Fork - LGPL
23097  * <script type="text/javascript">
23098  */
23099
23100 /**
23101 * @class Roo.data.Record
23102  * Instances of this class encapsulate both record <em>definition</em> information, and record
23103  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
23104  * to access Records cached in an {@link Roo.data.Store} object.<br>
23105  * <p>
23106  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
23107  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
23108  * objects.<br>
23109  * <p>
23110  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
23111  * @constructor
23112  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
23113  * {@link #create}. The parameters are the same.
23114  * @param {Array} data An associative Array of data values keyed by the field name.
23115  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
23116  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
23117  * not specified an integer id is generated.
23118  */
23119 Roo.data.Record = function(data, id){
23120     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
23121     this.data = data;
23122 };
23123
23124 /**
23125  * Generate a constructor for a specific record layout.
23126  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
23127  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
23128  * Each field definition object may contain the following properties: <ul>
23129  * <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,
23130  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
23131  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
23132  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
23133  * is being used, then this is a string containing the javascript expression to reference the data relative to 
23134  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
23135  * to the data item relative to the record element. If the mapping expression is the same as the field name,
23136  * this may be omitted.</p></li>
23137  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
23138  * <ul><li>auto (Default, implies no conversion)</li>
23139  * <li>string</li>
23140  * <li>int</li>
23141  * <li>float</li>
23142  * <li>boolean</li>
23143  * <li>date</li></ul></p></li>
23144  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
23145  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
23146  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
23147  * by the Reader into an object that will be stored in the Record. It is passed the
23148  * following parameters:<ul>
23149  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
23150  * </ul></p></li>
23151  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
23152  * </ul>
23153  * <br>usage:<br><pre><code>
23154 var TopicRecord = Roo.data.Record.create(
23155     {name: 'title', mapping: 'topic_title'},
23156     {name: 'author', mapping: 'username'},
23157     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
23158     {name: 'lastPost', mapping: 'post_time', type: 'date'},
23159     {name: 'lastPoster', mapping: 'user2'},
23160     {name: 'excerpt', mapping: 'post_text'}
23161 );
23162
23163 var myNewRecord = new TopicRecord({
23164     title: 'Do my job please',
23165     author: 'noobie',
23166     totalPosts: 1,
23167     lastPost: new Date(),
23168     lastPoster: 'Animal',
23169     excerpt: 'No way dude!'
23170 });
23171 myStore.add(myNewRecord);
23172 </code></pre>
23173  * @method create
23174  * @static
23175  */
23176 Roo.data.Record.create = function(o){
23177     var f = function(){
23178         f.superclass.constructor.apply(this, arguments);
23179     };
23180     Roo.extend(f, Roo.data.Record);
23181     var p = f.prototype;
23182     p.fields = new Roo.util.MixedCollection(false, function(field){
23183         return field.name;
23184     });
23185     for(var i = 0, len = o.length; i < len; i++){
23186         p.fields.add(new Roo.data.Field(o[i]));
23187     }
23188     f.getField = function(name){
23189         return p.fields.get(name);  
23190     };
23191     return f;
23192 };
23193
23194 Roo.data.Record.AUTO_ID = 1000;
23195 Roo.data.Record.EDIT = 'edit';
23196 Roo.data.Record.REJECT = 'reject';
23197 Roo.data.Record.COMMIT = 'commit';
23198
23199 Roo.data.Record.prototype = {
23200     /**
23201      * Readonly flag - true if this record has been modified.
23202      * @type Boolean
23203      */
23204     dirty : false,
23205     editing : false,
23206     error: null,
23207     modified: null,
23208
23209     // private
23210     join : function(store){
23211         this.store = store;
23212     },
23213
23214     /**
23215      * Set the named field to the specified value.
23216      * @param {String} name The name of the field to set.
23217      * @param {Object} value The value to set the field to.
23218      */
23219     set : function(name, value){
23220         if(this.data[name] == value){
23221             return;
23222         }
23223         this.dirty = true;
23224         if(!this.modified){
23225             this.modified = {};
23226         }
23227         if(typeof this.modified[name] == 'undefined'){
23228             this.modified[name] = this.data[name];
23229         }
23230         this.data[name] = value;
23231         if(!this.editing && this.store){
23232             this.store.afterEdit(this);
23233         }       
23234     },
23235
23236     /**
23237      * Get the value of the named field.
23238      * @param {String} name The name of the field to get the value of.
23239      * @return {Object} The value of the field.
23240      */
23241     get : function(name){
23242         return this.data[name]; 
23243     },
23244
23245     // private
23246     beginEdit : function(){
23247         this.editing = true;
23248         this.modified = {}; 
23249     },
23250
23251     // private
23252     cancelEdit : function(){
23253         this.editing = false;
23254         delete this.modified;
23255     },
23256
23257     // private
23258     endEdit : function(){
23259         this.editing = false;
23260         if(this.dirty && this.store){
23261             this.store.afterEdit(this);
23262         }
23263     },
23264
23265     /**
23266      * Usually called by the {@link Roo.data.Store} which owns the Record.
23267      * Rejects all changes made to the Record since either creation, or the last commit operation.
23268      * Modified fields are reverted to their original values.
23269      * <p>
23270      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
23271      * of reject operations.
23272      */
23273     reject : function(){
23274         var m = this.modified;
23275         for(var n in m){
23276             if(typeof m[n] != "function"){
23277                 this.data[n] = m[n];
23278             }
23279         }
23280         this.dirty = false;
23281         delete this.modified;
23282         this.editing = false;
23283         if(this.store){
23284             this.store.afterReject(this);
23285         }
23286     },
23287
23288     /**
23289      * Usually called by the {@link Roo.data.Store} which owns the Record.
23290      * Commits all changes made to the Record since either creation, or the last commit operation.
23291      * <p>
23292      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
23293      * of commit operations.
23294      */
23295     commit : function(){
23296         this.dirty = false;
23297         delete this.modified;
23298         this.editing = false;
23299         if(this.store){
23300             this.store.afterCommit(this);
23301         }
23302     },
23303
23304     // private
23305     hasError : function(){
23306         return this.error != null;
23307     },
23308
23309     // private
23310     clearError : function(){
23311         this.error = null;
23312     },
23313
23314     /**
23315      * Creates a copy of this record.
23316      * @param {String} id (optional) A new record id if you don't want to use this record's id
23317      * @return {Record}
23318      */
23319     copy : function(newId) {
23320         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
23321     }
23322 };/*
23323  * Based on:
23324  * Ext JS Library 1.1.1
23325  * Copyright(c) 2006-2007, Ext JS, LLC.
23326  *
23327  * Originally Released Under LGPL - original licence link has changed is not relivant.
23328  *
23329  * Fork - LGPL
23330  * <script type="text/javascript">
23331  */
23332
23333
23334
23335 /**
23336  * @class Roo.data.Store
23337  * @extends Roo.util.Observable
23338  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
23339  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
23340  * <p>
23341  * 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
23342  * has no knowledge of the format of the data returned by the Proxy.<br>
23343  * <p>
23344  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
23345  * instances from the data object. These records are cached and made available through accessor functions.
23346  * @constructor
23347  * Creates a new Store.
23348  * @param {Object} config A config object containing the objects needed for the Store to access data,
23349  * and read the data into Records.
23350  */
23351 Roo.data.Store = function(config){
23352     this.data = new Roo.util.MixedCollection(false);
23353     this.data.getKey = function(o){
23354         return o.id;
23355     };
23356     this.baseParams = {};
23357     // private
23358     this.paramNames = {
23359         "start" : "start",
23360         "limit" : "limit",
23361         "sort" : "sort",
23362         "dir" : "dir",
23363         "multisort" : "_multisort"
23364     };
23365
23366     if(config && config.data){
23367         this.inlineData = config.data;
23368         delete config.data;
23369     }
23370
23371     Roo.apply(this, config);
23372     
23373     if(this.reader){ // reader passed
23374         this.reader = Roo.factory(this.reader, Roo.data);
23375         this.reader.xmodule = this.xmodule || false;
23376         if(!this.recordType){
23377             this.recordType = this.reader.recordType;
23378         }
23379         if(this.reader.onMetaChange){
23380             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
23381         }
23382     }
23383
23384     if(this.recordType){
23385         this.fields = this.recordType.prototype.fields;
23386     }
23387     this.modified = [];
23388
23389     this.addEvents({
23390         /**
23391          * @event datachanged
23392          * Fires when the data cache has changed, and a widget which is using this Store
23393          * as a Record cache should refresh its view.
23394          * @param {Store} this
23395          */
23396         datachanged : true,
23397         /**
23398          * @event metachange
23399          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
23400          * @param {Store} this
23401          * @param {Object} meta The JSON metadata
23402          */
23403         metachange : true,
23404         /**
23405          * @event add
23406          * Fires when Records have been added to the Store
23407          * @param {Store} this
23408          * @param {Roo.data.Record[]} records The array of Records added
23409          * @param {Number} index The index at which the record(s) were added
23410          */
23411         add : true,
23412         /**
23413          * @event remove
23414          * Fires when a Record has been removed from the Store
23415          * @param {Store} this
23416          * @param {Roo.data.Record} record The Record that was removed
23417          * @param {Number} index The index at which the record was removed
23418          */
23419         remove : true,
23420         /**
23421          * @event update
23422          * Fires when a Record has been updated
23423          * @param {Store} this
23424          * @param {Roo.data.Record} record The Record that was updated
23425          * @param {String} operation The update operation being performed.  Value may be one of:
23426          * <pre><code>
23427  Roo.data.Record.EDIT
23428  Roo.data.Record.REJECT
23429  Roo.data.Record.COMMIT
23430          * </code></pre>
23431          */
23432         update : true,
23433         /**
23434          * @event clear
23435          * Fires when the data cache has been cleared.
23436          * @param {Store} this
23437          */
23438         clear : true,
23439         /**
23440          * @event beforeload
23441          * Fires before a request is made for a new data object.  If the beforeload handler returns false
23442          * the load action will be canceled.
23443          * @param {Store} this
23444          * @param {Object} options The loading options that were specified (see {@link #load} for details)
23445          */
23446         beforeload : true,
23447         /**
23448          * @event beforeloadadd
23449          * Fires after a new set of Records has been loaded.
23450          * @param {Store} this
23451          * @param {Roo.data.Record[]} records The Records that were loaded
23452          * @param {Object} options The loading options that were specified (see {@link #load} for details)
23453          */
23454         beforeloadadd : true,
23455         /**
23456          * @event load
23457          * Fires after a new set of Records has been loaded, before they are added to the store.
23458          * @param {Store} this
23459          * @param {Roo.data.Record[]} records The Records that were loaded
23460          * @param {Object} options The loading options that were specified (see {@link #load} for details)
23461          * @params {Object} return from reader
23462          */
23463         load : true,
23464         /**
23465          * @event loadexception
23466          * Fires if an exception occurs in the Proxy during loading.
23467          * Called with the signature of the Proxy's "loadexception" event.
23468          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
23469          * 
23470          * @param {Proxy} 
23471          * @param {Object} return from JsonData.reader() - success, totalRecords, records
23472          * @param {Object} load options 
23473          * @param {Object} jsonData from your request (normally this contains the Exception)
23474          */
23475         loadexception : true
23476     });
23477     
23478     if(this.proxy){
23479         this.proxy = Roo.factory(this.proxy, Roo.data);
23480         this.proxy.xmodule = this.xmodule || false;
23481         this.relayEvents(this.proxy,  ["loadexception"]);
23482     }
23483     this.sortToggle = {};
23484     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
23485
23486     Roo.data.Store.superclass.constructor.call(this);
23487
23488     if(this.inlineData){
23489         this.loadData(this.inlineData);
23490         delete this.inlineData;
23491     }
23492 };
23493
23494 Roo.extend(Roo.data.Store, Roo.util.Observable, {
23495      /**
23496     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
23497     * without a remote query - used by combo/forms at present.
23498     */
23499     
23500     /**
23501     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
23502     */
23503     /**
23504     * @cfg {Array} data Inline data to be loaded when the store is initialized.
23505     */
23506     /**
23507     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
23508     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
23509     */
23510     /**
23511     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
23512     * on any HTTP request
23513     */
23514     /**
23515     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
23516     */
23517     /**
23518     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
23519     */
23520     multiSort: false,
23521     /**
23522     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
23523     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
23524     */
23525     remoteSort : false,
23526
23527     /**
23528     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
23529      * loaded or when a record is removed. (defaults to false).
23530     */
23531     pruneModifiedRecords : false,
23532
23533     // private
23534     lastOptions : null,
23535
23536     /**
23537      * Add Records to the Store and fires the add event.
23538      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
23539      */
23540     add : function(records){
23541         records = [].concat(records);
23542         for(var i = 0, len = records.length; i < len; i++){
23543             records[i].join(this);
23544         }
23545         var index = this.data.length;
23546         this.data.addAll(records);
23547         this.fireEvent("add", this, records, index);
23548     },
23549
23550     /**
23551      * Remove a Record from the Store and fires the remove event.
23552      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
23553      */
23554     remove : function(record){
23555         var index = this.data.indexOf(record);
23556         this.data.removeAt(index);
23557  
23558         if(this.pruneModifiedRecords){
23559             this.modified.remove(record);
23560         }
23561         this.fireEvent("remove", this, record, index);
23562     },
23563
23564     /**
23565      * Remove all Records from the Store and fires the clear event.
23566      */
23567     removeAll : function(){
23568         this.data.clear();
23569         if(this.pruneModifiedRecords){
23570             this.modified = [];
23571         }
23572         this.fireEvent("clear", this);
23573     },
23574
23575     /**
23576      * Inserts Records to the Store at the given index and fires the add event.
23577      * @param {Number} index The start index at which to insert the passed Records.
23578      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
23579      */
23580     insert : function(index, records){
23581         records = [].concat(records);
23582         for(var i = 0, len = records.length; i < len; i++){
23583             this.data.insert(index, records[i]);
23584             records[i].join(this);
23585         }
23586         this.fireEvent("add", this, records, index);
23587     },
23588
23589     /**
23590      * Get the index within the cache of the passed Record.
23591      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
23592      * @return {Number} The index of the passed Record. Returns -1 if not found.
23593      */
23594     indexOf : function(record){
23595         return this.data.indexOf(record);
23596     },
23597
23598     /**
23599      * Get the index within the cache of the Record with the passed id.
23600      * @param {String} id The id of the Record to find.
23601      * @return {Number} The index of the Record. Returns -1 if not found.
23602      */
23603     indexOfId : function(id){
23604         return this.data.indexOfKey(id);
23605     },
23606
23607     /**
23608      * Get the Record with the specified id.
23609      * @param {String} id The id of the Record to find.
23610      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
23611      */
23612     getById : function(id){
23613         return this.data.key(id);
23614     },
23615
23616     /**
23617      * Get the Record at the specified index.
23618      * @param {Number} index The index of the Record to find.
23619      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
23620      */
23621     getAt : function(index){
23622         return this.data.itemAt(index);
23623     },
23624
23625     /**
23626      * Returns a range of Records between specified indices.
23627      * @param {Number} startIndex (optional) The starting index (defaults to 0)
23628      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
23629      * @return {Roo.data.Record[]} An array of Records
23630      */
23631     getRange : function(start, end){
23632         return this.data.getRange(start, end);
23633     },
23634
23635     // private
23636     storeOptions : function(o){
23637         o = Roo.apply({}, o);
23638         delete o.callback;
23639         delete o.scope;
23640         this.lastOptions = o;
23641     },
23642
23643     /**
23644      * Loads the Record cache from the configured Proxy using the configured Reader.
23645      * <p>
23646      * If using remote paging, then the first load call must specify the <em>start</em>
23647      * and <em>limit</em> properties in the options.params property to establish the initial
23648      * position within the dataset, and the number of Records to cache on each read from the Proxy.
23649      * <p>
23650      * <strong>It is important to note that for remote data sources, loading is asynchronous,
23651      * and this call will return before the new data has been loaded. Perform any post-processing
23652      * in a callback function, or in a "load" event handler.</strong>
23653      * <p>
23654      * @param {Object} options An object containing properties which control loading options:<ul>
23655      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
23656      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
23657      * passed the following arguments:<ul>
23658      * <li>r : Roo.data.Record[]</li>
23659      * <li>options: Options object from the load call</li>
23660      * <li>success: Boolean success indicator</li></ul></li>
23661      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
23662      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
23663      * </ul>
23664      */
23665     load : function(options){
23666         options = options || {};
23667         if(this.fireEvent("beforeload", this, options) !== false){
23668             this.storeOptions(options);
23669             var p = Roo.apply(options.params || {}, this.baseParams);
23670             // if meta was not loaded from remote source.. try requesting it.
23671             if (!this.reader.metaFromRemote) {
23672                 p._requestMeta = 1;
23673             }
23674             if(this.sortInfo && this.remoteSort){
23675                 var pn = this.paramNames;
23676                 p[pn["sort"]] = this.sortInfo.field;
23677                 p[pn["dir"]] = this.sortInfo.direction;
23678             }
23679             if (this.multiSort) {
23680                 var pn = this.paramNames;
23681                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
23682             }
23683             
23684             this.proxy.load(p, this.reader, this.loadRecords, this, options);
23685         }
23686     },
23687
23688     /**
23689      * Reloads the Record cache from the configured Proxy using the configured Reader and
23690      * the options from the last load operation performed.
23691      * @param {Object} options (optional) An object containing properties which may override the options
23692      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
23693      * the most recently used options are reused).
23694      */
23695     reload : function(options){
23696         this.load(Roo.applyIf(options||{}, this.lastOptions));
23697     },
23698
23699     // private
23700     // Called as a callback by the Reader during a load operation.
23701     loadRecords : function(o, options, success){
23702         if(!o || success === false){
23703             if(success !== false){
23704                 this.fireEvent("load", this, [], options, o);
23705             }
23706             if(options.callback){
23707                 options.callback.call(options.scope || this, [], options, false);
23708             }
23709             return;
23710         }
23711         // if data returned failure - throw an exception.
23712         if (o.success === false) {
23713             // show a message if no listener is registered.
23714             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
23715                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
23716             }
23717             // loadmask wil be hooked into this..
23718             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
23719             return;
23720         }
23721         var r = o.records, t = o.totalRecords || r.length;
23722         
23723         this.fireEvent("beforeloadadd", this, r, options, o);
23724         
23725         if(!options || options.add !== true){
23726             if(this.pruneModifiedRecords){
23727                 this.modified = [];
23728             }
23729             for(var i = 0, len = r.length; i < len; i++){
23730                 r[i].join(this);
23731             }
23732             if(this.snapshot){
23733                 this.data = this.snapshot;
23734                 delete this.snapshot;
23735             }
23736             this.data.clear();
23737             this.data.addAll(r);
23738             this.totalLength = t;
23739             this.applySort();
23740             this.fireEvent("datachanged", this);
23741         }else{
23742             this.totalLength = Math.max(t, this.data.length+r.length);
23743             this.add(r);
23744         }
23745         
23746         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
23747                 
23748             var e = new Roo.data.Record({});
23749
23750             e.set(this.parent.displayField, this.parent.emptyTitle);
23751             e.set(this.parent.valueField, '');
23752
23753             this.insert(0, e);
23754         }
23755             
23756         this.fireEvent("load", this, r, options, o);
23757         if(options.callback){
23758             options.callback.call(options.scope || this, r, options, true);
23759         }
23760     },
23761
23762
23763     /**
23764      * Loads data from a passed data block. A Reader which understands the format of the data
23765      * must have been configured in the constructor.
23766      * @param {Object} data The data block from which to read the Records.  The format of the data expected
23767      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
23768      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
23769      */
23770     loadData : function(o, append){
23771         var r = this.reader.readRecords(o);
23772         this.loadRecords(r, {add: append}, true);
23773     },
23774     
23775      /**
23776      * using 'cn' the nested child reader read the child array into it's child stores.
23777      * @param {Object} rec The record with a 'children array
23778      */
23779     loadDataFromChildren : function(rec)
23780     {
23781         this.loadData(this.reader.toLoadData(rec));
23782     },
23783     
23784
23785     /**
23786      * Gets the number of cached records.
23787      * <p>
23788      * <em>If using paging, this may not be the total size of the dataset. If the data object
23789      * used by the Reader contains the dataset size, then the getTotalCount() function returns
23790      * the data set size</em>
23791      */
23792     getCount : function(){
23793         return this.data.length || 0;
23794     },
23795
23796     /**
23797      * Gets the total number of records in the dataset as returned by the server.
23798      * <p>
23799      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
23800      * the dataset size</em>
23801      */
23802     getTotalCount : function(){
23803         return this.totalLength || 0;
23804     },
23805
23806     /**
23807      * Returns the sort state of the Store as an object with two properties:
23808      * <pre><code>
23809  field {String} The name of the field by which the Records are sorted
23810  direction {String} The sort order, "ASC" or "DESC"
23811      * </code></pre>
23812      */
23813     getSortState : function(){
23814         return this.sortInfo;
23815     },
23816
23817     // private
23818     applySort : function(){
23819         if(this.sortInfo && !this.remoteSort){
23820             var s = this.sortInfo, f = s.field;
23821             var st = this.fields.get(f).sortType;
23822             var fn = function(r1, r2){
23823                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
23824                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
23825             };
23826             this.data.sort(s.direction, fn);
23827             if(this.snapshot && this.snapshot != this.data){
23828                 this.snapshot.sort(s.direction, fn);
23829             }
23830         }
23831     },
23832
23833     /**
23834      * Sets the default sort column and order to be used by the next load operation.
23835      * @param {String} fieldName The name of the field to sort by.
23836      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23837      */
23838     setDefaultSort : function(field, dir){
23839         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
23840     },
23841
23842     /**
23843      * Sort the Records.
23844      * If remote sorting is used, the sort is performed on the server, and the cache is
23845      * reloaded. If local sorting is used, the cache is sorted internally.
23846      * @param {String} fieldName The name of the field to sort by.
23847      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23848      */
23849     sort : function(fieldName, dir){
23850         var f = this.fields.get(fieldName);
23851         if(!dir){
23852             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
23853             
23854             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
23855                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
23856             }else{
23857                 dir = f.sortDir;
23858             }
23859         }
23860         this.sortToggle[f.name] = dir;
23861         this.sortInfo = {field: f.name, direction: dir};
23862         if(!this.remoteSort){
23863             this.applySort();
23864             this.fireEvent("datachanged", this);
23865         }else{
23866             this.load(this.lastOptions);
23867         }
23868     },
23869
23870     /**
23871      * Calls the specified function for each of the Records in the cache.
23872      * @param {Function} fn The function to call. The Record is passed as the first parameter.
23873      * Returning <em>false</em> aborts and exits the iteration.
23874      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
23875      */
23876     each : function(fn, scope){
23877         this.data.each(fn, scope);
23878     },
23879
23880     /**
23881      * Gets all records modified since the last commit.  Modified records are persisted across load operations
23882      * (e.g., during paging).
23883      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
23884      */
23885     getModifiedRecords : function(){
23886         return this.modified;
23887     },
23888
23889     // private
23890     createFilterFn : function(property, value, anyMatch){
23891         if(!value.exec){ // not a regex
23892             value = String(value);
23893             if(value.length == 0){
23894                 return false;
23895             }
23896             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
23897         }
23898         return function(r){
23899             return value.test(r.data[property]);
23900         };
23901     },
23902
23903     /**
23904      * Sums the value of <i>property</i> for each record between start and end and returns the result.
23905      * @param {String} property A field on your records
23906      * @param {Number} start The record index to start at (defaults to 0)
23907      * @param {Number} end The last record index to include (defaults to length - 1)
23908      * @return {Number} The sum
23909      */
23910     sum : function(property, start, end){
23911         var rs = this.data.items, v = 0;
23912         start = start || 0;
23913         end = (end || end === 0) ? end : rs.length-1;
23914
23915         for(var i = start; i <= end; i++){
23916             v += (rs[i].data[property] || 0);
23917         }
23918         return v;
23919     },
23920
23921     /**
23922      * Filter the records by a specified property.
23923      * @param {String} field A field on your records
23924      * @param {String/RegExp} value Either a string that the field
23925      * should start with or a RegExp to test against the field
23926      * @param {Boolean} anyMatch True to match any part not just the beginning
23927      */
23928     filter : function(property, value, anyMatch){
23929         var fn = this.createFilterFn(property, value, anyMatch);
23930         return fn ? this.filterBy(fn) : this.clearFilter();
23931     },
23932
23933     /**
23934      * Filter by a function. The specified function will be called with each
23935      * record in this data source. If the function returns true the record is included,
23936      * otherwise it is filtered.
23937      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23938      * @param {Object} scope (optional) The scope of the function (defaults to this)
23939      */
23940     filterBy : function(fn, scope){
23941         this.snapshot = this.snapshot || this.data;
23942         this.data = this.queryBy(fn, scope||this);
23943         this.fireEvent("datachanged", this);
23944     },
23945
23946     /**
23947      * Query the records by a specified property.
23948      * @param {String} field A field on your records
23949      * @param {String/RegExp} value Either a string that the field
23950      * should start with or a RegExp to test against the field
23951      * @param {Boolean} anyMatch True to match any part not just the beginning
23952      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23953      */
23954     query : function(property, value, anyMatch){
23955         var fn = this.createFilterFn(property, value, anyMatch);
23956         return fn ? this.queryBy(fn) : this.data.clone();
23957     },
23958
23959     /**
23960      * Query by a function. The specified function will be called with each
23961      * record in this data source. If the function returns true the record is included
23962      * in the results.
23963      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23964      * @param {Object} scope (optional) The scope of the function (defaults to this)
23965       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23966      **/
23967     queryBy : function(fn, scope){
23968         var data = this.snapshot || this.data;
23969         return data.filterBy(fn, scope||this);
23970     },
23971
23972     /**
23973      * Collects unique values for a particular dataIndex from this store.
23974      * @param {String} dataIndex The property to collect
23975      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
23976      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
23977      * @return {Array} An array of the unique values
23978      **/
23979     collect : function(dataIndex, allowNull, bypassFilter){
23980         var d = (bypassFilter === true && this.snapshot) ?
23981                 this.snapshot.items : this.data.items;
23982         var v, sv, r = [], l = {};
23983         for(var i = 0, len = d.length; i < len; i++){
23984             v = d[i].data[dataIndex];
23985             sv = String(v);
23986             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
23987                 l[sv] = true;
23988                 r[r.length] = v;
23989             }
23990         }
23991         return r;
23992     },
23993
23994     /**
23995      * Revert to a view of the Record cache with no filtering applied.
23996      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
23997      */
23998     clearFilter : function(suppressEvent){
23999         if(this.snapshot && this.snapshot != this.data){
24000             this.data = this.snapshot;
24001             delete this.snapshot;
24002             if(suppressEvent !== true){
24003                 this.fireEvent("datachanged", this);
24004             }
24005         }
24006     },
24007
24008     // private
24009     afterEdit : function(record){
24010         if(this.modified.indexOf(record) == -1){
24011             this.modified.push(record);
24012         }
24013         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
24014     },
24015     
24016     // private
24017     afterReject : function(record){
24018         this.modified.remove(record);
24019         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
24020     },
24021
24022     // private
24023     afterCommit : function(record){
24024         this.modified.remove(record);
24025         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
24026     },
24027
24028     /**
24029      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
24030      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
24031      */
24032     commitChanges : function(){
24033         var m = this.modified.slice(0);
24034         this.modified = [];
24035         for(var i = 0, len = m.length; i < len; i++){
24036             m[i].commit();
24037         }
24038     },
24039
24040     /**
24041      * Cancel outstanding changes on all changed records.
24042      */
24043     rejectChanges : function(){
24044         var m = this.modified.slice(0);
24045         this.modified = [];
24046         for(var i = 0, len = m.length; i < len; i++){
24047             m[i].reject();
24048         }
24049     },
24050
24051     onMetaChange : function(meta, rtype, o){
24052         this.recordType = rtype;
24053         this.fields = rtype.prototype.fields;
24054         delete this.snapshot;
24055         this.sortInfo = meta.sortInfo || this.sortInfo;
24056         this.modified = [];
24057         this.fireEvent('metachange', this, this.reader.meta);
24058     },
24059     
24060     moveIndex : function(data, type)
24061     {
24062         var index = this.indexOf(data);
24063         
24064         var newIndex = index + type;
24065         
24066         this.remove(data);
24067         
24068         this.insert(newIndex, data);
24069         
24070     }
24071 });/*
24072  * Based on:
24073  * Ext JS Library 1.1.1
24074  * Copyright(c) 2006-2007, Ext JS, LLC.
24075  *
24076  * Originally Released Under LGPL - original licence link has changed is not relivant.
24077  *
24078  * Fork - LGPL
24079  * <script type="text/javascript">
24080  */
24081
24082 /**
24083  * @class Roo.data.SimpleStore
24084  * @extends Roo.data.Store
24085  * Small helper class to make creating Stores from Array data easier.
24086  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
24087  * @cfg {Array} fields An array of field definition objects, or field name strings.
24088  * @cfg {Object} an existing reader (eg. copied from another store)
24089  * @cfg {Array} data The multi-dimensional array of data
24090  * @constructor
24091  * @param {Object} config
24092  */
24093 Roo.data.SimpleStore = function(config)
24094 {
24095     Roo.data.SimpleStore.superclass.constructor.call(this, {
24096         isLocal : true,
24097         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
24098                 id: config.id
24099             },
24100             Roo.data.Record.create(config.fields)
24101         ),
24102         proxy : new Roo.data.MemoryProxy(config.data)
24103     });
24104     this.load();
24105 };
24106 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
24107  * Based on:
24108  * Ext JS Library 1.1.1
24109  * Copyright(c) 2006-2007, Ext JS, LLC.
24110  *
24111  * Originally Released Under LGPL - original licence link has changed is not relivant.
24112  *
24113  * Fork - LGPL
24114  * <script type="text/javascript">
24115  */
24116
24117 /**
24118 /**
24119  * @extends Roo.data.Store
24120  * @class Roo.data.JsonStore
24121  * Small helper class to make creating Stores for JSON data easier. <br/>
24122 <pre><code>
24123 var store = new Roo.data.JsonStore({
24124     url: 'get-images.php',
24125     root: 'images',
24126     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
24127 });
24128 </code></pre>
24129  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
24130  * JsonReader and HttpProxy (unless inline data is provided).</b>
24131  * @cfg {Array} fields An array of field definition objects, or field name strings.
24132  * @constructor
24133  * @param {Object} config
24134  */
24135 Roo.data.JsonStore = function(c){
24136     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
24137         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
24138         reader: new Roo.data.JsonReader(c, c.fields)
24139     }));
24140 };
24141 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
24142  * Based on:
24143  * Ext JS Library 1.1.1
24144  * Copyright(c) 2006-2007, Ext JS, LLC.
24145  *
24146  * Originally Released Under LGPL - original licence link has changed is not relivant.
24147  *
24148  * Fork - LGPL
24149  * <script type="text/javascript">
24150  */
24151
24152  
24153 Roo.data.Field = function(config){
24154     if(typeof config == "string"){
24155         config = {name: config};
24156     }
24157     Roo.apply(this, config);
24158     
24159     if(!this.type){
24160         this.type = "auto";
24161     }
24162     
24163     var st = Roo.data.SortTypes;
24164     // named sortTypes are supported, here we look them up
24165     if(typeof this.sortType == "string"){
24166         this.sortType = st[this.sortType];
24167     }
24168     
24169     // set default sortType for strings and dates
24170     if(!this.sortType){
24171         switch(this.type){
24172             case "string":
24173                 this.sortType = st.asUCString;
24174                 break;
24175             case "date":
24176                 this.sortType = st.asDate;
24177                 break;
24178             default:
24179                 this.sortType = st.none;
24180         }
24181     }
24182
24183     // define once
24184     var stripRe = /[\$,%]/g;
24185
24186     // prebuilt conversion function for this field, instead of
24187     // switching every time we're reading a value
24188     if(!this.convert){
24189         var cv, dateFormat = this.dateFormat;
24190         switch(this.type){
24191             case "":
24192             case "auto":
24193             case undefined:
24194                 cv = function(v){ return v; };
24195                 break;
24196             case "string":
24197                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
24198                 break;
24199             case "int":
24200                 cv = function(v){
24201                     return v !== undefined && v !== null && v !== '' ?
24202                            parseInt(String(v).replace(stripRe, ""), 10) : '';
24203                     };
24204                 break;
24205             case "float":
24206                 cv = function(v){
24207                     return v !== undefined && v !== null && v !== '' ?
24208                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
24209                     };
24210                 break;
24211             case "bool":
24212             case "boolean":
24213                 cv = function(v){ return v === true || v === "true" || v == 1; };
24214                 break;
24215             case "date":
24216                 cv = function(v){
24217                     if(!v){
24218                         return '';
24219                     }
24220                     if(v instanceof Date){
24221                         return v;
24222                     }
24223                     if(dateFormat){
24224                         if(dateFormat == "timestamp"){
24225                             return new Date(v*1000);
24226                         }
24227                         return Date.parseDate(v, dateFormat);
24228                     }
24229                     var parsed = Date.parse(v);
24230                     return parsed ? new Date(parsed) : null;
24231                 };
24232              break;
24233             
24234         }
24235         this.convert = cv;
24236     }
24237 };
24238
24239 Roo.data.Field.prototype = {
24240     dateFormat: null,
24241     defaultValue: "",
24242     mapping: null,
24243     sortType : null,
24244     sortDir : "ASC"
24245 };/*
24246  * Based on:
24247  * Ext JS Library 1.1.1
24248  * Copyright(c) 2006-2007, Ext JS, LLC.
24249  *
24250  * Originally Released Under LGPL - original licence link has changed is not relivant.
24251  *
24252  * Fork - LGPL
24253  * <script type="text/javascript">
24254  */
24255  
24256 // Base class for reading structured data from a data source.  This class is intended to be
24257 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
24258
24259 /**
24260  * @class Roo.data.DataReader
24261  * Base class for reading structured data from a data source.  This class is intended to be
24262  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
24263  */
24264
24265 Roo.data.DataReader = function(meta, recordType){
24266     
24267     this.meta = meta;
24268     
24269     this.recordType = recordType instanceof Array ? 
24270         Roo.data.Record.create(recordType) : recordType;
24271 };
24272
24273 Roo.data.DataReader.prototype = {
24274     
24275     
24276     readerType : 'Data',
24277      /**
24278      * Create an empty record
24279      * @param {Object} data (optional) - overlay some values
24280      * @return {Roo.data.Record} record created.
24281      */
24282     newRow :  function(d) {
24283         var da =  {};
24284         this.recordType.prototype.fields.each(function(c) {
24285             switch( c.type) {
24286                 case 'int' : da[c.name] = 0; break;
24287                 case 'date' : da[c.name] = new Date(); break;
24288                 case 'float' : da[c.name] = 0.0; break;
24289                 case 'boolean' : da[c.name] = false; break;
24290                 default : da[c.name] = ""; break;
24291             }
24292             
24293         });
24294         return new this.recordType(Roo.apply(da, d));
24295     }
24296     
24297     
24298 };/*
24299  * Based on:
24300  * Ext JS Library 1.1.1
24301  * Copyright(c) 2006-2007, Ext JS, LLC.
24302  *
24303  * Originally Released Under LGPL - original licence link has changed is not relivant.
24304  *
24305  * Fork - LGPL
24306  * <script type="text/javascript">
24307  */
24308
24309 /**
24310  * @class Roo.data.DataProxy
24311  * @extends Roo.data.Observable
24312  * This class is an abstract base class for implementations which provide retrieval of
24313  * unformatted data objects.<br>
24314  * <p>
24315  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
24316  * (of the appropriate type which knows how to parse the data object) to provide a block of
24317  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
24318  * <p>
24319  * Custom implementations must implement the load method as described in
24320  * {@link Roo.data.HttpProxy#load}.
24321  */
24322 Roo.data.DataProxy = function(){
24323     this.addEvents({
24324         /**
24325          * @event beforeload
24326          * Fires before a network request is made to retrieve a data object.
24327          * @param {Object} This DataProxy object.
24328          * @param {Object} params The params parameter to the load function.
24329          */
24330         beforeload : true,
24331         /**
24332          * @event load
24333          * Fires before the load method's callback is called.
24334          * @param {Object} This DataProxy object.
24335          * @param {Object} o The data object.
24336          * @param {Object} arg The callback argument object passed to the load function.
24337          */
24338         load : true,
24339         /**
24340          * @event loadexception
24341          * Fires if an Exception occurs during data retrieval.
24342          * @param {Object} This DataProxy object.
24343          * @param {Object} o The data object.
24344          * @param {Object} arg The callback argument object passed to the load function.
24345          * @param {Object} e The Exception.
24346          */
24347         loadexception : true
24348     });
24349     Roo.data.DataProxy.superclass.constructor.call(this);
24350 };
24351
24352 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
24353
24354     /**
24355      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
24356      */
24357 /*
24358  * Based on:
24359  * Ext JS Library 1.1.1
24360  * Copyright(c) 2006-2007, Ext JS, LLC.
24361  *
24362  * Originally Released Under LGPL - original licence link has changed is not relivant.
24363  *
24364  * Fork - LGPL
24365  * <script type="text/javascript">
24366  */
24367 /**
24368  * @class Roo.data.MemoryProxy
24369  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
24370  * to the Reader when its load method is called.
24371  * @constructor
24372  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
24373  */
24374 Roo.data.MemoryProxy = function(data){
24375     if (data.data) {
24376         data = data.data;
24377     }
24378     Roo.data.MemoryProxy.superclass.constructor.call(this);
24379     this.data = data;
24380 };
24381
24382 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
24383     
24384     /**
24385      * Load data from the requested source (in this case an in-memory
24386      * data object passed to the constructor), read the data object into
24387      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
24388      * process that block using the passed callback.
24389      * @param {Object} params This parameter is not used by the MemoryProxy class.
24390      * @param {Roo.data.DataReader} reader The Reader object which converts the data
24391      * object into a block of Roo.data.Records.
24392      * @param {Function} callback The function into which to pass the block of Roo.data.records.
24393      * The function must be passed <ul>
24394      * <li>The Record block object</li>
24395      * <li>The "arg" argument from the load function</li>
24396      * <li>A boolean success indicator</li>
24397      * </ul>
24398      * @param {Object} scope The scope in which to call the callback
24399      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24400      */
24401     load : function(params, reader, callback, scope, arg){
24402         params = params || {};
24403         var result;
24404         try {
24405             result = reader.readRecords(params.data ? params.data :this.data);
24406         }catch(e){
24407             this.fireEvent("loadexception", this, arg, null, e);
24408             callback.call(scope, null, arg, false);
24409             return;
24410         }
24411         callback.call(scope, result, arg, true);
24412     },
24413     
24414     // private
24415     update : function(params, records){
24416         
24417     }
24418 });/*
24419  * Based on:
24420  * Ext JS Library 1.1.1
24421  * Copyright(c) 2006-2007, Ext JS, LLC.
24422  *
24423  * Originally Released Under LGPL - original licence link has changed is not relivant.
24424  *
24425  * Fork - LGPL
24426  * <script type="text/javascript">
24427  */
24428 /**
24429  * @class Roo.data.HttpProxy
24430  * @extends Roo.data.DataProxy
24431  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
24432  * configured to reference a certain URL.<br><br>
24433  * <p>
24434  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
24435  * from which the running page was served.<br><br>
24436  * <p>
24437  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
24438  * <p>
24439  * Be aware that to enable the browser to parse an XML document, the server must set
24440  * the Content-Type header in the HTTP response to "text/xml".
24441  * @constructor
24442  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
24443  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
24444  * will be used to make the request.
24445  */
24446 Roo.data.HttpProxy = function(conn){
24447     Roo.data.HttpProxy.superclass.constructor.call(this);
24448     // is conn a conn config or a real conn?
24449     this.conn = conn;
24450     this.useAjax = !conn || !conn.events;
24451   
24452 };
24453
24454 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
24455     // thse are take from connection...
24456     
24457     /**
24458      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
24459      */
24460     /**
24461      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
24462      * extra parameters to each request made by this object. (defaults to undefined)
24463      */
24464     /**
24465      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
24466      *  to each request made by this object. (defaults to undefined)
24467      */
24468     /**
24469      * @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)
24470      */
24471     /**
24472      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
24473      */
24474      /**
24475      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
24476      * @type Boolean
24477      */
24478   
24479
24480     /**
24481      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
24482      * @type Boolean
24483      */
24484     /**
24485      * Return the {@link Roo.data.Connection} object being used by this Proxy.
24486      * @return {Connection} The Connection object. This object may be used to subscribe to events on
24487      * a finer-grained basis than the DataProxy events.
24488      */
24489     getConnection : function(){
24490         return this.useAjax ? Roo.Ajax : this.conn;
24491     },
24492
24493     /**
24494      * Load data from the configured {@link Roo.data.Connection}, read the data object into
24495      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
24496      * process that block using the passed callback.
24497      * @param {Object} params An object containing properties which are to be used as HTTP parameters
24498      * for the request to the remote server.
24499      * @param {Roo.data.DataReader} reader The Reader object which converts the data
24500      * object into a block of Roo.data.Records.
24501      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
24502      * The function must be passed <ul>
24503      * <li>The Record block object</li>
24504      * <li>The "arg" argument from the load function</li>
24505      * <li>A boolean success indicator</li>
24506      * </ul>
24507      * @param {Object} scope The scope in which to call the callback
24508      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24509      */
24510     load : function(params, reader, callback, scope, arg){
24511         if(this.fireEvent("beforeload", this, params) !== false){
24512             var  o = {
24513                 params : params || {},
24514                 request: {
24515                     callback : callback,
24516                     scope : scope,
24517                     arg : arg
24518                 },
24519                 reader: reader,
24520                 callback : this.loadResponse,
24521                 scope: this
24522             };
24523             if(this.useAjax){
24524                 Roo.applyIf(o, this.conn);
24525                 if(this.activeRequest){
24526                     Roo.Ajax.abort(this.activeRequest);
24527                 }
24528                 this.activeRequest = Roo.Ajax.request(o);
24529             }else{
24530                 this.conn.request(o);
24531             }
24532         }else{
24533             callback.call(scope||this, null, arg, false);
24534         }
24535     },
24536
24537     // private
24538     loadResponse : function(o, success, response){
24539         delete this.activeRequest;
24540         if(!success){
24541             this.fireEvent("loadexception", this, o, response);
24542             o.request.callback.call(o.request.scope, null, o.request.arg, false);
24543             return;
24544         }
24545         var result;
24546         try {
24547             result = o.reader.read(response);
24548         }catch(e){
24549             this.fireEvent("loadexception", this, o, response, e);
24550             o.request.callback.call(o.request.scope, null, o.request.arg, false);
24551             return;
24552         }
24553         
24554         this.fireEvent("load", this, o, o.request.arg);
24555         o.request.callback.call(o.request.scope, result, o.request.arg, true);
24556     },
24557
24558     // private
24559     update : function(dataSet){
24560
24561     },
24562
24563     // private
24564     updateResponse : function(dataSet){
24565
24566     }
24567 });/*
24568  * Based on:
24569  * Ext JS Library 1.1.1
24570  * Copyright(c) 2006-2007, Ext JS, LLC.
24571  *
24572  * Originally Released Under LGPL - original licence link has changed is not relivant.
24573  *
24574  * Fork - LGPL
24575  * <script type="text/javascript">
24576  */
24577
24578 /**
24579  * @class Roo.data.ScriptTagProxy
24580  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
24581  * other than the originating domain of the running page.<br><br>
24582  * <p>
24583  * <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
24584  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
24585  * <p>
24586  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
24587  * source code that is used as the source inside a &lt;script> tag.<br><br>
24588  * <p>
24589  * In order for the browser to process the returned data, the server must wrap the data object
24590  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
24591  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
24592  * depending on whether the callback name was passed:
24593  * <p>
24594  * <pre><code>
24595 boolean scriptTag = false;
24596 String cb = request.getParameter("callback");
24597 if (cb != null) {
24598     scriptTag = true;
24599     response.setContentType("text/javascript");
24600 } else {
24601     response.setContentType("application/x-json");
24602 }
24603 Writer out = response.getWriter();
24604 if (scriptTag) {
24605     out.write(cb + "(");
24606 }
24607 out.print(dataBlock.toJsonString());
24608 if (scriptTag) {
24609     out.write(");");
24610 }
24611 </pre></code>
24612  *
24613  * @constructor
24614  * @param {Object} config A configuration object.
24615  */
24616 Roo.data.ScriptTagProxy = function(config){
24617     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
24618     Roo.apply(this, config);
24619     this.head = document.getElementsByTagName("head")[0];
24620 };
24621
24622 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
24623
24624 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
24625     /**
24626      * @cfg {String} url The URL from which to request the data object.
24627      */
24628     /**
24629      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
24630      */
24631     timeout : 30000,
24632     /**
24633      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
24634      * the server the name of the callback function set up by the load call to process the returned data object.
24635      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
24636      * javascript output which calls this named function passing the data object as its only parameter.
24637      */
24638     callbackParam : "callback",
24639     /**
24640      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
24641      * name to the request.
24642      */
24643     nocache : true,
24644
24645     /**
24646      * Load data from the configured URL, read the data object into
24647      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
24648      * process that block using the passed callback.
24649      * @param {Object} params An object containing properties which are to be used as HTTP parameters
24650      * for the request to the remote server.
24651      * @param {Roo.data.DataReader} reader The Reader object which converts the data
24652      * object into a block of Roo.data.Records.
24653      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
24654      * The function must be passed <ul>
24655      * <li>The Record block object</li>
24656      * <li>The "arg" argument from the load function</li>
24657      * <li>A boolean success indicator</li>
24658      * </ul>
24659      * @param {Object} scope The scope in which to call the callback
24660      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24661      */
24662     load : function(params, reader, callback, scope, arg){
24663         if(this.fireEvent("beforeload", this, params) !== false){
24664
24665             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
24666
24667             var url = this.url;
24668             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
24669             if(this.nocache){
24670                 url += "&_dc=" + (new Date().getTime());
24671             }
24672             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
24673             var trans = {
24674                 id : transId,
24675                 cb : "stcCallback"+transId,
24676                 scriptId : "stcScript"+transId,
24677                 params : params,
24678                 arg : arg,
24679                 url : url,
24680                 callback : callback,
24681                 scope : scope,
24682                 reader : reader
24683             };
24684             var conn = this;
24685
24686             window[trans.cb] = function(o){
24687                 conn.handleResponse(o, trans);
24688             };
24689
24690             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
24691
24692             if(this.autoAbort !== false){
24693                 this.abort();
24694             }
24695
24696             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
24697
24698             var script = document.createElement("script");
24699             script.setAttribute("src", url);
24700             script.setAttribute("type", "text/javascript");
24701             script.setAttribute("id", trans.scriptId);
24702             this.head.appendChild(script);
24703
24704             this.trans = trans;
24705         }else{
24706             callback.call(scope||this, null, arg, false);
24707         }
24708     },
24709
24710     // private
24711     isLoading : function(){
24712         return this.trans ? true : false;
24713     },
24714
24715     /**
24716      * Abort the current server request.
24717      */
24718     abort : function(){
24719         if(this.isLoading()){
24720             this.destroyTrans(this.trans);
24721         }
24722     },
24723
24724     // private
24725     destroyTrans : function(trans, isLoaded){
24726         this.head.removeChild(document.getElementById(trans.scriptId));
24727         clearTimeout(trans.timeoutId);
24728         if(isLoaded){
24729             window[trans.cb] = undefined;
24730             try{
24731                 delete window[trans.cb];
24732             }catch(e){}
24733         }else{
24734             // if hasn't been loaded, wait for load to remove it to prevent script error
24735             window[trans.cb] = function(){
24736                 window[trans.cb] = undefined;
24737                 try{
24738                     delete window[trans.cb];
24739                 }catch(e){}
24740             };
24741         }
24742     },
24743
24744     // private
24745     handleResponse : function(o, trans){
24746         this.trans = false;
24747         this.destroyTrans(trans, true);
24748         var result;
24749         try {
24750             result = trans.reader.readRecords(o);
24751         }catch(e){
24752             this.fireEvent("loadexception", this, o, trans.arg, e);
24753             trans.callback.call(trans.scope||window, null, trans.arg, false);
24754             return;
24755         }
24756         this.fireEvent("load", this, o, trans.arg);
24757         trans.callback.call(trans.scope||window, result, trans.arg, true);
24758     },
24759
24760     // private
24761     handleFailure : function(trans){
24762         this.trans = false;
24763         this.destroyTrans(trans, false);
24764         this.fireEvent("loadexception", this, null, trans.arg);
24765         trans.callback.call(trans.scope||window, null, trans.arg, false);
24766     }
24767 });/*
24768  * Based on:
24769  * Ext JS Library 1.1.1
24770  * Copyright(c) 2006-2007, Ext JS, LLC.
24771  *
24772  * Originally Released Under LGPL - original licence link has changed is not relivant.
24773  *
24774  * Fork - LGPL
24775  * <script type="text/javascript">
24776  */
24777
24778 /**
24779  * @class Roo.data.JsonReader
24780  * @extends Roo.data.DataReader
24781  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
24782  * based on mappings in a provided Roo.data.Record constructor.
24783  * 
24784  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
24785  * in the reply previously. 
24786  * 
24787  * <p>
24788  * Example code:
24789  * <pre><code>
24790 var RecordDef = Roo.data.Record.create([
24791     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24792     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24793 ]);
24794 var myReader = new Roo.data.JsonReader({
24795     totalProperty: "results",    // The property which contains the total dataset size (optional)
24796     root: "rows",                // The property which contains an Array of row objects
24797     id: "id"                     // The property within each row object that provides an ID for the record (optional)
24798 }, RecordDef);
24799 </code></pre>
24800  * <p>
24801  * This would consume a JSON file like this:
24802  * <pre><code>
24803 { 'results': 2, 'rows': [
24804     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
24805     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
24806 }
24807 </code></pre>
24808  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
24809  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24810  * paged from the remote server.
24811  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
24812  * @cfg {String} root name of the property which contains the Array of row objects.
24813  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
24814  * @cfg {Array} fields Array of field definition objects
24815  * @constructor
24816  * Create a new JsonReader
24817  * @param {Object} meta Metadata configuration options
24818  * @param {Object} recordType Either an Array of field definition objects,
24819  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
24820  */
24821 Roo.data.JsonReader = function(meta, recordType){
24822     
24823     meta = meta || {};
24824     // set some defaults:
24825     Roo.applyIf(meta, {
24826         totalProperty: 'total',
24827         successProperty : 'success',
24828         root : 'data',
24829         id : 'id'
24830     });
24831     
24832     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24833 };
24834 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
24835     
24836     readerType : 'Json',
24837     
24838     /**
24839      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
24840      * Used by Store query builder to append _requestMeta to params.
24841      * 
24842      */
24843     metaFromRemote : false,
24844     /**
24845      * This method is only used by a DataProxy which has retrieved data from a remote server.
24846      * @param {Object} response The XHR object which contains the JSON data in its responseText.
24847      * @return {Object} data A data block which is used by an Roo.data.Store object as
24848      * a cache of Roo.data.Records.
24849      */
24850     read : function(response){
24851         var json = response.responseText;
24852        
24853         var o = /* eval:var:o */ eval("("+json+")");
24854         if(!o) {
24855             throw {message: "JsonReader.read: Json object not found"};
24856         }
24857         
24858         if(o.metaData){
24859             
24860             delete this.ef;
24861             this.metaFromRemote = true;
24862             this.meta = o.metaData;
24863             this.recordType = Roo.data.Record.create(o.metaData.fields);
24864             this.onMetaChange(this.meta, this.recordType, o);
24865         }
24866         return this.readRecords(o);
24867     },
24868
24869     // private function a store will implement
24870     onMetaChange : function(meta, recordType, o){
24871
24872     },
24873
24874     /**
24875          * @ignore
24876          */
24877     simpleAccess: function(obj, subsc) {
24878         return obj[subsc];
24879     },
24880
24881         /**
24882          * @ignore
24883          */
24884     getJsonAccessor: function(){
24885         var re = /[\[\.]/;
24886         return function(expr) {
24887             try {
24888                 return(re.test(expr))
24889                     ? new Function("obj", "return obj." + expr)
24890                     : function(obj){
24891                         return obj[expr];
24892                     };
24893             } catch(e){}
24894             return Roo.emptyFn;
24895         };
24896     }(),
24897
24898     /**
24899      * Create a data block containing Roo.data.Records from an XML document.
24900      * @param {Object} o An object which contains an Array of row objects in the property specified
24901      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
24902      * which contains the total size of the dataset.
24903      * @return {Object} data A data block which is used by an Roo.data.Store object as
24904      * a cache of Roo.data.Records.
24905      */
24906     readRecords : function(o){
24907         /**
24908          * After any data loads, the raw JSON data is available for further custom processing.
24909          * @type Object
24910          */
24911         this.o = o;
24912         var s = this.meta, Record = this.recordType,
24913             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
24914
24915 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
24916         if (!this.ef) {
24917             if(s.totalProperty) {
24918                     this.getTotal = this.getJsonAccessor(s.totalProperty);
24919                 }
24920                 if(s.successProperty) {
24921                     this.getSuccess = this.getJsonAccessor(s.successProperty);
24922                 }
24923                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
24924                 if (s.id) {
24925                         var g = this.getJsonAccessor(s.id);
24926                         this.getId = function(rec) {
24927                                 var r = g(rec);  
24928                                 return (r === undefined || r === "") ? null : r;
24929                         };
24930                 } else {
24931                         this.getId = function(){return null;};
24932                 }
24933             this.ef = [];
24934             for(var jj = 0; jj < fl; jj++){
24935                 f = fi[jj];
24936                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
24937                 this.ef[jj] = this.getJsonAccessor(map);
24938             }
24939         }
24940
24941         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
24942         if(s.totalProperty){
24943             var vt = parseInt(this.getTotal(o), 10);
24944             if(!isNaN(vt)){
24945                 totalRecords = vt;
24946             }
24947         }
24948         if(s.successProperty){
24949             var vs = this.getSuccess(o);
24950             if(vs === false || vs === 'false'){
24951                 success = false;
24952             }
24953         }
24954         var records = [];
24955         for(var i = 0; i < c; i++){
24956                 var n = root[i];
24957             var values = {};
24958             var id = this.getId(n);
24959             for(var j = 0; j < fl; j++){
24960                 f = fi[j];
24961             var v = this.ef[j](n);
24962             if (!f.convert) {
24963                 Roo.log('missing convert for ' + f.name);
24964                 Roo.log(f);
24965                 continue;
24966             }
24967             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
24968             }
24969             var record = new Record(values, id);
24970             record.json = n;
24971             records[i] = record;
24972         }
24973         return {
24974             raw : o,
24975             success : success,
24976             records : records,
24977             totalRecords : totalRecords
24978         };
24979     },
24980     // used when loading children.. @see loadDataFromChildren
24981     toLoadData: function(rec)
24982     {
24983         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
24984         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
24985         return { data : data, total : data.length };
24986         
24987     }
24988 });/*
24989  * Based on:
24990  * Ext JS Library 1.1.1
24991  * Copyright(c) 2006-2007, Ext JS, LLC.
24992  *
24993  * Originally Released Under LGPL - original licence link has changed is not relivant.
24994  *
24995  * Fork - LGPL
24996  * <script type="text/javascript">
24997  */
24998
24999 /**
25000  * @class Roo.data.XmlReader
25001  * @extends Roo.data.DataReader
25002  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
25003  * based on mappings in a provided Roo.data.Record constructor.<br><br>
25004  * <p>
25005  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
25006  * header in the HTTP response must be set to "text/xml".</em>
25007  * <p>
25008  * Example code:
25009  * <pre><code>
25010 var RecordDef = Roo.data.Record.create([
25011    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
25012    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
25013 ]);
25014 var myReader = new Roo.data.XmlReader({
25015    totalRecords: "results", // The element which contains the total dataset size (optional)
25016    record: "row",           // The repeated element which contains row information
25017    id: "id"                 // The element within the row that provides an ID for the record (optional)
25018 }, RecordDef);
25019 </code></pre>
25020  * <p>
25021  * This would consume an XML file like this:
25022  * <pre><code>
25023 &lt;?xml?>
25024 &lt;dataset>
25025  &lt;results>2&lt;/results>
25026  &lt;row>
25027    &lt;id>1&lt;/id>
25028    &lt;name>Bill&lt;/name>
25029    &lt;occupation>Gardener&lt;/occupation>
25030  &lt;/row>
25031  &lt;row>
25032    &lt;id>2&lt;/id>
25033    &lt;name>Ben&lt;/name>
25034    &lt;occupation>Horticulturalist&lt;/occupation>
25035  &lt;/row>
25036 &lt;/dataset>
25037 </code></pre>
25038  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
25039  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
25040  * paged from the remote server.
25041  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
25042  * @cfg {String} success The DomQuery path to the success attribute used by forms.
25043  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
25044  * a record identifier value.
25045  * @constructor
25046  * Create a new XmlReader
25047  * @param {Object} meta Metadata configuration options
25048  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
25049  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
25050  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
25051  */
25052 Roo.data.XmlReader = function(meta, recordType){
25053     meta = meta || {};
25054     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
25055 };
25056 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
25057     
25058     readerType : 'Xml',
25059     
25060     /**
25061      * This method is only used by a DataProxy which has retrieved data from a remote server.
25062          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
25063          * to contain a method called 'responseXML' that returns an XML document object.
25064      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
25065      * a cache of Roo.data.Records.
25066      */
25067     read : function(response){
25068         var doc = response.responseXML;
25069         if(!doc) {
25070             throw {message: "XmlReader.read: XML Document not available"};
25071         }
25072         return this.readRecords(doc);
25073     },
25074
25075     /**
25076      * Create a data block containing Roo.data.Records from an XML document.
25077          * @param {Object} doc A parsed XML document.
25078      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
25079      * a cache of Roo.data.Records.
25080      */
25081     readRecords : function(doc){
25082         /**
25083          * After any data loads/reads, the raw XML Document is available for further custom processing.
25084          * @type XMLDocument
25085          */
25086         this.xmlData = doc;
25087         var root = doc.documentElement || doc;
25088         var q = Roo.DomQuery;
25089         var recordType = this.recordType, fields = recordType.prototype.fields;
25090         var sid = this.meta.id;
25091         var totalRecords = 0, success = true;
25092         if(this.meta.totalRecords){
25093             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
25094         }
25095         
25096         if(this.meta.success){
25097             var sv = q.selectValue(this.meta.success, root, true);
25098             success = sv !== false && sv !== 'false';
25099         }
25100         var records = [];
25101         var ns = q.select(this.meta.record, root);
25102         for(var i = 0, len = ns.length; i < len; i++) {
25103                 var n = ns[i];
25104                 var values = {};
25105                 var id = sid ? q.selectValue(sid, n) : undefined;
25106                 for(var j = 0, jlen = fields.length; j < jlen; j++){
25107                     var f = fields.items[j];
25108                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
25109                     v = f.convert(v);
25110                     values[f.name] = v;
25111                 }
25112                 var record = new recordType(values, id);
25113                 record.node = n;
25114                 records[records.length] = record;
25115             }
25116
25117             return {
25118                 success : success,
25119                 records : records,
25120                 totalRecords : totalRecords || records.length
25121             };
25122     }
25123 });/*
25124  * Based on:
25125  * Ext JS Library 1.1.1
25126  * Copyright(c) 2006-2007, Ext JS, LLC.
25127  *
25128  * Originally Released Under LGPL - original licence link has changed is not relivant.
25129  *
25130  * Fork - LGPL
25131  * <script type="text/javascript">
25132  */
25133
25134 /**
25135  * @class Roo.data.ArrayReader
25136  * @extends Roo.data.DataReader
25137  * Data reader class to create an Array of Roo.data.Record objects from an Array.
25138  * Each element of that Array represents a row of data fields. The
25139  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
25140  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
25141  * <p>
25142  * Example code:.
25143  * <pre><code>
25144 var RecordDef = Roo.data.Record.create([
25145     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
25146     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
25147 ]);
25148 var myReader = new Roo.data.ArrayReader({
25149     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
25150 }, RecordDef);
25151 </code></pre>
25152  * <p>
25153  * This would consume an Array like this:
25154  * <pre><code>
25155 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
25156   </code></pre>
25157  
25158  * @constructor
25159  * Create a new JsonReader
25160  * @param {Object} meta Metadata configuration options.
25161  * @param {Object|Array} recordType Either an Array of field definition objects
25162  * 
25163  * @cfg {Array} fields Array of field definition objects
25164  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
25165  * as specified to {@link Roo.data.Record#create},
25166  * or an {@link Roo.data.Record} object
25167  *
25168  * 
25169  * created using {@link Roo.data.Record#create}.
25170  */
25171 Roo.data.ArrayReader = function(meta, recordType)
25172 {    
25173     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
25174 };
25175
25176 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
25177     
25178       /**
25179      * Create a data block containing Roo.data.Records from an XML document.
25180      * @param {Object} o An Array of row objects which represents the dataset.
25181      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
25182      * a cache of Roo.data.Records.
25183      */
25184     readRecords : function(o)
25185     {
25186         var sid = this.meta ? this.meta.id : null;
25187         var recordType = this.recordType, fields = recordType.prototype.fields;
25188         var records = [];
25189         var root = o;
25190         for(var i = 0; i < root.length; i++){
25191                 var n = root[i];
25192             var values = {};
25193             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
25194             for(var j = 0, jlen = fields.length; j < jlen; j++){
25195                 var f = fields.items[j];
25196                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
25197                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
25198                 v = f.convert(v);
25199                 values[f.name] = v;
25200             }
25201             var record = new recordType(values, id);
25202             record.json = n;
25203             records[records.length] = record;
25204         }
25205         return {
25206             records : records,
25207             totalRecords : records.length
25208         };
25209     },
25210     // used when loading children.. @see loadDataFromChildren
25211     toLoadData: function(rec)
25212     {
25213         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
25214         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
25215         
25216     }
25217     
25218     
25219 });/*
25220  * Based on:
25221  * Ext JS Library 1.1.1
25222  * Copyright(c) 2006-2007, Ext JS, LLC.
25223  *
25224  * Originally Released Under LGPL - original licence link has changed is not relivant.
25225  *
25226  * Fork - LGPL
25227  * <script type="text/javascript">
25228  */
25229
25230
25231 /**
25232  * @class Roo.data.Tree
25233  * @extends Roo.util.Observable
25234  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
25235  * in the tree have most standard DOM functionality.
25236  * @constructor
25237  * @param {Node} root (optional) The root node
25238  */
25239 Roo.data.Tree = function(root){
25240    this.nodeHash = {};
25241    /**
25242     * The root node for this tree
25243     * @type Node
25244     */
25245    this.root = null;
25246    if(root){
25247        this.setRootNode(root);
25248    }
25249    this.addEvents({
25250        /**
25251         * @event append
25252         * Fires when a new child node is appended to a node in this tree.
25253         * @param {Tree} tree The owner tree
25254         * @param {Node} parent The parent node
25255         * @param {Node} node The newly appended node
25256         * @param {Number} index The index of the newly appended node
25257         */
25258        "append" : true,
25259        /**
25260         * @event remove
25261         * Fires when a child node is removed from a node in this tree.
25262         * @param {Tree} tree The owner tree
25263         * @param {Node} parent The parent node
25264         * @param {Node} node The child node removed
25265         */
25266        "remove" : true,
25267        /**
25268         * @event move
25269         * Fires when a node is moved to a new location in the tree
25270         * @param {Tree} tree The owner tree
25271         * @param {Node} node The node moved
25272         * @param {Node} oldParent The old parent of this node
25273         * @param {Node} newParent The new parent of this node
25274         * @param {Number} index The index it was moved to
25275         */
25276        "move" : true,
25277        /**
25278         * @event insert
25279         * Fires when a new child node is inserted in a node in this tree.
25280         * @param {Tree} tree The owner tree
25281         * @param {Node} parent The parent node
25282         * @param {Node} node The child node inserted
25283         * @param {Node} refNode The child node the node was inserted before
25284         */
25285        "insert" : true,
25286        /**
25287         * @event beforeappend
25288         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
25289         * @param {Tree} tree The owner tree
25290         * @param {Node} parent The parent node
25291         * @param {Node} node The child node to be appended
25292         */
25293        "beforeappend" : true,
25294        /**
25295         * @event beforeremove
25296         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
25297         * @param {Tree} tree The owner tree
25298         * @param {Node} parent The parent node
25299         * @param {Node} node The child node to be removed
25300         */
25301        "beforeremove" : true,
25302        /**
25303         * @event beforemove
25304         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
25305         * @param {Tree} tree The owner tree
25306         * @param {Node} node The node being moved
25307         * @param {Node} oldParent The parent of the node
25308         * @param {Node} newParent The new parent the node is moving to
25309         * @param {Number} index The index it is being moved to
25310         */
25311        "beforemove" : true,
25312        /**
25313         * @event beforeinsert
25314         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
25315         * @param {Tree} tree The owner tree
25316         * @param {Node} parent The parent node
25317         * @param {Node} node The child node to be inserted
25318         * @param {Node} refNode The child node the node is being inserted before
25319         */
25320        "beforeinsert" : true
25321    });
25322
25323     Roo.data.Tree.superclass.constructor.call(this);
25324 };
25325
25326 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
25327     pathSeparator: "/",
25328
25329     proxyNodeEvent : function(){
25330         return this.fireEvent.apply(this, arguments);
25331     },
25332
25333     /**
25334      * Returns the root node for this tree.
25335      * @return {Node}
25336      */
25337     getRootNode : function(){
25338         return this.root;
25339     },
25340
25341     /**
25342      * Sets the root node for this tree.
25343      * @param {Node} node
25344      * @return {Node}
25345      */
25346     setRootNode : function(node){
25347         this.root = node;
25348         node.ownerTree = this;
25349         node.isRoot = true;
25350         this.registerNode(node);
25351         return node;
25352     },
25353
25354     /**
25355      * Gets a node in this tree by its id.
25356      * @param {String} id
25357      * @return {Node}
25358      */
25359     getNodeById : function(id){
25360         return this.nodeHash[id];
25361     },
25362
25363     registerNode : function(node){
25364         this.nodeHash[node.id] = node;
25365     },
25366
25367     unregisterNode : function(node){
25368         delete this.nodeHash[node.id];
25369     },
25370
25371     toString : function(){
25372         return "[Tree"+(this.id?" "+this.id:"")+"]";
25373     }
25374 });
25375
25376 /**
25377  * @class Roo.data.Node
25378  * @extends Roo.util.Observable
25379  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
25380  * @cfg {String} id The id for this node. If one is not specified, one is generated.
25381  * @constructor
25382  * @param {Object} attributes The attributes/config for the node
25383  */
25384 Roo.data.Node = function(attributes){
25385     /**
25386      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
25387      * @type {Object}
25388      */
25389     this.attributes = attributes || {};
25390     this.leaf = this.attributes.leaf;
25391     /**
25392      * The node id. @type String
25393      */
25394     this.id = this.attributes.id;
25395     if(!this.id){
25396         this.id = Roo.id(null, "ynode-");
25397         this.attributes.id = this.id;
25398     }
25399      
25400     
25401     /**
25402      * All child nodes of this node. @type Array
25403      */
25404     this.childNodes = [];
25405     if(!this.childNodes.indexOf){ // indexOf is a must
25406         this.childNodes.indexOf = function(o){
25407             for(var i = 0, len = this.length; i < len; i++){
25408                 if(this[i] == o) {
25409                     return i;
25410                 }
25411             }
25412             return -1;
25413         };
25414     }
25415     /**
25416      * The parent node for this node. @type Node
25417      */
25418     this.parentNode = null;
25419     /**
25420      * The first direct child node of this node, or null if this node has no child nodes. @type Node
25421      */
25422     this.firstChild = null;
25423     /**
25424      * The last direct child node of this node, or null if this node has no child nodes. @type Node
25425      */
25426     this.lastChild = null;
25427     /**
25428      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
25429      */
25430     this.previousSibling = null;
25431     /**
25432      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
25433      */
25434     this.nextSibling = null;
25435
25436     this.addEvents({
25437        /**
25438         * @event append
25439         * Fires when a new child node is appended
25440         * @param {Tree} tree The owner tree
25441         * @param {Node} this This node
25442         * @param {Node} node The newly appended node
25443         * @param {Number} index The index of the newly appended node
25444         */
25445        "append" : true,
25446        /**
25447         * @event remove
25448         * Fires when a child node is removed
25449         * @param {Tree} tree The owner tree
25450         * @param {Node} this This node
25451         * @param {Node} node The removed node
25452         */
25453        "remove" : true,
25454        /**
25455         * @event move
25456         * Fires when this node is moved to a new location in the tree
25457         * @param {Tree} tree The owner tree
25458         * @param {Node} this This node
25459         * @param {Node} oldParent The old parent of this node
25460         * @param {Node} newParent The new parent of this node
25461         * @param {Number} index The index it was moved to
25462         */
25463        "move" : true,
25464        /**
25465         * @event insert
25466         * Fires when a new child node is inserted.
25467         * @param {Tree} tree The owner tree
25468         * @param {Node} this This node
25469         * @param {Node} node The child node inserted
25470         * @param {Node} refNode The child node the node was inserted before
25471         */
25472        "insert" : true,
25473        /**
25474         * @event beforeappend
25475         * Fires before a new child is appended, return false to cancel the append.
25476         * @param {Tree} tree The owner tree
25477         * @param {Node} this This node
25478         * @param {Node} node The child node to be appended
25479         */
25480        "beforeappend" : true,
25481        /**
25482         * @event beforeremove
25483         * Fires before a child is removed, return false to cancel the remove.
25484         * @param {Tree} tree The owner tree
25485         * @param {Node} this This node
25486         * @param {Node} node The child node to be removed
25487         */
25488        "beforeremove" : true,
25489        /**
25490         * @event beforemove
25491         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
25492         * @param {Tree} tree The owner tree
25493         * @param {Node} this This node
25494         * @param {Node} oldParent The parent of this node
25495         * @param {Node} newParent The new parent this node is moving to
25496         * @param {Number} index The index it is being moved to
25497         */
25498        "beforemove" : true,
25499        /**
25500         * @event beforeinsert
25501         * Fires before a new child is inserted, return false to cancel the insert.
25502         * @param {Tree} tree The owner tree
25503         * @param {Node} this This node
25504         * @param {Node} node The child node to be inserted
25505         * @param {Node} refNode The child node the node is being inserted before
25506         */
25507        "beforeinsert" : true
25508    });
25509     this.listeners = this.attributes.listeners;
25510     Roo.data.Node.superclass.constructor.call(this);
25511 };
25512
25513 Roo.extend(Roo.data.Node, Roo.util.Observable, {
25514     fireEvent : function(evtName){
25515         // first do standard event for this node
25516         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
25517             return false;
25518         }
25519         // then bubble it up to the tree if the event wasn't cancelled
25520         var ot = this.getOwnerTree();
25521         if(ot){
25522             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
25523                 return false;
25524             }
25525         }
25526         return true;
25527     },
25528
25529     /**
25530      * Returns true if this node is a leaf
25531      * @return {Boolean}
25532      */
25533     isLeaf : function(){
25534         return this.leaf === true;
25535     },
25536
25537     // private
25538     setFirstChild : function(node){
25539         this.firstChild = node;
25540     },
25541
25542     //private
25543     setLastChild : function(node){
25544         this.lastChild = node;
25545     },
25546
25547
25548     /**
25549      * Returns true if this node is the last child of its parent
25550      * @return {Boolean}
25551      */
25552     isLast : function(){
25553        return (!this.parentNode ? true : this.parentNode.lastChild == this);
25554     },
25555
25556     /**
25557      * Returns true if this node is the first child of its parent
25558      * @return {Boolean}
25559      */
25560     isFirst : function(){
25561        return (!this.parentNode ? true : this.parentNode.firstChild == this);
25562     },
25563
25564     hasChildNodes : function(){
25565         return !this.isLeaf() && this.childNodes.length > 0;
25566     },
25567
25568     /**
25569      * Insert node(s) as the last child node of this node.
25570      * @param {Node/Array} node The node or Array of nodes to append
25571      * @return {Node} The appended node if single append, or null if an array was passed
25572      */
25573     appendChild : function(node){
25574         var multi = false;
25575         if(node instanceof Array){
25576             multi = node;
25577         }else if(arguments.length > 1){
25578             multi = arguments;
25579         }
25580         
25581         // if passed an array or multiple args do them one by one
25582         if(multi){
25583             for(var i = 0, len = multi.length; i < len; i++) {
25584                 this.appendChild(multi[i]);
25585             }
25586         }else{
25587             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
25588                 return false;
25589             }
25590             var index = this.childNodes.length;
25591             var oldParent = node.parentNode;
25592             // it's a move, make sure we move it cleanly
25593             if(oldParent){
25594                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
25595                     return false;
25596                 }
25597                 oldParent.removeChild(node);
25598             }
25599             
25600             index = this.childNodes.length;
25601             if(index == 0){
25602                 this.setFirstChild(node);
25603             }
25604             this.childNodes.push(node);
25605             node.parentNode = this;
25606             var ps = this.childNodes[index-1];
25607             if(ps){
25608                 node.previousSibling = ps;
25609                 ps.nextSibling = node;
25610             }else{
25611                 node.previousSibling = null;
25612             }
25613             node.nextSibling = null;
25614             this.setLastChild(node);
25615             node.setOwnerTree(this.getOwnerTree());
25616             this.fireEvent("append", this.ownerTree, this, node, index);
25617             if(this.ownerTree) {
25618                 this.ownerTree.fireEvent("appendnode", this, node, index);
25619             }
25620             if(oldParent){
25621                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
25622             }
25623             return node;
25624         }
25625     },
25626
25627     /**
25628      * Removes a child node from this node.
25629      * @param {Node} node The node to remove
25630      * @return {Node} The removed node
25631      */
25632     removeChild : function(node){
25633         var index = this.childNodes.indexOf(node);
25634         if(index == -1){
25635             return false;
25636         }
25637         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
25638             return false;
25639         }
25640
25641         // remove it from childNodes collection
25642         this.childNodes.splice(index, 1);
25643
25644         // update siblings
25645         if(node.previousSibling){
25646             node.previousSibling.nextSibling = node.nextSibling;
25647         }
25648         if(node.nextSibling){
25649             node.nextSibling.previousSibling = node.previousSibling;
25650         }
25651
25652         // update child refs
25653         if(this.firstChild == node){
25654             this.setFirstChild(node.nextSibling);
25655         }
25656         if(this.lastChild == node){
25657             this.setLastChild(node.previousSibling);
25658         }
25659
25660         node.setOwnerTree(null);
25661         // clear any references from the node
25662         node.parentNode = null;
25663         node.previousSibling = null;
25664         node.nextSibling = null;
25665         this.fireEvent("remove", this.ownerTree, this, node);
25666         return node;
25667     },
25668
25669     /**
25670      * Inserts the first node before the second node in this nodes childNodes collection.
25671      * @param {Node} node The node to insert
25672      * @param {Node} refNode The node to insert before (if null the node is appended)
25673      * @return {Node} The inserted node
25674      */
25675     insertBefore : function(node, refNode){
25676         if(!refNode){ // like standard Dom, refNode can be null for append
25677             return this.appendChild(node);
25678         }
25679         // nothing to do
25680         if(node == refNode){
25681             return false;
25682         }
25683
25684         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
25685             return false;
25686         }
25687         var index = this.childNodes.indexOf(refNode);
25688         var oldParent = node.parentNode;
25689         var refIndex = index;
25690
25691         // when moving internally, indexes will change after remove
25692         if(oldParent == this && this.childNodes.indexOf(node) < index){
25693             refIndex--;
25694         }
25695
25696         // it's a move, make sure we move it cleanly
25697         if(oldParent){
25698             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
25699                 return false;
25700             }
25701             oldParent.removeChild(node);
25702         }
25703         if(refIndex == 0){
25704             this.setFirstChild(node);
25705         }
25706         this.childNodes.splice(refIndex, 0, node);
25707         node.parentNode = this;
25708         var ps = this.childNodes[refIndex-1];
25709         if(ps){
25710             node.previousSibling = ps;
25711             ps.nextSibling = node;
25712         }else{
25713             node.previousSibling = null;
25714         }
25715         node.nextSibling = refNode;
25716         refNode.previousSibling = node;
25717         node.setOwnerTree(this.getOwnerTree());
25718         this.fireEvent("insert", this.ownerTree, this, node, refNode);
25719         if(oldParent){
25720             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
25721         }
25722         return node;
25723     },
25724
25725     /**
25726      * Returns the child node at the specified index.
25727      * @param {Number} index
25728      * @return {Node}
25729      */
25730     item : function(index){
25731         return this.childNodes[index];
25732     },
25733
25734     /**
25735      * Replaces one child node in this node with another.
25736      * @param {Node} newChild The replacement node
25737      * @param {Node} oldChild The node to replace
25738      * @return {Node} The replaced node
25739      */
25740     replaceChild : function(newChild, oldChild){
25741         this.insertBefore(newChild, oldChild);
25742         this.removeChild(oldChild);
25743         return oldChild;
25744     },
25745
25746     /**
25747      * Returns the index of a child node
25748      * @param {Node} node
25749      * @return {Number} The index of the node or -1 if it was not found
25750      */
25751     indexOf : function(child){
25752         return this.childNodes.indexOf(child);
25753     },
25754
25755     /**
25756      * Returns the tree this node is in.
25757      * @return {Tree}
25758      */
25759     getOwnerTree : function(){
25760         // if it doesn't have one, look for one
25761         if(!this.ownerTree){
25762             var p = this;
25763             while(p){
25764                 if(p.ownerTree){
25765                     this.ownerTree = p.ownerTree;
25766                     break;
25767                 }
25768                 p = p.parentNode;
25769             }
25770         }
25771         return this.ownerTree;
25772     },
25773
25774     /**
25775      * Returns depth of this node (the root node has a depth of 0)
25776      * @return {Number}
25777      */
25778     getDepth : function(){
25779         var depth = 0;
25780         var p = this;
25781         while(p.parentNode){
25782             ++depth;
25783             p = p.parentNode;
25784         }
25785         return depth;
25786     },
25787
25788     // private
25789     setOwnerTree : function(tree){
25790         // if it's move, we need to update everyone
25791         if(tree != this.ownerTree){
25792             if(this.ownerTree){
25793                 this.ownerTree.unregisterNode(this);
25794             }
25795             this.ownerTree = tree;
25796             var cs = this.childNodes;
25797             for(var i = 0, len = cs.length; i < len; i++) {
25798                 cs[i].setOwnerTree(tree);
25799             }
25800             if(tree){
25801                 tree.registerNode(this);
25802             }
25803         }
25804     },
25805
25806     /**
25807      * Returns the path for this node. The path can be used to expand or select this node programmatically.
25808      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
25809      * @return {String} The path
25810      */
25811     getPath : function(attr){
25812         attr = attr || "id";
25813         var p = this.parentNode;
25814         var b = [this.attributes[attr]];
25815         while(p){
25816             b.unshift(p.attributes[attr]);
25817             p = p.parentNode;
25818         }
25819         var sep = this.getOwnerTree().pathSeparator;
25820         return sep + b.join(sep);
25821     },
25822
25823     /**
25824      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25825      * function call will be the scope provided or the current node. The arguments to the function
25826      * will be the args provided or the current node. If the function returns false at any point,
25827      * the bubble is stopped.
25828      * @param {Function} fn The function to call
25829      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25830      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25831      */
25832     bubble : function(fn, scope, args){
25833         var p = this;
25834         while(p){
25835             if(fn.call(scope || p, args || p) === false){
25836                 break;
25837             }
25838             p = p.parentNode;
25839         }
25840     },
25841
25842     /**
25843      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25844      * function call will be the scope provided or the current node. The arguments to the function
25845      * will be the args provided or the current node. If the function returns false at any point,
25846      * the cascade is stopped on that branch.
25847      * @param {Function} fn The function to call
25848      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25849      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25850      */
25851     cascade : function(fn, scope, args){
25852         if(fn.call(scope || this, args || this) !== false){
25853             var cs = this.childNodes;
25854             for(var i = 0, len = cs.length; i < len; i++) {
25855                 cs[i].cascade(fn, scope, args);
25856             }
25857         }
25858     },
25859
25860     /**
25861      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
25862      * function call will be the scope provided or the current node. The arguments to the function
25863      * will be the args provided or the current node. If the function returns false at any point,
25864      * the iteration stops.
25865      * @param {Function} fn The function to call
25866      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25867      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25868      */
25869     eachChild : function(fn, scope, args){
25870         var cs = this.childNodes;
25871         for(var i = 0, len = cs.length; i < len; i++) {
25872                 if(fn.call(scope || this, args || cs[i]) === false){
25873                     break;
25874                 }
25875         }
25876     },
25877
25878     /**
25879      * Finds the first child that has the attribute with the specified value.
25880      * @param {String} attribute The attribute name
25881      * @param {Mixed} value The value to search for
25882      * @return {Node} The found child or null if none was found
25883      */
25884     findChild : function(attribute, value){
25885         var cs = this.childNodes;
25886         for(var i = 0, len = cs.length; i < len; i++) {
25887                 if(cs[i].attributes[attribute] == value){
25888                     return cs[i];
25889                 }
25890         }
25891         return null;
25892     },
25893
25894     /**
25895      * Finds the first child by a custom function. The child matches if the function passed
25896      * returns true.
25897      * @param {Function} fn
25898      * @param {Object} scope (optional)
25899      * @return {Node} The found child or null if none was found
25900      */
25901     findChildBy : function(fn, scope){
25902         var cs = this.childNodes;
25903         for(var i = 0, len = cs.length; i < len; i++) {
25904                 if(fn.call(scope||cs[i], cs[i]) === true){
25905                     return cs[i];
25906                 }
25907         }
25908         return null;
25909     },
25910
25911     /**
25912      * Sorts this nodes children using the supplied sort function
25913      * @param {Function} fn
25914      * @param {Object} scope (optional)
25915      */
25916     sort : function(fn, scope){
25917         var cs = this.childNodes;
25918         var len = cs.length;
25919         if(len > 0){
25920             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
25921             cs.sort(sortFn);
25922             for(var i = 0; i < len; i++){
25923                 var n = cs[i];
25924                 n.previousSibling = cs[i-1];
25925                 n.nextSibling = cs[i+1];
25926                 if(i == 0){
25927                     this.setFirstChild(n);
25928                 }
25929                 if(i == len-1){
25930                     this.setLastChild(n);
25931                 }
25932             }
25933         }
25934     },
25935
25936     /**
25937      * Returns true if this node is an ancestor (at any point) of the passed node.
25938      * @param {Node} node
25939      * @return {Boolean}
25940      */
25941     contains : function(node){
25942         return node.isAncestor(this);
25943     },
25944
25945     /**
25946      * Returns true if the passed node is an ancestor (at any point) of this node.
25947      * @param {Node} node
25948      * @return {Boolean}
25949      */
25950     isAncestor : function(node){
25951         var p = this.parentNode;
25952         while(p){
25953             if(p == node){
25954                 return true;
25955             }
25956             p = p.parentNode;
25957         }
25958         return false;
25959     },
25960
25961     toString : function(){
25962         return "[Node"+(this.id?" "+this.id:"")+"]";
25963     }
25964 });/*
25965  * Based on:
25966  * Ext JS Library 1.1.1
25967  * Copyright(c) 2006-2007, Ext JS, LLC.
25968  *
25969  * Originally Released Under LGPL - original licence link has changed is not relivant.
25970  *
25971  * Fork - LGPL
25972  * <script type="text/javascript">
25973  */
25974
25975
25976 /**
25977  * @class Roo.Shadow
25978  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
25979  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
25980  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
25981  * @constructor
25982  * Create a new Shadow
25983  * @param {Object} config The config object
25984  */
25985 Roo.Shadow = function(config){
25986     Roo.apply(this, config);
25987     if(typeof this.mode != "string"){
25988         this.mode = this.defaultMode;
25989     }
25990     var o = this.offset, a = {h: 0};
25991     var rad = Math.floor(this.offset/2);
25992     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
25993         case "drop":
25994             a.w = 0;
25995             a.l = a.t = o;
25996             a.t -= 1;
25997             if(Roo.isIE){
25998                 a.l -= this.offset + rad;
25999                 a.t -= this.offset + rad;
26000                 a.w -= rad;
26001                 a.h -= rad;
26002                 a.t += 1;
26003             }
26004         break;
26005         case "sides":
26006             a.w = (o*2);
26007             a.l = -o;
26008             a.t = o-1;
26009             if(Roo.isIE){
26010                 a.l -= (this.offset - rad);
26011                 a.t -= this.offset + rad;
26012                 a.l += 1;
26013                 a.w -= (this.offset - rad)*2;
26014                 a.w -= rad + 1;
26015                 a.h -= 1;
26016             }
26017         break;
26018         case "frame":
26019             a.w = a.h = (o*2);
26020             a.l = a.t = -o;
26021             a.t += 1;
26022             a.h -= 2;
26023             if(Roo.isIE){
26024                 a.l -= (this.offset - rad);
26025                 a.t -= (this.offset - rad);
26026                 a.l += 1;
26027                 a.w -= (this.offset + rad + 1);
26028                 a.h -= (this.offset + rad);
26029                 a.h += 1;
26030             }
26031         break;
26032     };
26033
26034     this.adjusts = a;
26035 };
26036
26037 Roo.Shadow.prototype = {
26038     /**
26039      * @cfg {String} mode
26040      * The shadow display mode.  Supports the following options:<br />
26041      * sides: Shadow displays on both sides and bottom only<br />
26042      * frame: Shadow displays equally on all four sides<br />
26043      * drop: Traditional bottom-right drop shadow (default)
26044      */
26045     /**
26046      * @cfg {String} offset
26047      * The number of pixels to offset the shadow from the element (defaults to 4)
26048      */
26049     offset: 4,
26050
26051     // private
26052     defaultMode: "drop",
26053
26054     /**
26055      * Displays the shadow under the target element
26056      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
26057      */
26058     show : function(target){
26059         target = Roo.get(target);
26060         if(!this.el){
26061             this.el = Roo.Shadow.Pool.pull();
26062             if(this.el.dom.nextSibling != target.dom){
26063                 this.el.insertBefore(target);
26064             }
26065         }
26066         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
26067         if(Roo.isIE){
26068             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
26069         }
26070         this.realign(
26071             target.getLeft(true),
26072             target.getTop(true),
26073             target.getWidth(),
26074             target.getHeight()
26075         );
26076         this.el.dom.style.display = "block";
26077     },
26078
26079     /**
26080      * Returns true if the shadow is visible, else false
26081      */
26082     isVisible : function(){
26083         return this.el ? true : false;  
26084     },
26085
26086     /**
26087      * Direct alignment when values are already available. Show must be called at least once before
26088      * calling this method to ensure it is initialized.
26089      * @param {Number} left The target element left position
26090      * @param {Number} top The target element top position
26091      * @param {Number} width The target element width
26092      * @param {Number} height The target element height
26093      */
26094     realign : function(l, t, w, h){
26095         if(!this.el){
26096             return;
26097         }
26098         var a = this.adjusts, d = this.el.dom, s = d.style;
26099         var iea = 0;
26100         s.left = (l+a.l)+"px";
26101         s.top = (t+a.t)+"px";
26102         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
26103  
26104         if(s.width != sws || s.height != shs){
26105             s.width = sws;
26106             s.height = shs;
26107             if(!Roo.isIE){
26108                 var cn = d.childNodes;
26109                 var sww = Math.max(0, (sw-12))+"px";
26110                 cn[0].childNodes[1].style.width = sww;
26111                 cn[1].childNodes[1].style.width = sww;
26112                 cn[2].childNodes[1].style.width = sww;
26113                 cn[1].style.height = Math.max(0, (sh-12))+"px";
26114             }
26115         }
26116     },
26117
26118     /**
26119      * Hides this shadow
26120      */
26121     hide : function(){
26122         if(this.el){
26123             this.el.dom.style.display = "none";
26124             Roo.Shadow.Pool.push(this.el);
26125             delete this.el;
26126         }
26127     },
26128
26129     /**
26130      * Adjust the z-index of this shadow
26131      * @param {Number} zindex The new z-index
26132      */
26133     setZIndex : function(z){
26134         this.zIndex = z;
26135         if(this.el){
26136             this.el.setStyle("z-index", z);
26137         }
26138     }
26139 };
26140
26141 // Private utility class that manages the internal Shadow cache
26142 Roo.Shadow.Pool = function(){
26143     var p = [];
26144     var markup = Roo.isIE ?
26145                  '<div class="x-ie-shadow"></div>' :
26146                  '<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>';
26147     return {
26148         pull : function(){
26149             var sh = p.shift();
26150             if(!sh){
26151                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
26152                 sh.autoBoxAdjust = false;
26153             }
26154             return sh;
26155         },
26156
26157         push : function(sh){
26158             p.push(sh);
26159         }
26160     };
26161 }();/*
26162  * Based on:
26163  * Ext JS Library 1.1.1
26164  * Copyright(c) 2006-2007, Ext JS, LLC.
26165  *
26166  * Originally Released Under LGPL - original licence link has changed is not relivant.
26167  *
26168  * Fork - LGPL
26169  * <script type="text/javascript">
26170  */
26171
26172
26173 /**
26174  * @class Roo.SplitBar
26175  * @extends Roo.util.Observable
26176  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
26177  * <br><br>
26178  * Usage:
26179  * <pre><code>
26180 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
26181                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
26182 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
26183 split.minSize = 100;
26184 split.maxSize = 600;
26185 split.animate = true;
26186 split.on('moved', splitterMoved);
26187 </code></pre>
26188  * @constructor
26189  * Create a new SplitBar
26190  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
26191  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
26192  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
26193  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
26194                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
26195                         position of the SplitBar).
26196  */
26197 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
26198     
26199     /** @private */
26200     this.el = Roo.get(dragElement, true);
26201     this.el.dom.unselectable = "on";
26202     /** @private */
26203     this.resizingEl = Roo.get(resizingElement, true);
26204
26205     /**
26206      * @private
26207      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
26208      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
26209      * @type Number
26210      */
26211     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
26212     
26213     /**
26214      * The minimum size of the resizing element. (Defaults to 0)
26215      * @type Number
26216      */
26217     this.minSize = 0;
26218     
26219     /**
26220      * The maximum size of the resizing element. (Defaults to 2000)
26221      * @type Number
26222      */
26223     this.maxSize = 2000;
26224     
26225     /**
26226      * Whether to animate the transition to the new size
26227      * @type Boolean
26228      */
26229     this.animate = false;
26230     
26231     /**
26232      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
26233      * @type Boolean
26234      */
26235     this.useShim = false;
26236     
26237     /** @private */
26238     this.shim = null;
26239     
26240     if(!existingProxy){
26241         /** @private */
26242         this.proxy = Roo.SplitBar.createProxy(this.orientation);
26243     }else{
26244         this.proxy = Roo.get(existingProxy).dom;
26245     }
26246     /** @private */
26247     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
26248     
26249     /** @private */
26250     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
26251     
26252     /** @private */
26253     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
26254     
26255     /** @private */
26256     this.dragSpecs = {};
26257     
26258     /**
26259      * @private The adapter to use to positon and resize elements
26260      */
26261     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
26262     this.adapter.init(this);
26263     
26264     if(this.orientation == Roo.SplitBar.HORIZONTAL){
26265         /** @private */
26266         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
26267         this.el.addClass("x-splitbar-h");
26268     }else{
26269         /** @private */
26270         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
26271         this.el.addClass("x-splitbar-v");
26272     }
26273     
26274     this.addEvents({
26275         /**
26276          * @event resize
26277          * Fires when the splitter is moved (alias for {@link #event-moved})
26278          * @param {Roo.SplitBar} this
26279          * @param {Number} newSize the new width or height
26280          */
26281         "resize" : true,
26282         /**
26283          * @event moved
26284          * Fires when the splitter is moved
26285          * @param {Roo.SplitBar} this
26286          * @param {Number} newSize the new width or height
26287          */
26288         "moved" : true,
26289         /**
26290          * @event beforeresize
26291          * Fires before the splitter is dragged
26292          * @param {Roo.SplitBar} this
26293          */
26294         "beforeresize" : true,
26295
26296         "beforeapply" : true
26297     });
26298
26299     Roo.util.Observable.call(this);
26300 };
26301
26302 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
26303     onStartProxyDrag : function(x, y){
26304         this.fireEvent("beforeresize", this);
26305         if(!this.overlay){
26306             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
26307             o.unselectable();
26308             o.enableDisplayMode("block");
26309             // all splitbars share the same overlay
26310             Roo.SplitBar.prototype.overlay = o;
26311         }
26312         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
26313         this.overlay.show();
26314         Roo.get(this.proxy).setDisplayed("block");
26315         var size = this.adapter.getElementSize(this);
26316         this.activeMinSize = this.getMinimumSize();;
26317         this.activeMaxSize = this.getMaximumSize();;
26318         var c1 = size - this.activeMinSize;
26319         var c2 = Math.max(this.activeMaxSize - size, 0);
26320         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26321             this.dd.resetConstraints();
26322             this.dd.setXConstraint(
26323                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
26324                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
26325             );
26326             this.dd.setYConstraint(0, 0);
26327         }else{
26328             this.dd.resetConstraints();
26329             this.dd.setXConstraint(0, 0);
26330             this.dd.setYConstraint(
26331                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
26332                 this.placement == Roo.SplitBar.TOP ? c2 : c1
26333             );
26334          }
26335         this.dragSpecs.startSize = size;
26336         this.dragSpecs.startPoint = [x, y];
26337         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
26338     },
26339     
26340     /** 
26341      * @private Called after the drag operation by the DDProxy
26342      */
26343     onEndProxyDrag : function(e){
26344         Roo.get(this.proxy).setDisplayed(false);
26345         var endPoint = Roo.lib.Event.getXY(e);
26346         if(this.overlay){
26347             this.overlay.hide();
26348         }
26349         var newSize;
26350         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26351             newSize = this.dragSpecs.startSize + 
26352                 (this.placement == Roo.SplitBar.LEFT ?
26353                     endPoint[0] - this.dragSpecs.startPoint[0] :
26354                     this.dragSpecs.startPoint[0] - endPoint[0]
26355                 );
26356         }else{
26357             newSize = this.dragSpecs.startSize + 
26358                 (this.placement == Roo.SplitBar.TOP ?
26359                     endPoint[1] - this.dragSpecs.startPoint[1] :
26360                     this.dragSpecs.startPoint[1] - endPoint[1]
26361                 );
26362         }
26363         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
26364         if(newSize != this.dragSpecs.startSize){
26365             if(this.fireEvent('beforeapply', this, newSize) !== false){
26366                 this.adapter.setElementSize(this, newSize);
26367                 this.fireEvent("moved", this, newSize);
26368                 this.fireEvent("resize", this, newSize);
26369             }
26370         }
26371     },
26372     
26373     /**
26374      * Get the adapter this SplitBar uses
26375      * @return The adapter object
26376      */
26377     getAdapter : function(){
26378         return this.adapter;
26379     },
26380     
26381     /**
26382      * Set the adapter this SplitBar uses
26383      * @param {Object} adapter A SplitBar adapter object
26384      */
26385     setAdapter : function(adapter){
26386         this.adapter = adapter;
26387         this.adapter.init(this);
26388     },
26389     
26390     /**
26391      * Gets the minimum size for the resizing element
26392      * @return {Number} The minimum size
26393      */
26394     getMinimumSize : function(){
26395         return this.minSize;
26396     },
26397     
26398     /**
26399      * Sets the minimum size for the resizing element
26400      * @param {Number} minSize The minimum size
26401      */
26402     setMinimumSize : function(minSize){
26403         this.minSize = minSize;
26404     },
26405     
26406     /**
26407      * Gets the maximum size for the resizing element
26408      * @return {Number} The maximum size
26409      */
26410     getMaximumSize : function(){
26411         return this.maxSize;
26412     },
26413     
26414     /**
26415      * Sets the maximum size for the resizing element
26416      * @param {Number} maxSize The maximum size
26417      */
26418     setMaximumSize : function(maxSize){
26419         this.maxSize = maxSize;
26420     },
26421     
26422     /**
26423      * Sets the initialize size for the resizing element
26424      * @param {Number} size The initial size
26425      */
26426     setCurrentSize : function(size){
26427         var oldAnimate = this.animate;
26428         this.animate = false;
26429         this.adapter.setElementSize(this, size);
26430         this.animate = oldAnimate;
26431     },
26432     
26433     /**
26434      * Destroy this splitbar. 
26435      * @param {Boolean} removeEl True to remove the element
26436      */
26437     destroy : function(removeEl){
26438         if(this.shim){
26439             this.shim.remove();
26440         }
26441         this.dd.unreg();
26442         this.proxy.parentNode.removeChild(this.proxy);
26443         if(removeEl){
26444             this.el.remove();
26445         }
26446     }
26447 });
26448
26449 /**
26450  * @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.
26451  */
26452 Roo.SplitBar.createProxy = function(dir){
26453     var proxy = new Roo.Element(document.createElement("div"));
26454     proxy.unselectable();
26455     var cls = 'x-splitbar-proxy';
26456     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
26457     document.body.appendChild(proxy.dom);
26458     return proxy.dom;
26459 };
26460
26461 /** 
26462  * @class Roo.SplitBar.BasicLayoutAdapter
26463  * Default Adapter. It assumes the splitter and resizing element are not positioned
26464  * elements and only gets/sets the width of the element. Generally used for table based layouts.
26465  */
26466 Roo.SplitBar.BasicLayoutAdapter = function(){
26467 };
26468
26469 Roo.SplitBar.BasicLayoutAdapter.prototype = {
26470     // do nothing for now
26471     init : function(s){
26472     
26473     },
26474     /**
26475      * Called before drag operations to get the current size of the resizing element. 
26476      * @param {Roo.SplitBar} s The SplitBar using this adapter
26477      */
26478      getElementSize : function(s){
26479         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26480             return s.resizingEl.getWidth();
26481         }else{
26482             return s.resizingEl.getHeight();
26483         }
26484     },
26485     
26486     /**
26487      * Called after drag operations to set the size of the resizing element.
26488      * @param {Roo.SplitBar} s The SplitBar using this adapter
26489      * @param {Number} newSize The new size to set
26490      * @param {Function} onComplete A function to be invoked when resizing is complete
26491      */
26492     setElementSize : function(s, newSize, onComplete){
26493         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26494             if(!s.animate){
26495                 s.resizingEl.setWidth(newSize);
26496                 if(onComplete){
26497                     onComplete(s, newSize);
26498                 }
26499             }else{
26500                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
26501             }
26502         }else{
26503             
26504             if(!s.animate){
26505                 s.resizingEl.setHeight(newSize);
26506                 if(onComplete){
26507                     onComplete(s, newSize);
26508                 }
26509             }else{
26510                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
26511             }
26512         }
26513     }
26514 };
26515
26516 /** 
26517  *@class Roo.SplitBar.AbsoluteLayoutAdapter
26518  * @extends Roo.SplitBar.BasicLayoutAdapter
26519  * Adapter that  moves the splitter element to align with the resized sizing element. 
26520  * Used with an absolute positioned SplitBar.
26521  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
26522  * document.body, make sure you assign an id to the body element.
26523  */
26524 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
26525     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
26526     this.container = Roo.get(container);
26527 };
26528
26529 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
26530     init : function(s){
26531         this.basic.init(s);
26532     },
26533     
26534     getElementSize : function(s){
26535         return this.basic.getElementSize(s);
26536     },
26537     
26538     setElementSize : function(s, newSize, onComplete){
26539         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
26540     },
26541     
26542     moveSplitter : function(s){
26543         var yes = Roo.SplitBar;
26544         switch(s.placement){
26545             case yes.LEFT:
26546                 s.el.setX(s.resizingEl.getRight());
26547                 break;
26548             case yes.RIGHT:
26549                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
26550                 break;
26551             case yes.TOP:
26552                 s.el.setY(s.resizingEl.getBottom());
26553                 break;
26554             case yes.BOTTOM:
26555                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
26556                 break;
26557         }
26558     }
26559 };
26560
26561 /**
26562  * Orientation constant - Create a vertical SplitBar
26563  * @static
26564  * @type Number
26565  */
26566 Roo.SplitBar.VERTICAL = 1;
26567
26568 /**
26569  * Orientation constant - Create a horizontal SplitBar
26570  * @static
26571  * @type Number
26572  */
26573 Roo.SplitBar.HORIZONTAL = 2;
26574
26575 /**
26576  * Placement constant - The resizing element is to the left of the splitter element
26577  * @static
26578  * @type Number
26579  */
26580 Roo.SplitBar.LEFT = 1;
26581
26582 /**
26583  * Placement constant - The resizing element is to the right of the splitter element
26584  * @static
26585  * @type Number
26586  */
26587 Roo.SplitBar.RIGHT = 2;
26588
26589 /**
26590  * Placement constant - The resizing element is positioned above the splitter element
26591  * @static
26592  * @type Number
26593  */
26594 Roo.SplitBar.TOP = 3;
26595
26596 /**
26597  * Placement constant - The resizing element is positioned under splitter element
26598  * @static
26599  * @type Number
26600  */
26601 Roo.SplitBar.BOTTOM = 4;
26602 /*
26603  * Based on:
26604  * Ext JS Library 1.1.1
26605  * Copyright(c) 2006-2007, Ext JS, LLC.
26606  *
26607  * Originally Released Under LGPL - original licence link has changed is not relivant.
26608  *
26609  * Fork - LGPL
26610  * <script type="text/javascript">
26611  */
26612
26613 /**
26614  * @class Roo.View
26615  * @extends Roo.util.Observable
26616  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
26617  * This class also supports single and multi selection modes. <br>
26618  * Create a data model bound view:
26619  <pre><code>
26620  var store = new Roo.data.Store(...);
26621
26622  var view = new Roo.View({
26623     el : "my-element",
26624     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
26625  
26626     singleSelect: true,
26627     selectedClass: "ydataview-selected",
26628     store: store
26629  });
26630
26631  // listen for node click?
26632  view.on("click", function(vw, index, node, e){
26633  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
26634  });
26635
26636  // load XML data
26637  dataModel.load("foobar.xml");
26638  </code></pre>
26639  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
26640  * <br><br>
26641  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
26642  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
26643  * 
26644  * Note: old style constructor is still suported (container, template, config)
26645  * 
26646  * @constructor
26647  * Create a new View
26648  * @param {Object} config The config object
26649  * 
26650  */
26651 Roo.View = function(config, depreciated_tpl, depreciated_config){
26652     
26653     this.parent = false;
26654     
26655     if (typeof(depreciated_tpl) == 'undefined') {
26656         // new way.. - universal constructor.
26657         Roo.apply(this, config);
26658         this.el  = Roo.get(this.el);
26659     } else {
26660         // old format..
26661         this.el  = Roo.get(config);
26662         this.tpl = depreciated_tpl;
26663         Roo.apply(this, depreciated_config);
26664     }
26665     this.wrapEl  = this.el.wrap().wrap();
26666     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
26667     
26668     
26669     if(typeof(this.tpl) == "string"){
26670         this.tpl = new Roo.Template(this.tpl);
26671     } else {
26672         // support xtype ctors..
26673         this.tpl = new Roo.factory(this.tpl, Roo);
26674     }
26675     
26676     
26677     this.tpl.compile();
26678     
26679     /** @private */
26680     this.addEvents({
26681         /**
26682          * @event beforeclick
26683          * Fires before a click is processed. Returns false to cancel the default action.
26684          * @param {Roo.View} this
26685          * @param {Number} index The index of the target node
26686          * @param {HTMLElement} node The target node
26687          * @param {Roo.EventObject} e The raw event object
26688          */
26689             "beforeclick" : true,
26690         /**
26691          * @event click
26692          * Fires when a template node is clicked.
26693          * @param {Roo.View} this
26694          * @param {Number} index The index of the target node
26695          * @param {HTMLElement} node The target node
26696          * @param {Roo.EventObject} e The raw event object
26697          */
26698             "click" : true,
26699         /**
26700          * @event dblclick
26701          * Fires when a template node is double clicked.
26702          * @param {Roo.View} this
26703          * @param {Number} index The index of the target node
26704          * @param {HTMLElement} node The target node
26705          * @param {Roo.EventObject} e The raw event object
26706          */
26707             "dblclick" : true,
26708         /**
26709          * @event contextmenu
26710          * Fires when a template node is right clicked.
26711          * @param {Roo.View} this
26712          * @param {Number} index The index of the target node
26713          * @param {HTMLElement} node The target node
26714          * @param {Roo.EventObject} e The raw event object
26715          */
26716             "contextmenu" : true,
26717         /**
26718          * @event selectionchange
26719          * Fires when the selected nodes change.
26720          * @param {Roo.View} this
26721          * @param {Array} selections Array of the selected nodes
26722          */
26723             "selectionchange" : true,
26724     
26725         /**
26726          * @event beforeselect
26727          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
26728          * @param {Roo.View} this
26729          * @param {HTMLElement} node The node to be selected
26730          * @param {Array} selections Array of currently selected nodes
26731          */
26732             "beforeselect" : true,
26733         /**
26734          * @event preparedata
26735          * Fires on every row to render, to allow you to change the data.
26736          * @param {Roo.View} this
26737          * @param {Object} data to be rendered (change this)
26738          */
26739           "preparedata" : true
26740           
26741           
26742         });
26743
26744
26745
26746     this.el.on({
26747         "click": this.onClick,
26748         "dblclick": this.onDblClick,
26749         "contextmenu": this.onContextMenu,
26750         scope:this
26751     });
26752
26753     this.selections = [];
26754     this.nodes = [];
26755     this.cmp = new Roo.CompositeElementLite([]);
26756     if(this.store){
26757         this.store = Roo.factory(this.store, Roo.data);
26758         this.setStore(this.store, true);
26759     }
26760     
26761     if ( this.footer && this.footer.xtype) {
26762            
26763          var fctr = this.wrapEl.appendChild(document.createElement("div"));
26764         
26765         this.footer.dataSource = this.store;
26766         this.footer.container = fctr;
26767         this.footer = Roo.factory(this.footer, Roo);
26768         fctr.insertFirst(this.el);
26769         
26770         // this is a bit insane - as the paging toolbar seems to detach the el..
26771 //        dom.parentNode.parentNode.parentNode
26772          // they get detached?
26773     }
26774     
26775     
26776     Roo.View.superclass.constructor.call(this);
26777     
26778     
26779 };
26780
26781 Roo.extend(Roo.View, Roo.util.Observable, {
26782     
26783      /**
26784      * @cfg {Roo.data.Store} store Data store to load data from.
26785      */
26786     store : false,
26787     
26788     /**
26789      * @cfg {String|Roo.Element} el The container element.
26790      */
26791     el : '',
26792     
26793     /**
26794      * @cfg {String|Roo.Template} tpl The template used by this View 
26795      */
26796     tpl : false,
26797     /**
26798      * @cfg {String} dataName the named area of the template to use as the data area
26799      *                          Works with domtemplates roo-name="name"
26800      */
26801     dataName: false,
26802     /**
26803      * @cfg {String} selectedClass The css class to add to selected nodes
26804      */
26805     selectedClass : "x-view-selected",
26806      /**
26807      * @cfg {String} emptyText The empty text to show when nothing is loaded.
26808      */
26809     emptyText : "",
26810     
26811     /**
26812      * @cfg {String} text to display on mask (default Loading)
26813      */
26814     mask : false,
26815     /**
26816      * @cfg {Boolean} multiSelect Allow multiple selection
26817      */
26818     multiSelect : false,
26819     /**
26820      * @cfg {Boolean} singleSelect Allow single selection
26821      */
26822     singleSelect:  false,
26823     
26824     /**
26825      * @cfg {Boolean} toggleSelect - selecting 
26826      */
26827     toggleSelect : false,
26828     
26829     /**
26830      * @cfg {Boolean} tickable - selecting 
26831      */
26832     tickable : false,
26833     
26834     /**
26835      * Returns the element this view is bound to.
26836      * @return {Roo.Element}
26837      */
26838     getEl : function(){
26839         return this.wrapEl;
26840     },
26841     
26842     
26843
26844     /**
26845      * Refreshes the view. - called by datachanged on the store. - do not call directly.
26846      */
26847     refresh : function(){
26848         //Roo.log('refresh');
26849         var t = this.tpl;
26850         
26851         // if we are using something like 'domtemplate', then
26852         // the what gets used is:
26853         // t.applySubtemplate(NAME, data, wrapping data..)
26854         // the outer template then get' applied with
26855         //     the store 'extra data'
26856         // and the body get's added to the
26857         //      roo-name="data" node?
26858         //      <span class='roo-tpl-{name}'></span> ?????
26859         
26860         
26861         
26862         this.clearSelections();
26863         this.el.update("");
26864         var html = [];
26865         var records = this.store.getRange();
26866         if(records.length < 1) {
26867             
26868             // is this valid??  = should it render a template??
26869             
26870             this.el.update(this.emptyText);
26871             return;
26872         }
26873         var el = this.el;
26874         if (this.dataName) {
26875             this.el.update(t.apply(this.store.meta)); //????
26876             el = this.el.child('.roo-tpl-' + this.dataName);
26877         }
26878         
26879         for(var i = 0, len = records.length; i < len; i++){
26880             var data = this.prepareData(records[i].data, i, records[i]);
26881             this.fireEvent("preparedata", this, data, i, records[i]);
26882             
26883             var d = Roo.apply({}, data);
26884             
26885             if(this.tickable){
26886                 Roo.apply(d, {'roo-id' : Roo.id()});
26887                 
26888                 var _this = this;
26889             
26890                 Roo.each(this.parent.item, function(item){
26891                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
26892                         return;
26893                     }
26894                     Roo.apply(d, {'roo-data-checked' : 'checked'});
26895                 });
26896             }
26897             
26898             html[html.length] = Roo.util.Format.trim(
26899                 this.dataName ?
26900                     t.applySubtemplate(this.dataName, d, this.store.meta) :
26901                     t.apply(d)
26902             );
26903         }
26904         
26905         
26906         
26907         el.update(html.join(""));
26908         this.nodes = el.dom.childNodes;
26909         this.updateIndexes(0);
26910     },
26911     
26912
26913     /**
26914      * Function to override to reformat the data that is sent to
26915      * the template for each node.
26916      * DEPRICATED - use the preparedata event handler.
26917      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
26918      * a JSON object for an UpdateManager bound view).
26919      */
26920     prepareData : function(data, index, record)
26921     {
26922         this.fireEvent("preparedata", this, data, index, record);
26923         return data;
26924     },
26925
26926     onUpdate : function(ds, record){
26927         // Roo.log('on update');   
26928         this.clearSelections();
26929         var index = this.store.indexOf(record);
26930         var n = this.nodes[index];
26931         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
26932         n.parentNode.removeChild(n);
26933         this.updateIndexes(index, index);
26934     },
26935
26936     
26937     
26938 // --------- FIXME     
26939     onAdd : function(ds, records, index)
26940     {
26941         //Roo.log(['on Add', ds, records, index] );        
26942         this.clearSelections();
26943         if(this.nodes.length == 0){
26944             this.refresh();
26945             return;
26946         }
26947         var n = this.nodes[index];
26948         for(var i = 0, len = records.length; i < len; i++){
26949             var d = this.prepareData(records[i].data, i, records[i]);
26950             if(n){
26951                 this.tpl.insertBefore(n, d);
26952             }else{
26953                 
26954                 this.tpl.append(this.el, d);
26955             }
26956         }
26957         this.updateIndexes(index);
26958     },
26959
26960     onRemove : function(ds, record, index){
26961        // Roo.log('onRemove');
26962         this.clearSelections();
26963         var el = this.dataName  ?
26964             this.el.child('.roo-tpl-' + this.dataName) :
26965             this.el; 
26966         
26967         el.dom.removeChild(this.nodes[index]);
26968         this.updateIndexes(index);
26969     },
26970
26971     /**
26972      * Refresh an individual node.
26973      * @param {Number} index
26974      */
26975     refreshNode : function(index){
26976         this.onUpdate(this.store, this.store.getAt(index));
26977     },
26978
26979     updateIndexes : function(startIndex, endIndex){
26980         var ns = this.nodes;
26981         startIndex = startIndex || 0;
26982         endIndex = endIndex || ns.length - 1;
26983         for(var i = startIndex; i <= endIndex; i++){
26984             ns[i].nodeIndex = i;
26985         }
26986     },
26987
26988     /**
26989      * Changes the data store this view uses and refresh the view.
26990      * @param {Store} store
26991      */
26992     setStore : function(store, initial){
26993         if(!initial && this.store){
26994             this.store.un("datachanged", this.refresh);
26995             this.store.un("add", this.onAdd);
26996             this.store.un("remove", this.onRemove);
26997             this.store.un("update", this.onUpdate);
26998             this.store.un("clear", this.refresh);
26999             this.store.un("beforeload", this.onBeforeLoad);
27000             this.store.un("load", this.onLoad);
27001             this.store.un("loadexception", this.onLoad);
27002         }
27003         if(store){
27004           
27005             store.on("datachanged", this.refresh, this);
27006             store.on("add", this.onAdd, this);
27007             store.on("remove", this.onRemove, this);
27008             store.on("update", this.onUpdate, this);
27009             store.on("clear", this.refresh, this);
27010             store.on("beforeload", this.onBeforeLoad, this);
27011             store.on("load", this.onLoad, this);
27012             store.on("loadexception", this.onLoad, this);
27013         }
27014         
27015         if(store){
27016             this.refresh();
27017         }
27018     },
27019     /**
27020      * onbeforeLoad - masks the loading area.
27021      *
27022      */
27023     onBeforeLoad : function(store,opts)
27024     {
27025          //Roo.log('onBeforeLoad');   
27026         if (!opts.add) {
27027             this.el.update("");
27028         }
27029         this.el.mask(this.mask ? this.mask : "Loading" ); 
27030     },
27031     onLoad : function ()
27032     {
27033         this.el.unmask();
27034     },
27035     
27036
27037     /**
27038      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
27039      * @param {HTMLElement} node
27040      * @return {HTMLElement} The template node
27041      */
27042     findItemFromChild : function(node){
27043         var el = this.dataName  ?
27044             this.el.child('.roo-tpl-' + this.dataName,true) :
27045             this.el.dom; 
27046         
27047         if(!node || node.parentNode == el){
27048                     return node;
27049             }
27050             var p = node.parentNode;
27051             while(p && p != el){
27052             if(p.parentNode == el){
27053                 return p;
27054             }
27055             p = p.parentNode;
27056         }
27057             return null;
27058     },
27059
27060     /** @ignore */
27061     onClick : function(e){
27062         var item = this.findItemFromChild(e.getTarget());
27063         if(item){
27064             var index = this.indexOf(item);
27065             if(this.onItemClick(item, index, e) !== false){
27066                 this.fireEvent("click", this, index, item, e);
27067             }
27068         }else{
27069             this.clearSelections();
27070         }
27071     },
27072
27073     /** @ignore */
27074     onContextMenu : function(e){
27075         var item = this.findItemFromChild(e.getTarget());
27076         if(item){
27077             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
27078         }
27079     },
27080
27081     /** @ignore */
27082     onDblClick : function(e){
27083         var item = this.findItemFromChild(e.getTarget());
27084         if(item){
27085             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
27086         }
27087     },
27088
27089     onItemClick : function(item, index, e)
27090     {
27091         if(this.fireEvent("beforeclick", this, index, item, e) === false){
27092             return false;
27093         }
27094         if (this.toggleSelect) {
27095             var m = this.isSelected(item) ? 'unselect' : 'select';
27096             //Roo.log(m);
27097             var _t = this;
27098             _t[m](item, true, false);
27099             return true;
27100         }
27101         if(this.multiSelect || this.singleSelect){
27102             if(this.multiSelect && e.shiftKey && this.lastSelection){
27103                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
27104             }else{
27105                 this.select(item, this.multiSelect && e.ctrlKey);
27106                 this.lastSelection = item;
27107             }
27108             
27109             if(!this.tickable){
27110                 e.preventDefault();
27111             }
27112             
27113         }
27114         return true;
27115     },
27116
27117     /**
27118      * Get the number of selected nodes.
27119      * @return {Number}
27120      */
27121     getSelectionCount : function(){
27122         return this.selections.length;
27123     },
27124
27125     /**
27126      * Get the currently selected nodes.
27127      * @return {Array} An array of HTMLElements
27128      */
27129     getSelectedNodes : function(){
27130         return this.selections;
27131     },
27132
27133     /**
27134      * Get the indexes of the selected nodes.
27135      * @return {Array}
27136      */
27137     getSelectedIndexes : function(){
27138         var indexes = [], s = this.selections;
27139         for(var i = 0, len = s.length; i < len; i++){
27140             indexes.push(s[i].nodeIndex);
27141         }
27142         return indexes;
27143     },
27144
27145     /**
27146      * Clear all selections
27147      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
27148      */
27149     clearSelections : function(suppressEvent){
27150         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
27151             this.cmp.elements = this.selections;
27152             this.cmp.removeClass(this.selectedClass);
27153             this.selections = [];
27154             if(!suppressEvent){
27155                 this.fireEvent("selectionchange", this, this.selections);
27156             }
27157         }
27158     },
27159
27160     /**
27161      * Returns true if the passed node is selected
27162      * @param {HTMLElement/Number} node The node or node index
27163      * @return {Boolean}
27164      */
27165     isSelected : function(node){
27166         var s = this.selections;
27167         if(s.length < 1){
27168             return false;
27169         }
27170         node = this.getNode(node);
27171         return s.indexOf(node) !== -1;
27172     },
27173
27174     /**
27175      * Selects nodes.
27176      * @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
27177      * @param {Boolean} keepExisting (optional) true to keep existing selections
27178      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
27179      */
27180     select : function(nodeInfo, keepExisting, suppressEvent){
27181         if(nodeInfo instanceof Array){
27182             if(!keepExisting){
27183                 this.clearSelections(true);
27184             }
27185             for(var i = 0, len = nodeInfo.length; i < len; i++){
27186                 this.select(nodeInfo[i], true, true);
27187             }
27188             return;
27189         } 
27190         var node = this.getNode(nodeInfo);
27191         if(!node || this.isSelected(node)){
27192             return; // already selected.
27193         }
27194         if(!keepExisting){
27195             this.clearSelections(true);
27196         }
27197         
27198         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
27199             Roo.fly(node).addClass(this.selectedClass);
27200             this.selections.push(node);
27201             if(!suppressEvent){
27202                 this.fireEvent("selectionchange", this, this.selections);
27203             }
27204         }
27205         
27206         
27207     },
27208       /**
27209      * Unselects nodes.
27210      * @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
27211      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
27212      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
27213      */
27214     unselect : function(nodeInfo, keepExisting, suppressEvent)
27215     {
27216         if(nodeInfo instanceof Array){
27217             Roo.each(this.selections, function(s) {
27218                 this.unselect(s, nodeInfo);
27219             }, this);
27220             return;
27221         }
27222         var node = this.getNode(nodeInfo);
27223         if(!node || !this.isSelected(node)){
27224             //Roo.log("not selected");
27225             return; // not selected.
27226         }
27227         // fireevent???
27228         var ns = [];
27229         Roo.each(this.selections, function(s) {
27230             if (s == node ) {
27231                 Roo.fly(node).removeClass(this.selectedClass);
27232
27233                 return;
27234             }
27235             ns.push(s);
27236         },this);
27237         
27238         this.selections= ns;
27239         this.fireEvent("selectionchange", this, this.selections);
27240     },
27241
27242     /**
27243      * Gets a template node.
27244      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27245      * @return {HTMLElement} The node or null if it wasn't found
27246      */
27247     getNode : function(nodeInfo){
27248         if(typeof nodeInfo == "string"){
27249             return document.getElementById(nodeInfo);
27250         }else if(typeof nodeInfo == "number"){
27251             return this.nodes[nodeInfo];
27252         }
27253         return nodeInfo;
27254     },
27255
27256     /**
27257      * Gets a range template nodes.
27258      * @param {Number} startIndex
27259      * @param {Number} endIndex
27260      * @return {Array} An array of nodes
27261      */
27262     getNodes : function(start, end){
27263         var ns = this.nodes;
27264         start = start || 0;
27265         end = typeof end == "undefined" ? ns.length - 1 : end;
27266         var nodes = [];
27267         if(start <= end){
27268             for(var i = start; i <= end; i++){
27269                 nodes.push(ns[i]);
27270             }
27271         } else{
27272             for(var i = start; i >= end; i--){
27273                 nodes.push(ns[i]);
27274             }
27275         }
27276         return nodes;
27277     },
27278
27279     /**
27280      * Finds the index of the passed node
27281      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27282      * @return {Number} The index of the node or -1
27283      */
27284     indexOf : function(node){
27285         node = this.getNode(node);
27286         if(typeof node.nodeIndex == "number"){
27287             return node.nodeIndex;
27288         }
27289         var ns = this.nodes;
27290         for(var i = 0, len = ns.length; i < len; i++){
27291             if(ns[i] == node){
27292                 return i;
27293             }
27294         }
27295         return -1;
27296     }
27297 });
27298 /*
27299  * Based on:
27300  * Ext JS Library 1.1.1
27301  * Copyright(c) 2006-2007, Ext JS, LLC.
27302  *
27303  * Originally Released Under LGPL - original licence link has changed is not relivant.
27304  *
27305  * Fork - LGPL
27306  * <script type="text/javascript">
27307  */
27308
27309 /**
27310  * @class Roo.JsonView
27311  * @extends Roo.View
27312  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
27313 <pre><code>
27314 var view = new Roo.JsonView({
27315     container: "my-element",
27316     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
27317     multiSelect: true, 
27318     jsonRoot: "data" 
27319 });
27320
27321 // listen for node click?
27322 view.on("click", function(vw, index, node, e){
27323     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27324 });
27325
27326 // direct load of JSON data
27327 view.load("foobar.php");
27328
27329 // Example from my blog list
27330 var tpl = new Roo.Template(
27331     '&lt;div class="entry"&gt;' +
27332     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
27333     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
27334     "&lt;/div&gt;&lt;hr /&gt;"
27335 );
27336
27337 var moreView = new Roo.JsonView({
27338     container :  "entry-list", 
27339     template : tpl,
27340     jsonRoot: "posts"
27341 });
27342 moreView.on("beforerender", this.sortEntries, this);
27343 moreView.load({
27344     url: "/blog/get-posts.php",
27345     params: "allposts=true",
27346     text: "Loading Blog Entries..."
27347 });
27348 </code></pre>
27349
27350 * Note: old code is supported with arguments : (container, template, config)
27351
27352
27353  * @constructor
27354  * Create a new JsonView
27355  * 
27356  * @param {Object} config The config object
27357  * 
27358  */
27359 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
27360     
27361     
27362     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
27363
27364     var um = this.el.getUpdateManager();
27365     um.setRenderer(this);
27366     um.on("update", this.onLoad, this);
27367     um.on("failure", this.onLoadException, this);
27368
27369     /**
27370      * @event beforerender
27371      * Fires before rendering of the downloaded JSON data.
27372      * @param {Roo.JsonView} this
27373      * @param {Object} data The JSON data loaded
27374      */
27375     /**
27376      * @event load
27377      * Fires when data is loaded.
27378      * @param {Roo.JsonView} this
27379      * @param {Object} data The JSON data loaded
27380      * @param {Object} response The raw Connect response object
27381      */
27382     /**
27383      * @event loadexception
27384      * Fires when loading fails.
27385      * @param {Roo.JsonView} this
27386      * @param {Object} response The raw Connect response object
27387      */
27388     this.addEvents({
27389         'beforerender' : true,
27390         'load' : true,
27391         'loadexception' : true
27392     });
27393 };
27394 Roo.extend(Roo.JsonView, Roo.View, {
27395     /**
27396      * @type {String} The root property in the loaded JSON object that contains the data
27397      */
27398     jsonRoot : "",
27399
27400     /**
27401      * Refreshes the view.
27402      */
27403     refresh : function(){
27404         this.clearSelections();
27405         this.el.update("");
27406         var html = [];
27407         var o = this.jsonData;
27408         if(o && o.length > 0){
27409             for(var i = 0, len = o.length; i < len; i++){
27410                 var data = this.prepareData(o[i], i, o);
27411                 html[html.length] = this.tpl.apply(data);
27412             }
27413         }else{
27414             html.push(this.emptyText);
27415         }
27416         this.el.update(html.join(""));
27417         this.nodes = this.el.dom.childNodes;
27418         this.updateIndexes(0);
27419     },
27420
27421     /**
27422      * 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.
27423      * @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:
27424      <pre><code>
27425      view.load({
27426          url: "your-url.php",
27427          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
27428          callback: yourFunction,
27429          scope: yourObject, //(optional scope)
27430          discardUrl: false,
27431          nocache: false,
27432          text: "Loading...",
27433          timeout: 30,
27434          scripts: false
27435      });
27436      </code></pre>
27437      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
27438      * 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.
27439      * @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}
27440      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
27441      * @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.
27442      */
27443     load : function(){
27444         var um = this.el.getUpdateManager();
27445         um.update.apply(um, arguments);
27446     },
27447
27448     // note - render is a standard framework call...
27449     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
27450     render : function(el, response){
27451         
27452         this.clearSelections();
27453         this.el.update("");
27454         var o;
27455         try{
27456             if (response != '') {
27457                 o = Roo.util.JSON.decode(response.responseText);
27458                 if(this.jsonRoot){
27459                     
27460                     o = o[this.jsonRoot];
27461                 }
27462             }
27463         } catch(e){
27464         }
27465         /**
27466          * The current JSON data or null
27467          */
27468         this.jsonData = o;
27469         this.beforeRender();
27470         this.refresh();
27471     },
27472
27473 /**
27474  * Get the number of records in the current JSON dataset
27475  * @return {Number}
27476  */
27477     getCount : function(){
27478         return this.jsonData ? this.jsonData.length : 0;
27479     },
27480
27481 /**
27482  * Returns the JSON object for the specified node(s)
27483  * @param {HTMLElement/Array} node The node or an array of nodes
27484  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
27485  * you get the JSON object for the node
27486  */
27487     getNodeData : function(node){
27488         if(node instanceof Array){
27489             var data = [];
27490             for(var i = 0, len = node.length; i < len; i++){
27491                 data.push(this.getNodeData(node[i]));
27492             }
27493             return data;
27494         }
27495         return this.jsonData[this.indexOf(node)] || null;
27496     },
27497
27498     beforeRender : function(){
27499         this.snapshot = this.jsonData;
27500         if(this.sortInfo){
27501             this.sort.apply(this, this.sortInfo);
27502         }
27503         this.fireEvent("beforerender", this, this.jsonData);
27504     },
27505
27506     onLoad : function(el, o){
27507         this.fireEvent("load", this, this.jsonData, o);
27508     },
27509
27510     onLoadException : function(el, o){
27511         this.fireEvent("loadexception", this, o);
27512     },
27513
27514 /**
27515  * Filter the data by a specific property.
27516  * @param {String} property A property on your JSON objects
27517  * @param {String/RegExp} value Either string that the property values
27518  * should start with, or a RegExp to test against the property
27519  */
27520     filter : function(property, value){
27521         if(this.jsonData){
27522             var data = [];
27523             var ss = this.snapshot;
27524             if(typeof value == "string"){
27525                 var vlen = value.length;
27526                 if(vlen == 0){
27527                     this.clearFilter();
27528                     return;
27529                 }
27530                 value = value.toLowerCase();
27531                 for(var i = 0, len = ss.length; i < len; i++){
27532                     var o = ss[i];
27533                     if(o[property].substr(0, vlen).toLowerCase() == value){
27534                         data.push(o);
27535                     }
27536                 }
27537             } else if(value.exec){ // regex?
27538                 for(var i = 0, len = ss.length; i < len; i++){
27539                     var o = ss[i];
27540                     if(value.test(o[property])){
27541                         data.push(o);
27542                     }
27543                 }
27544             } else{
27545                 return;
27546             }
27547             this.jsonData = data;
27548             this.refresh();
27549         }
27550     },
27551
27552 /**
27553  * Filter by a function. The passed function will be called with each
27554  * object in the current dataset. If the function returns true the value is kept,
27555  * otherwise it is filtered.
27556  * @param {Function} fn
27557  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
27558  */
27559     filterBy : function(fn, scope){
27560         if(this.jsonData){
27561             var data = [];
27562             var ss = this.snapshot;
27563             for(var i = 0, len = ss.length; i < len; i++){
27564                 var o = ss[i];
27565                 if(fn.call(scope || this, o)){
27566                     data.push(o);
27567                 }
27568             }
27569             this.jsonData = data;
27570             this.refresh();
27571         }
27572     },
27573
27574 /**
27575  * Clears the current filter.
27576  */
27577     clearFilter : function(){
27578         if(this.snapshot && this.jsonData != this.snapshot){
27579             this.jsonData = this.snapshot;
27580             this.refresh();
27581         }
27582     },
27583
27584
27585 /**
27586  * Sorts the data for this view and refreshes it.
27587  * @param {String} property A property on your JSON objects to sort on
27588  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
27589  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
27590  */
27591     sort : function(property, dir, sortType){
27592         this.sortInfo = Array.prototype.slice.call(arguments, 0);
27593         if(this.jsonData){
27594             var p = property;
27595             var dsc = dir && dir.toLowerCase() == "desc";
27596             var f = function(o1, o2){
27597                 var v1 = sortType ? sortType(o1[p]) : o1[p];
27598                 var v2 = sortType ? sortType(o2[p]) : o2[p];
27599                 ;
27600                 if(v1 < v2){
27601                     return dsc ? +1 : -1;
27602                 } else if(v1 > v2){
27603                     return dsc ? -1 : +1;
27604                 } else{
27605                     return 0;
27606                 }
27607             };
27608             this.jsonData.sort(f);
27609             this.refresh();
27610             if(this.jsonData != this.snapshot){
27611                 this.snapshot.sort(f);
27612             }
27613         }
27614     }
27615 });/*
27616  * Based on:
27617  * Ext JS Library 1.1.1
27618  * Copyright(c) 2006-2007, Ext JS, LLC.
27619  *
27620  * Originally Released Under LGPL - original licence link has changed is not relivant.
27621  *
27622  * Fork - LGPL
27623  * <script type="text/javascript">
27624  */
27625  
27626
27627 /**
27628  * @class Roo.ColorPalette
27629  * @extends Roo.Component
27630  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
27631  * Here's an example of typical usage:
27632  * <pre><code>
27633 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
27634 cp.render('my-div');
27635
27636 cp.on('select', function(palette, selColor){
27637     // do something with selColor
27638 });
27639 </code></pre>
27640  * @constructor
27641  * Create a new ColorPalette
27642  * @param {Object} config The config object
27643  */
27644 Roo.ColorPalette = function(config){
27645     Roo.ColorPalette.superclass.constructor.call(this, config);
27646     this.addEvents({
27647         /**
27648              * @event select
27649              * Fires when a color is selected
27650              * @param {ColorPalette} this
27651              * @param {String} color The 6-digit color hex code (without the # symbol)
27652              */
27653         select: true
27654     });
27655
27656     if(this.handler){
27657         this.on("select", this.handler, this.scope, true);
27658     }
27659 };
27660 Roo.extend(Roo.ColorPalette, Roo.Component, {
27661     /**
27662      * @cfg {String} itemCls
27663      * The CSS class to apply to the containing element (defaults to "x-color-palette")
27664      */
27665     itemCls : "x-color-palette",
27666     /**
27667      * @cfg {String} value
27668      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
27669      * the hex codes are case-sensitive.
27670      */
27671     value : null,
27672     clickEvent:'click',
27673     // private
27674     ctype: "Roo.ColorPalette",
27675
27676     /**
27677      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
27678      */
27679     allowReselect : false,
27680
27681     /**
27682      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
27683      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
27684      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
27685      * of colors with the width setting until the box is symmetrical.</p>
27686      * <p>You can override individual colors if needed:</p>
27687      * <pre><code>
27688 var cp = new Roo.ColorPalette();
27689 cp.colors[0] = "FF0000";  // change the first box to red
27690 </code></pre>
27691
27692 Or you can provide a custom array of your own for complete control:
27693 <pre><code>
27694 var cp = new Roo.ColorPalette();
27695 cp.colors = ["000000", "993300", "333300"];
27696 </code></pre>
27697      * @type Array
27698      */
27699     colors : [
27700         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
27701         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
27702         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
27703         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
27704         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
27705     ],
27706
27707     // private
27708     onRender : function(container, position){
27709         var t = new Roo.MasterTemplate(
27710             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
27711         );
27712         var c = this.colors;
27713         for(var i = 0, len = c.length; i < len; i++){
27714             t.add([c[i]]);
27715         }
27716         var el = document.createElement("div");
27717         el.className = this.itemCls;
27718         t.overwrite(el);
27719         container.dom.insertBefore(el, position);
27720         this.el = Roo.get(el);
27721         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
27722         if(this.clickEvent != 'click'){
27723             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
27724         }
27725     },
27726
27727     // private
27728     afterRender : function(){
27729         Roo.ColorPalette.superclass.afterRender.call(this);
27730         if(this.value){
27731             var s = this.value;
27732             this.value = null;
27733             this.select(s);
27734         }
27735     },
27736
27737     // private
27738     handleClick : function(e, t){
27739         e.preventDefault();
27740         if(!this.disabled){
27741             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
27742             this.select(c.toUpperCase());
27743         }
27744     },
27745
27746     /**
27747      * Selects the specified color in the palette (fires the select event)
27748      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
27749      */
27750     select : function(color){
27751         color = color.replace("#", "");
27752         if(color != this.value || this.allowReselect){
27753             var el = this.el;
27754             if(this.value){
27755                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
27756             }
27757             el.child("a.color-"+color).addClass("x-color-palette-sel");
27758             this.value = color;
27759             this.fireEvent("select", this, color);
27760         }
27761     }
27762 });/*
27763  * Based on:
27764  * Ext JS Library 1.1.1
27765  * Copyright(c) 2006-2007, Ext JS, LLC.
27766  *
27767  * Originally Released Under LGPL - original licence link has changed is not relivant.
27768  *
27769  * Fork - LGPL
27770  * <script type="text/javascript">
27771  */
27772  
27773 /**
27774  * @class Roo.DatePicker
27775  * @extends Roo.Component
27776  * Simple date picker class.
27777  * @constructor
27778  * Create a new DatePicker
27779  * @param {Object} config The config object
27780  */
27781 Roo.DatePicker = function(config){
27782     Roo.DatePicker.superclass.constructor.call(this, config);
27783
27784     this.value = config && config.value ?
27785                  config.value.clearTime() : new Date().clearTime();
27786
27787     this.addEvents({
27788         /**
27789              * @event select
27790              * Fires when a date is selected
27791              * @param {DatePicker} this
27792              * @param {Date} date The selected date
27793              */
27794         'select': true,
27795         /**
27796              * @event monthchange
27797              * Fires when the displayed month changes 
27798              * @param {DatePicker} this
27799              * @param {Date} date The selected month
27800              */
27801         'monthchange': true
27802     });
27803
27804     if(this.handler){
27805         this.on("select", this.handler,  this.scope || this);
27806     }
27807     // build the disabledDatesRE
27808     if(!this.disabledDatesRE && this.disabledDates){
27809         var dd = this.disabledDates;
27810         var re = "(?:";
27811         for(var i = 0; i < dd.length; i++){
27812             re += dd[i];
27813             if(i != dd.length-1) {
27814                 re += "|";
27815             }
27816         }
27817         this.disabledDatesRE = new RegExp(re + ")");
27818     }
27819 };
27820
27821 Roo.extend(Roo.DatePicker, Roo.Component, {
27822     /**
27823      * @cfg {String} todayText
27824      * The text to display on the button that selects the current date (defaults to "Today")
27825      */
27826     todayText : "Today",
27827     /**
27828      * @cfg {String} okText
27829      * The text to display on the ok button
27830      */
27831     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
27832     /**
27833      * @cfg {String} cancelText
27834      * The text to display on the cancel button
27835      */
27836     cancelText : "Cancel",
27837     /**
27838      * @cfg {String} todayTip
27839      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
27840      */
27841     todayTip : "{0} (Spacebar)",
27842     /**
27843      * @cfg {Date} minDate
27844      * Minimum allowable date (JavaScript date object, defaults to null)
27845      */
27846     minDate : null,
27847     /**
27848      * @cfg {Date} maxDate
27849      * Maximum allowable date (JavaScript date object, defaults to null)
27850      */
27851     maxDate : null,
27852     /**
27853      * @cfg {String} minText
27854      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
27855      */
27856     minText : "This date is before the minimum date",
27857     /**
27858      * @cfg {String} maxText
27859      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
27860      */
27861     maxText : "This date is after the maximum date",
27862     /**
27863      * @cfg {String} format
27864      * The default date format string which can be overriden for localization support.  The format must be
27865      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
27866      */
27867     format : "m/d/y",
27868     /**
27869      * @cfg {Array} disabledDays
27870      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
27871      */
27872     disabledDays : null,
27873     /**
27874      * @cfg {String} disabledDaysText
27875      * The tooltip to display when the date falls on a disabled day (defaults to "")
27876      */
27877     disabledDaysText : "",
27878     /**
27879      * @cfg {RegExp} disabledDatesRE
27880      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
27881      */
27882     disabledDatesRE : null,
27883     /**
27884      * @cfg {String} disabledDatesText
27885      * The tooltip text to display when the date falls on a disabled date (defaults to "")
27886      */
27887     disabledDatesText : "",
27888     /**
27889      * @cfg {Boolean} constrainToViewport
27890      * True to constrain the date picker to the viewport (defaults to true)
27891      */
27892     constrainToViewport : true,
27893     /**
27894      * @cfg {Array} monthNames
27895      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
27896      */
27897     monthNames : Date.monthNames,
27898     /**
27899      * @cfg {Array} dayNames
27900      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
27901      */
27902     dayNames : Date.dayNames,
27903     /**
27904      * @cfg {String} nextText
27905      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
27906      */
27907     nextText: 'Next Month (Control+Right)',
27908     /**
27909      * @cfg {String} prevText
27910      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
27911      */
27912     prevText: 'Previous Month (Control+Left)',
27913     /**
27914      * @cfg {String} monthYearText
27915      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
27916      */
27917     monthYearText: 'Choose a month (Control+Up/Down to move years)',
27918     /**
27919      * @cfg {Number} startDay
27920      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
27921      */
27922     startDay : 0,
27923     /**
27924      * @cfg {Bool} showClear
27925      * Show a clear button (usefull for date form elements that can be blank.)
27926      */
27927     
27928     showClear: false,
27929     
27930     /**
27931      * Sets the value of the date field
27932      * @param {Date} value The date to set
27933      */
27934     setValue : function(value){
27935         var old = this.value;
27936         
27937         if (typeof(value) == 'string') {
27938          
27939             value = Date.parseDate(value, this.format);
27940         }
27941         if (!value) {
27942             value = new Date();
27943         }
27944         
27945         this.value = value.clearTime(true);
27946         if(this.el){
27947             this.update(this.value);
27948         }
27949     },
27950
27951     /**
27952      * Gets the current selected value of the date field
27953      * @return {Date} The selected date
27954      */
27955     getValue : function(){
27956         return this.value;
27957     },
27958
27959     // private
27960     focus : function(){
27961         if(this.el){
27962             this.update(this.activeDate);
27963         }
27964     },
27965
27966     // privateval
27967     onRender : function(container, position){
27968         
27969         var m = [
27970              '<table cellspacing="0">',
27971                 '<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>',
27972                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
27973         var dn = this.dayNames;
27974         for(var i = 0; i < 7; i++){
27975             var d = this.startDay+i;
27976             if(d > 6){
27977                 d = d-7;
27978             }
27979             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
27980         }
27981         m[m.length] = "</tr></thead><tbody><tr>";
27982         for(var i = 0; i < 42; i++) {
27983             if(i % 7 == 0 && i != 0){
27984                 m[m.length] = "</tr><tr>";
27985             }
27986             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
27987         }
27988         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
27989             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
27990
27991         var el = document.createElement("div");
27992         el.className = "x-date-picker";
27993         el.innerHTML = m.join("");
27994
27995         container.dom.insertBefore(el, position);
27996
27997         this.el = Roo.get(el);
27998         this.eventEl = Roo.get(el.firstChild);
27999
28000         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
28001             handler: this.showPrevMonth,
28002             scope: this,
28003             preventDefault:true,
28004             stopDefault:true
28005         });
28006
28007         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
28008             handler: this.showNextMonth,
28009             scope: this,
28010             preventDefault:true,
28011             stopDefault:true
28012         });
28013
28014         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
28015
28016         this.monthPicker = this.el.down('div.x-date-mp');
28017         this.monthPicker.enableDisplayMode('block');
28018         
28019         var kn = new Roo.KeyNav(this.eventEl, {
28020             "left" : function(e){
28021                 e.ctrlKey ?
28022                     this.showPrevMonth() :
28023                     this.update(this.activeDate.add("d", -1));
28024             },
28025
28026             "right" : function(e){
28027                 e.ctrlKey ?
28028                     this.showNextMonth() :
28029                     this.update(this.activeDate.add("d", 1));
28030             },
28031
28032             "up" : function(e){
28033                 e.ctrlKey ?
28034                     this.showNextYear() :
28035                     this.update(this.activeDate.add("d", -7));
28036             },
28037
28038             "down" : function(e){
28039                 e.ctrlKey ?
28040                     this.showPrevYear() :
28041                     this.update(this.activeDate.add("d", 7));
28042             },
28043
28044             "pageUp" : function(e){
28045                 this.showNextMonth();
28046             },
28047
28048             "pageDown" : function(e){
28049                 this.showPrevMonth();
28050             },
28051
28052             "enter" : function(e){
28053                 e.stopPropagation();
28054                 return true;
28055             },
28056
28057             scope : this
28058         });
28059
28060         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
28061
28062         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
28063
28064         this.el.unselectable();
28065         
28066         this.cells = this.el.select("table.x-date-inner tbody td");
28067         this.textNodes = this.el.query("table.x-date-inner tbody span");
28068
28069         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
28070             text: "&#160;",
28071             tooltip: this.monthYearText
28072         });
28073
28074         this.mbtn.on('click', this.showMonthPicker, this);
28075         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
28076
28077
28078         var today = (new Date()).dateFormat(this.format);
28079         
28080         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
28081         if (this.showClear) {
28082             baseTb.add( new Roo.Toolbar.Fill());
28083         }
28084         baseTb.add({
28085             text: String.format(this.todayText, today),
28086             tooltip: String.format(this.todayTip, today),
28087             handler: this.selectToday,
28088             scope: this
28089         });
28090         
28091         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
28092             
28093         //});
28094         if (this.showClear) {
28095             
28096             baseTb.add( new Roo.Toolbar.Fill());
28097             baseTb.add({
28098                 text: '&#160;',
28099                 cls: 'x-btn-icon x-btn-clear',
28100                 handler: function() {
28101                     //this.value = '';
28102                     this.fireEvent("select", this, '');
28103                 },
28104                 scope: this
28105             });
28106         }
28107         
28108         
28109         if(Roo.isIE){
28110             this.el.repaint();
28111         }
28112         this.update(this.value);
28113     },
28114
28115     createMonthPicker : function(){
28116         if(!this.monthPicker.dom.firstChild){
28117             var buf = ['<table border="0" cellspacing="0">'];
28118             for(var i = 0; i < 6; i++){
28119                 buf.push(
28120                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
28121                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
28122                     i == 0 ?
28123                     '<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>' :
28124                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
28125                 );
28126             }
28127             buf.push(
28128                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
28129                     this.okText,
28130                     '</button><button type="button" class="x-date-mp-cancel">',
28131                     this.cancelText,
28132                     '</button></td></tr>',
28133                 '</table>'
28134             );
28135             this.monthPicker.update(buf.join(''));
28136             this.monthPicker.on('click', this.onMonthClick, this);
28137             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
28138
28139             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
28140             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
28141
28142             this.mpMonths.each(function(m, a, i){
28143                 i += 1;
28144                 if((i%2) == 0){
28145                     m.dom.xmonth = 5 + Math.round(i * .5);
28146                 }else{
28147                     m.dom.xmonth = Math.round((i-1) * .5);
28148                 }
28149             });
28150         }
28151     },
28152
28153     showMonthPicker : function(){
28154         this.createMonthPicker();
28155         var size = this.el.getSize();
28156         this.monthPicker.setSize(size);
28157         this.monthPicker.child('table').setSize(size);
28158
28159         this.mpSelMonth = (this.activeDate || this.value).getMonth();
28160         this.updateMPMonth(this.mpSelMonth);
28161         this.mpSelYear = (this.activeDate || this.value).getFullYear();
28162         this.updateMPYear(this.mpSelYear);
28163
28164         this.monthPicker.slideIn('t', {duration:.2});
28165     },
28166
28167     updateMPYear : function(y){
28168         this.mpyear = y;
28169         var ys = this.mpYears.elements;
28170         for(var i = 1; i <= 10; i++){
28171             var td = ys[i-1], y2;
28172             if((i%2) == 0){
28173                 y2 = y + Math.round(i * .5);
28174                 td.firstChild.innerHTML = y2;
28175                 td.xyear = y2;
28176             }else{
28177                 y2 = y - (5-Math.round(i * .5));
28178                 td.firstChild.innerHTML = y2;
28179                 td.xyear = y2;
28180             }
28181             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
28182         }
28183     },
28184
28185     updateMPMonth : function(sm){
28186         this.mpMonths.each(function(m, a, i){
28187             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
28188         });
28189     },
28190
28191     selectMPMonth: function(m){
28192         
28193     },
28194
28195     onMonthClick : function(e, t){
28196         e.stopEvent();
28197         var el = new Roo.Element(t), pn;
28198         if(el.is('button.x-date-mp-cancel')){
28199             this.hideMonthPicker();
28200         }
28201         else if(el.is('button.x-date-mp-ok')){
28202             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
28203             this.hideMonthPicker();
28204         }
28205         else if(pn = el.up('td.x-date-mp-month', 2)){
28206             this.mpMonths.removeClass('x-date-mp-sel');
28207             pn.addClass('x-date-mp-sel');
28208             this.mpSelMonth = pn.dom.xmonth;
28209         }
28210         else if(pn = el.up('td.x-date-mp-year', 2)){
28211             this.mpYears.removeClass('x-date-mp-sel');
28212             pn.addClass('x-date-mp-sel');
28213             this.mpSelYear = pn.dom.xyear;
28214         }
28215         else if(el.is('a.x-date-mp-prev')){
28216             this.updateMPYear(this.mpyear-10);
28217         }
28218         else if(el.is('a.x-date-mp-next')){
28219             this.updateMPYear(this.mpyear+10);
28220         }
28221     },
28222
28223     onMonthDblClick : function(e, t){
28224         e.stopEvent();
28225         var el = new Roo.Element(t), pn;
28226         if(pn = el.up('td.x-date-mp-month', 2)){
28227             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
28228             this.hideMonthPicker();
28229         }
28230         else if(pn = el.up('td.x-date-mp-year', 2)){
28231             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
28232             this.hideMonthPicker();
28233         }
28234     },
28235
28236     hideMonthPicker : function(disableAnim){
28237         if(this.monthPicker){
28238             if(disableAnim === true){
28239                 this.monthPicker.hide();
28240             }else{
28241                 this.monthPicker.slideOut('t', {duration:.2});
28242             }
28243         }
28244     },
28245
28246     // private
28247     showPrevMonth : function(e){
28248         this.update(this.activeDate.add("mo", -1));
28249     },
28250
28251     // private
28252     showNextMonth : function(e){
28253         this.update(this.activeDate.add("mo", 1));
28254     },
28255
28256     // private
28257     showPrevYear : function(){
28258         this.update(this.activeDate.add("y", -1));
28259     },
28260
28261     // private
28262     showNextYear : function(){
28263         this.update(this.activeDate.add("y", 1));
28264     },
28265
28266     // private
28267     handleMouseWheel : function(e){
28268         var delta = e.getWheelDelta();
28269         if(delta > 0){
28270             this.showPrevMonth();
28271             e.stopEvent();
28272         } else if(delta < 0){
28273             this.showNextMonth();
28274             e.stopEvent();
28275         }
28276     },
28277
28278     // private
28279     handleDateClick : function(e, t){
28280         e.stopEvent();
28281         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
28282             this.setValue(new Date(t.dateValue));
28283             this.fireEvent("select", this, this.value);
28284         }
28285     },
28286
28287     // private
28288     selectToday : function(){
28289         this.setValue(new Date().clearTime());
28290         this.fireEvent("select", this, this.value);
28291     },
28292
28293     // private
28294     update : function(date)
28295     {
28296         var vd = this.activeDate;
28297         this.activeDate = date;
28298         if(vd && this.el){
28299             var t = date.getTime();
28300             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
28301                 this.cells.removeClass("x-date-selected");
28302                 this.cells.each(function(c){
28303                    if(c.dom.firstChild.dateValue == t){
28304                        c.addClass("x-date-selected");
28305                        setTimeout(function(){
28306                             try{c.dom.firstChild.focus();}catch(e){}
28307                        }, 50);
28308                        return false;
28309                    }
28310                 });
28311                 return;
28312             }
28313         }
28314         
28315         var days = date.getDaysInMonth();
28316         var firstOfMonth = date.getFirstDateOfMonth();
28317         var startingPos = firstOfMonth.getDay()-this.startDay;
28318
28319         if(startingPos <= this.startDay){
28320             startingPos += 7;
28321         }
28322
28323         var pm = date.add("mo", -1);
28324         var prevStart = pm.getDaysInMonth()-startingPos;
28325
28326         var cells = this.cells.elements;
28327         var textEls = this.textNodes;
28328         days += startingPos;
28329
28330         // convert everything to numbers so it's fast
28331         var day = 86400000;
28332         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
28333         var today = new Date().clearTime().getTime();
28334         var sel = date.clearTime().getTime();
28335         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
28336         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
28337         var ddMatch = this.disabledDatesRE;
28338         var ddText = this.disabledDatesText;
28339         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
28340         var ddaysText = this.disabledDaysText;
28341         var format = this.format;
28342
28343         var setCellClass = function(cal, cell){
28344             cell.title = "";
28345             var t = d.getTime();
28346             cell.firstChild.dateValue = t;
28347             if(t == today){
28348                 cell.className += " x-date-today";
28349                 cell.title = cal.todayText;
28350             }
28351             if(t == sel){
28352                 cell.className += " x-date-selected";
28353                 setTimeout(function(){
28354                     try{cell.firstChild.focus();}catch(e){}
28355                 }, 50);
28356             }
28357             // disabling
28358             if(t < min) {
28359                 cell.className = " x-date-disabled";
28360                 cell.title = cal.minText;
28361                 return;
28362             }
28363             if(t > max) {
28364                 cell.className = " x-date-disabled";
28365                 cell.title = cal.maxText;
28366                 return;
28367             }
28368             if(ddays){
28369                 if(ddays.indexOf(d.getDay()) != -1){
28370                     cell.title = ddaysText;
28371                     cell.className = " x-date-disabled";
28372                 }
28373             }
28374             if(ddMatch && format){
28375                 var fvalue = d.dateFormat(format);
28376                 if(ddMatch.test(fvalue)){
28377                     cell.title = ddText.replace("%0", fvalue);
28378                     cell.className = " x-date-disabled";
28379                 }
28380             }
28381         };
28382
28383         var i = 0;
28384         for(; i < startingPos; i++) {
28385             textEls[i].innerHTML = (++prevStart);
28386             d.setDate(d.getDate()+1);
28387             cells[i].className = "x-date-prevday";
28388             setCellClass(this, cells[i]);
28389         }
28390         for(; i < days; i++){
28391             intDay = i - startingPos + 1;
28392             textEls[i].innerHTML = (intDay);
28393             d.setDate(d.getDate()+1);
28394             cells[i].className = "x-date-active";
28395             setCellClass(this, cells[i]);
28396         }
28397         var extraDays = 0;
28398         for(; i < 42; i++) {
28399              textEls[i].innerHTML = (++extraDays);
28400              d.setDate(d.getDate()+1);
28401              cells[i].className = "x-date-nextday";
28402              setCellClass(this, cells[i]);
28403         }
28404
28405         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
28406         this.fireEvent('monthchange', this, date);
28407         
28408         if(!this.internalRender){
28409             var main = this.el.dom.firstChild;
28410             var w = main.offsetWidth;
28411             this.el.setWidth(w + this.el.getBorderWidth("lr"));
28412             Roo.fly(main).setWidth(w);
28413             this.internalRender = true;
28414             // opera does not respect the auto grow header center column
28415             // then, after it gets a width opera refuses to recalculate
28416             // without a second pass
28417             if(Roo.isOpera && !this.secondPass){
28418                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
28419                 this.secondPass = true;
28420                 this.update.defer(10, this, [date]);
28421             }
28422         }
28423         
28424         
28425     }
28426 });        /*
28427  * Based on:
28428  * Ext JS Library 1.1.1
28429  * Copyright(c) 2006-2007, Ext JS, LLC.
28430  *
28431  * Originally Released Under LGPL - original licence link has changed is not relivant.
28432  *
28433  * Fork - LGPL
28434  * <script type="text/javascript">
28435  */
28436 /**
28437  * @class Roo.TabPanel
28438  * @extends Roo.util.Observable
28439  * A lightweight tab container.
28440  * <br><br>
28441  * Usage:
28442  * <pre><code>
28443 // basic tabs 1, built from existing content
28444 var tabs = new Roo.TabPanel("tabs1");
28445 tabs.addTab("script", "View Script");
28446 tabs.addTab("markup", "View Markup");
28447 tabs.activate("script");
28448
28449 // more advanced tabs, built from javascript
28450 var jtabs = new Roo.TabPanel("jtabs");
28451 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
28452
28453 // set up the UpdateManager
28454 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
28455 var updater = tab2.getUpdateManager();
28456 updater.setDefaultUrl("ajax1.htm");
28457 tab2.on('activate', updater.refresh, updater, true);
28458
28459 // Use setUrl for Ajax loading
28460 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
28461 tab3.setUrl("ajax2.htm", null, true);
28462
28463 // Disabled tab
28464 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
28465 tab4.disable();
28466
28467 jtabs.activate("jtabs-1");
28468  * </code></pre>
28469  * @constructor
28470  * Create a new TabPanel.
28471  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
28472  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
28473  */
28474 Roo.TabPanel = function(container, config){
28475     /**
28476     * The container element for this TabPanel.
28477     * @type Roo.Element
28478     */
28479     this.el = Roo.get(container, true);
28480     if(config){
28481         if(typeof config == "boolean"){
28482             this.tabPosition = config ? "bottom" : "top";
28483         }else{
28484             Roo.apply(this, config);
28485         }
28486     }
28487     if(this.tabPosition == "bottom"){
28488         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28489         this.el.addClass("x-tabs-bottom");
28490     }
28491     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
28492     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
28493     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
28494     if(Roo.isIE){
28495         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
28496     }
28497     if(this.tabPosition != "bottom"){
28498         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
28499          * @type Roo.Element
28500          */
28501         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28502         this.el.addClass("x-tabs-top");
28503     }
28504     this.items = [];
28505
28506     this.bodyEl.setStyle("position", "relative");
28507
28508     this.active = null;
28509     this.activateDelegate = this.activate.createDelegate(this);
28510
28511     this.addEvents({
28512         /**
28513          * @event tabchange
28514          * Fires when the active tab changes
28515          * @param {Roo.TabPanel} this
28516          * @param {Roo.TabPanelItem} activePanel The new active tab
28517          */
28518         "tabchange": true,
28519         /**
28520          * @event beforetabchange
28521          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
28522          * @param {Roo.TabPanel} this
28523          * @param {Object} e Set cancel to true on this object to cancel the tab change
28524          * @param {Roo.TabPanelItem} tab The tab being changed to
28525          */
28526         "beforetabchange" : true
28527     });
28528
28529     Roo.EventManager.onWindowResize(this.onResize, this);
28530     this.cpad = this.el.getPadding("lr");
28531     this.hiddenCount = 0;
28532
28533
28534     // toolbar on the tabbar support...
28535     if (this.toolbar) {
28536         var tcfg = this.toolbar;
28537         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
28538         this.toolbar = new Roo.Toolbar(tcfg);
28539         if (Roo.isSafari) {
28540             var tbl = tcfg.container.child('table', true);
28541             tbl.setAttribute('width', '100%');
28542         }
28543         
28544     }
28545    
28546
28547
28548     Roo.TabPanel.superclass.constructor.call(this);
28549 };
28550
28551 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
28552     /*
28553      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
28554      */
28555     tabPosition : "top",
28556     /*
28557      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
28558      */
28559     currentTabWidth : 0,
28560     /*
28561      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
28562      */
28563     minTabWidth : 40,
28564     /*
28565      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
28566      */
28567     maxTabWidth : 250,
28568     /*
28569      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
28570      */
28571     preferredTabWidth : 175,
28572     /*
28573      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
28574      */
28575     resizeTabs : false,
28576     /*
28577      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
28578      */
28579     monitorResize : true,
28580     /*
28581      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
28582      */
28583     toolbar : false,
28584
28585     /**
28586      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
28587      * @param {String} id The id of the div to use <b>or create</b>
28588      * @param {String} text The text for the tab
28589      * @param {String} content (optional) Content to put in the TabPanelItem body
28590      * @param {Boolean} closable (optional) True to create a close icon on the tab
28591      * @return {Roo.TabPanelItem} The created TabPanelItem
28592      */
28593     addTab : function(id, text, content, closable){
28594         var item = new Roo.TabPanelItem(this, id, text, closable);
28595         this.addTabItem(item);
28596         if(content){
28597             item.setContent(content);
28598         }
28599         return item;
28600     },
28601
28602     /**
28603      * Returns the {@link Roo.TabPanelItem} with the specified id/index
28604      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
28605      * @return {Roo.TabPanelItem}
28606      */
28607     getTab : function(id){
28608         return this.items[id];
28609     },
28610
28611     /**
28612      * Hides the {@link Roo.TabPanelItem} with the specified id/index
28613      * @param {String/Number} id The id or index of the TabPanelItem to hide.
28614      */
28615     hideTab : function(id){
28616         var t = this.items[id];
28617         if(!t.isHidden()){
28618            t.setHidden(true);
28619            this.hiddenCount++;
28620            this.autoSizeTabs();
28621         }
28622     },
28623
28624     /**
28625      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
28626      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
28627      */
28628     unhideTab : function(id){
28629         var t = this.items[id];
28630         if(t.isHidden()){
28631            t.setHidden(false);
28632            this.hiddenCount--;
28633            this.autoSizeTabs();
28634         }
28635     },
28636
28637     /**
28638      * Adds an existing {@link Roo.TabPanelItem}.
28639      * @param {Roo.TabPanelItem} item The TabPanelItem to add
28640      */
28641     addTabItem : function(item){
28642         this.items[item.id] = item;
28643         this.items.push(item);
28644         if(this.resizeTabs){
28645            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
28646            this.autoSizeTabs();
28647         }else{
28648             item.autoSize();
28649         }
28650     },
28651
28652     /**
28653      * Removes a {@link Roo.TabPanelItem}.
28654      * @param {String/Number} id The id or index of the TabPanelItem to remove.
28655      */
28656     removeTab : function(id){
28657         var items = this.items;
28658         var tab = items[id];
28659         if(!tab) { return; }
28660         var index = items.indexOf(tab);
28661         if(this.active == tab && items.length > 1){
28662             var newTab = this.getNextAvailable(index);
28663             if(newTab) {
28664                 newTab.activate();
28665             }
28666         }
28667         this.stripEl.dom.removeChild(tab.pnode.dom);
28668         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
28669             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
28670         }
28671         items.splice(index, 1);
28672         delete this.items[tab.id];
28673         tab.fireEvent("close", tab);
28674         tab.purgeListeners();
28675         this.autoSizeTabs();
28676     },
28677
28678     getNextAvailable : function(start){
28679         var items = this.items;
28680         var index = start;
28681         // look for a next tab that will slide over to
28682         // replace the one being removed
28683         while(index < items.length){
28684             var item = items[++index];
28685             if(item && !item.isHidden()){
28686                 return item;
28687             }
28688         }
28689         // if one isn't found select the previous tab (on the left)
28690         index = start;
28691         while(index >= 0){
28692             var item = items[--index];
28693             if(item && !item.isHidden()){
28694                 return item;
28695             }
28696         }
28697         return null;
28698     },
28699
28700     /**
28701      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
28702      * @param {String/Number} id The id or index of the TabPanelItem to disable.
28703      */
28704     disableTab : function(id){
28705         var tab = this.items[id];
28706         if(tab && this.active != tab){
28707             tab.disable();
28708         }
28709     },
28710
28711     /**
28712      * Enables a {@link Roo.TabPanelItem} that is disabled.
28713      * @param {String/Number} id The id or index of the TabPanelItem to enable.
28714      */
28715     enableTab : function(id){
28716         var tab = this.items[id];
28717         tab.enable();
28718     },
28719
28720     /**
28721      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
28722      * @param {String/Number} id The id or index of the TabPanelItem to activate.
28723      * @return {Roo.TabPanelItem} The TabPanelItem.
28724      */
28725     activate : function(id){
28726         var tab = this.items[id];
28727         if(!tab){
28728             return null;
28729         }
28730         if(tab == this.active || tab.disabled){
28731             return tab;
28732         }
28733         var e = {};
28734         this.fireEvent("beforetabchange", this, e, tab);
28735         if(e.cancel !== true && !tab.disabled){
28736             if(this.active){
28737                 this.active.hide();
28738             }
28739             this.active = this.items[id];
28740             this.active.show();
28741             this.fireEvent("tabchange", this, this.active);
28742         }
28743         return tab;
28744     },
28745
28746     /**
28747      * Gets the active {@link Roo.TabPanelItem}.
28748      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
28749      */
28750     getActiveTab : function(){
28751         return this.active;
28752     },
28753
28754     /**
28755      * Updates the tab body element to fit the height of the container element
28756      * for overflow scrolling
28757      * @param {Number} targetHeight (optional) Override the starting height from the elements height
28758      */
28759     syncHeight : function(targetHeight){
28760         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28761         var bm = this.bodyEl.getMargins();
28762         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
28763         this.bodyEl.setHeight(newHeight);
28764         return newHeight;
28765     },
28766
28767     onResize : function(){
28768         if(this.monitorResize){
28769             this.autoSizeTabs();
28770         }
28771     },
28772
28773     /**
28774      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
28775      */
28776     beginUpdate : function(){
28777         this.updating = true;
28778     },
28779
28780     /**
28781      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
28782      */
28783     endUpdate : function(){
28784         this.updating = false;
28785         this.autoSizeTabs();
28786     },
28787
28788     /**
28789      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
28790      */
28791     autoSizeTabs : function(){
28792         var count = this.items.length;
28793         var vcount = count - this.hiddenCount;
28794         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
28795             return;
28796         }
28797         var w = Math.max(this.el.getWidth() - this.cpad, 10);
28798         var availWidth = Math.floor(w / vcount);
28799         var b = this.stripBody;
28800         if(b.getWidth() > w){
28801             var tabs = this.items;
28802             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
28803             if(availWidth < this.minTabWidth){
28804                 /*if(!this.sleft){    // incomplete scrolling code
28805                     this.createScrollButtons();
28806                 }
28807                 this.showScroll();
28808                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
28809             }
28810         }else{
28811             if(this.currentTabWidth < this.preferredTabWidth){
28812                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
28813             }
28814         }
28815     },
28816
28817     /**
28818      * Returns the number of tabs in this TabPanel.
28819      * @return {Number}
28820      */
28821      getCount : function(){
28822          return this.items.length;
28823      },
28824
28825     /**
28826      * Resizes all the tabs to the passed width
28827      * @param {Number} The new width
28828      */
28829     setTabWidth : function(width){
28830         this.currentTabWidth = width;
28831         for(var i = 0, len = this.items.length; i < len; i++) {
28832                 if(!this.items[i].isHidden()) {
28833                 this.items[i].setWidth(width);
28834             }
28835         }
28836     },
28837
28838     /**
28839      * Destroys this TabPanel
28840      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
28841      */
28842     destroy : function(removeEl){
28843         Roo.EventManager.removeResizeListener(this.onResize, this);
28844         for(var i = 0, len = this.items.length; i < len; i++){
28845             this.items[i].purgeListeners();
28846         }
28847         if(removeEl === true){
28848             this.el.update("");
28849             this.el.remove();
28850         }
28851     }
28852 });
28853
28854 /**
28855  * @class Roo.TabPanelItem
28856  * @extends Roo.util.Observable
28857  * Represents an individual item (tab plus body) in a TabPanel.
28858  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
28859  * @param {String} id The id of this TabPanelItem
28860  * @param {String} text The text for the tab of this TabPanelItem
28861  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
28862  */
28863 Roo.TabPanelItem = function(tabPanel, id, text, closable){
28864     /**
28865      * The {@link Roo.TabPanel} this TabPanelItem belongs to
28866      * @type Roo.TabPanel
28867      */
28868     this.tabPanel = tabPanel;
28869     /**
28870      * The id for this TabPanelItem
28871      * @type String
28872      */
28873     this.id = id;
28874     /** @private */
28875     this.disabled = false;
28876     /** @private */
28877     this.text = text;
28878     /** @private */
28879     this.loaded = false;
28880     this.closable = closable;
28881
28882     /**
28883      * The body element for this TabPanelItem.
28884      * @type Roo.Element
28885      */
28886     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
28887     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
28888     this.bodyEl.setStyle("display", "block");
28889     this.bodyEl.setStyle("zoom", "1");
28890     this.hideAction();
28891
28892     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
28893     /** @private */
28894     this.el = Roo.get(els.el, true);
28895     this.inner = Roo.get(els.inner, true);
28896     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
28897     this.pnode = Roo.get(els.el.parentNode, true);
28898     this.el.on("mousedown", this.onTabMouseDown, this);
28899     this.el.on("click", this.onTabClick, this);
28900     /** @private */
28901     if(closable){
28902         var c = Roo.get(els.close, true);
28903         c.dom.title = this.closeText;
28904         c.addClassOnOver("close-over");
28905         c.on("click", this.closeClick, this);
28906      }
28907
28908     this.addEvents({
28909          /**
28910          * @event activate
28911          * Fires when this tab becomes the active tab.
28912          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28913          * @param {Roo.TabPanelItem} this
28914          */
28915         "activate": true,
28916         /**
28917          * @event beforeclose
28918          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
28919          * @param {Roo.TabPanelItem} this
28920          * @param {Object} e Set cancel to true on this object to cancel the close.
28921          */
28922         "beforeclose": true,
28923         /**
28924          * @event close
28925          * Fires when this tab is closed.
28926          * @param {Roo.TabPanelItem} this
28927          */
28928          "close": true,
28929         /**
28930          * @event deactivate
28931          * Fires when this tab is no longer the active tab.
28932          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28933          * @param {Roo.TabPanelItem} this
28934          */
28935          "deactivate" : true
28936     });
28937     this.hidden = false;
28938
28939     Roo.TabPanelItem.superclass.constructor.call(this);
28940 };
28941
28942 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
28943     purgeListeners : function(){
28944        Roo.util.Observable.prototype.purgeListeners.call(this);
28945        this.el.removeAllListeners();
28946     },
28947     /**
28948      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
28949      */
28950     show : function(){
28951         this.pnode.addClass("on");
28952         this.showAction();
28953         if(Roo.isOpera){
28954             this.tabPanel.stripWrap.repaint();
28955         }
28956         this.fireEvent("activate", this.tabPanel, this);
28957     },
28958
28959     /**
28960      * Returns true if this tab is the active tab.
28961      * @return {Boolean}
28962      */
28963     isActive : function(){
28964         return this.tabPanel.getActiveTab() == this;
28965     },
28966
28967     /**
28968      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
28969      */
28970     hide : function(){
28971         this.pnode.removeClass("on");
28972         this.hideAction();
28973         this.fireEvent("deactivate", this.tabPanel, this);
28974     },
28975
28976     hideAction : function(){
28977         this.bodyEl.hide();
28978         this.bodyEl.setStyle("position", "absolute");
28979         this.bodyEl.setLeft("-20000px");
28980         this.bodyEl.setTop("-20000px");
28981     },
28982
28983     showAction : function(){
28984         this.bodyEl.setStyle("position", "relative");
28985         this.bodyEl.setTop("");
28986         this.bodyEl.setLeft("");
28987         this.bodyEl.show();
28988     },
28989
28990     /**
28991      * Set the tooltip for the tab.
28992      * @param {String} tooltip The tab's tooltip
28993      */
28994     setTooltip : function(text){
28995         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
28996             this.textEl.dom.qtip = text;
28997             this.textEl.dom.removeAttribute('title');
28998         }else{
28999             this.textEl.dom.title = text;
29000         }
29001     },
29002
29003     onTabClick : function(e){
29004         e.preventDefault();
29005         this.tabPanel.activate(this.id);
29006     },
29007
29008     onTabMouseDown : function(e){
29009         e.preventDefault();
29010         this.tabPanel.activate(this.id);
29011     },
29012
29013     getWidth : function(){
29014         return this.inner.getWidth();
29015     },
29016
29017     setWidth : function(width){
29018         var iwidth = width - this.pnode.getPadding("lr");
29019         this.inner.setWidth(iwidth);
29020         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
29021         this.pnode.setWidth(width);
29022     },
29023
29024     /**
29025      * Show or hide the tab
29026      * @param {Boolean} hidden True to hide or false to show.
29027      */
29028     setHidden : function(hidden){
29029         this.hidden = hidden;
29030         this.pnode.setStyle("display", hidden ? "none" : "");
29031     },
29032
29033     /**
29034      * Returns true if this tab is "hidden"
29035      * @return {Boolean}
29036      */
29037     isHidden : function(){
29038         return this.hidden;
29039     },
29040
29041     /**
29042      * Returns the text for this tab
29043      * @return {String}
29044      */
29045     getText : function(){
29046         return this.text;
29047     },
29048
29049     autoSize : function(){
29050         //this.el.beginMeasure();
29051         this.textEl.setWidth(1);
29052         /*
29053          *  #2804 [new] Tabs in Roojs
29054          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
29055          */
29056         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
29057         //this.el.endMeasure();
29058     },
29059
29060     /**
29061      * Sets the text for the tab (Note: this also sets the tooltip text)
29062      * @param {String} text The tab's text and tooltip
29063      */
29064     setText : function(text){
29065         this.text = text;
29066         this.textEl.update(text);
29067         this.setTooltip(text);
29068         if(!this.tabPanel.resizeTabs){
29069             this.autoSize();
29070         }
29071     },
29072     /**
29073      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
29074      */
29075     activate : function(){
29076         this.tabPanel.activate(this.id);
29077     },
29078
29079     /**
29080      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
29081      */
29082     disable : function(){
29083         if(this.tabPanel.active != this){
29084             this.disabled = true;
29085             this.pnode.addClass("disabled");
29086         }
29087     },
29088
29089     /**
29090      * Enables this TabPanelItem if it was previously disabled.
29091      */
29092     enable : function(){
29093         this.disabled = false;
29094         this.pnode.removeClass("disabled");
29095     },
29096
29097     /**
29098      * Sets the content for this TabPanelItem.
29099      * @param {String} content The content
29100      * @param {Boolean} loadScripts true to look for and load scripts
29101      */
29102     setContent : function(content, loadScripts){
29103         this.bodyEl.update(content, loadScripts);
29104     },
29105
29106     /**
29107      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
29108      * @return {Roo.UpdateManager} The UpdateManager
29109      */
29110     getUpdateManager : function(){
29111         return this.bodyEl.getUpdateManager();
29112     },
29113
29114     /**
29115      * Set a URL to be used to load the content for this TabPanelItem.
29116      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
29117      * @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)
29118      * @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)
29119      * @return {Roo.UpdateManager} The UpdateManager
29120      */
29121     setUrl : function(url, params, loadOnce){
29122         if(this.refreshDelegate){
29123             this.un('activate', this.refreshDelegate);
29124         }
29125         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
29126         this.on("activate", this.refreshDelegate);
29127         return this.bodyEl.getUpdateManager();
29128     },
29129
29130     /** @private */
29131     _handleRefresh : function(url, params, loadOnce){
29132         if(!loadOnce || !this.loaded){
29133             var updater = this.bodyEl.getUpdateManager();
29134             updater.update(url, params, this._setLoaded.createDelegate(this));
29135         }
29136     },
29137
29138     /**
29139      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
29140      *   Will fail silently if the setUrl method has not been called.
29141      *   This does not activate the panel, just updates its content.
29142      */
29143     refresh : function(){
29144         if(this.refreshDelegate){
29145            this.loaded = false;
29146            this.refreshDelegate();
29147         }
29148     },
29149
29150     /** @private */
29151     _setLoaded : function(){
29152         this.loaded = true;
29153     },
29154
29155     /** @private */
29156     closeClick : function(e){
29157         var o = {};
29158         e.stopEvent();
29159         this.fireEvent("beforeclose", this, o);
29160         if(o.cancel !== true){
29161             this.tabPanel.removeTab(this.id);
29162         }
29163     },
29164     /**
29165      * The text displayed in the tooltip for the close icon.
29166      * @type String
29167      */
29168     closeText : "Close this tab"
29169 });
29170
29171 /** @private */
29172 Roo.TabPanel.prototype.createStrip = function(container){
29173     var strip = document.createElement("div");
29174     strip.className = "x-tabs-wrap";
29175     container.appendChild(strip);
29176     return strip;
29177 };
29178 /** @private */
29179 Roo.TabPanel.prototype.createStripList = function(strip){
29180     // div wrapper for retard IE
29181     // returns the "tr" element.
29182     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
29183         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
29184         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
29185     return strip.firstChild.firstChild.firstChild.firstChild;
29186 };
29187 /** @private */
29188 Roo.TabPanel.prototype.createBody = function(container){
29189     var body = document.createElement("div");
29190     Roo.id(body, "tab-body");
29191     Roo.fly(body).addClass("x-tabs-body");
29192     container.appendChild(body);
29193     return body;
29194 };
29195 /** @private */
29196 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
29197     var body = Roo.getDom(id);
29198     if(!body){
29199         body = document.createElement("div");
29200         body.id = id;
29201     }
29202     Roo.fly(body).addClass("x-tabs-item-body");
29203     bodyEl.insertBefore(body, bodyEl.firstChild);
29204     return body;
29205 };
29206 /** @private */
29207 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
29208     var td = document.createElement("td");
29209     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
29210     //stripEl.appendChild(td);
29211     if(closable){
29212         td.className = "x-tabs-closable";
29213         if(!this.closeTpl){
29214             this.closeTpl = new Roo.Template(
29215                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
29216                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
29217                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
29218             );
29219         }
29220         var el = this.closeTpl.overwrite(td, {"text": text});
29221         var close = el.getElementsByTagName("div")[0];
29222         var inner = el.getElementsByTagName("em")[0];
29223         return {"el": el, "close": close, "inner": inner};
29224     } else {
29225         if(!this.tabTpl){
29226             this.tabTpl = new Roo.Template(
29227                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
29228                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
29229             );
29230         }
29231         var el = this.tabTpl.overwrite(td, {"text": text});
29232         var inner = el.getElementsByTagName("em")[0];
29233         return {"el": el, "inner": inner};
29234     }
29235 };/*
29236  * Based on:
29237  * Ext JS Library 1.1.1
29238  * Copyright(c) 2006-2007, Ext JS, LLC.
29239  *
29240  * Originally Released Under LGPL - original licence link has changed is not relivant.
29241  *
29242  * Fork - LGPL
29243  * <script type="text/javascript">
29244  */
29245
29246 /**
29247  * @class Roo.Button
29248  * @extends Roo.util.Observable
29249  * Simple Button class
29250  * @cfg {String} text The button text
29251  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
29252  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
29253  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
29254  * @cfg {Object} scope The scope of the handler
29255  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
29256  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
29257  * @cfg {Boolean} hidden True to start hidden (defaults to false)
29258  * @cfg {Boolean} disabled True to start disabled (defaults to false)
29259  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
29260  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
29261    applies if enableToggle = true)
29262  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
29263  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
29264   an {@link Roo.util.ClickRepeater} config object (defaults to false).
29265  * @constructor
29266  * Create a new button
29267  * @param {Object} config The config object
29268  */
29269 Roo.Button = function(renderTo, config)
29270 {
29271     if (!config) {
29272         config = renderTo;
29273         renderTo = config.renderTo || false;
29274     }
29275     
29276     Roo.apply(this, config);
29277     this.addEvents({
29278         /**
29279              * @event click
29280              * Fires when this button is clicked
29281              * @param {Button} this
29282              * @param {EventObject} e The click event
29283              */
29284             "click" : true,
29285         /**
29286              * @event toggle
29287              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
29288              * @param {Button} this
29289              * @param {Boolean} pressed
29290              */
29291             "toggle" : true,
29292         /**
29293              * @event mouseover
29294              * Fires when the mouse hovers over the button
29295              * @param {Button} this
29296              * @param {Event} e The event object
29297              */
29298         'mouseover' : true,
29299         /**
29300              * @event mouseout
29301              * Fires when the mouse exits the button
29302              * @param {Button} this
29303              * @param {Event} e The event object
29304              */
29305         'mouseout': true,
29306          /**
29307              * @event render
29308              * Fires when the button is rendered
29309              * @param {Button} this
29310              */
29311         'render': true
29312     });
29313     if(this.menu){
29314         this.menu = Roo.menu.MenuMgr.get(this.menu);
29315     }
29316     // register listeners first!!  - so render can be captured..
29317     Roo.util.Observable.call(this);
29318     if(renderTo){
29319         this.render(renderTo);
29320     }
29321     
29322   
29323 };
29324
29325 Roo.extend(Roo.Button, Roo.util.Observable, {
29326     /**
29327      * 
29328      */
29329     
29330     /**
29331      * Read-only. True if this button is hidden
29332      * @type Boolean
29333      */
29334     hidden : false,
29335     /**
29336      * Read-only. True if this button is disabled
29337      * @type Boolean
29338      */
29339     disabled : false,
29340     /**
29341      * Read-only. True if this button is pressed (only if enableToggle = true)
29342      * @type Boolean
29343      */
29344     pressed : false,
29345
29346     /**
29347      * @cfg {Number} tabIndex 
29348      * The DOM tabIndex for this button (defaults to undefined)
29349      */
29350     tabIndex : undefined,
29351
29352     /**
29353      * @cfg {Boolean} enableToggle
29354      * True to enable pressed/not pressed toggling (defaults to false)
29355      */
29356     enableToggle: false,
29357     /**
29358      * @cfg {Mixed} menu
29359      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
29360      */
29361     menu : undefined,
29362     /**
29363      * @cfg {String} menuAlign
29364      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
29365      */
29366     menuAlign : "tl-bl?",
29367
29368     /**
29369      * @cfg {String} iconCls
29370      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
29371      */
29372     iconCls : undefined,
29373     /**
29374      * @cfg {String} type
29375      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
29376      */
29377     type : 'button',
29378
29379     // private
29380     menuClassTarget: 'tr',
29381
29382     /**
29383      * @cfg {String} clickEvent
29384      * The type of event to map to the button's event handler (defaults to 'click')
29385      */
29386     clickEvent : 'click',
29387
29388     /**
29389      * @cfg {Boolean} handleMouseEvents
29390      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
29391      */
29392     handleMouseEvents : true,
29393
29394     /**
29395      * @cfg {String} tooltipType
29396      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
29397      */
29398     tooltipType : 'qtip',
29399
29400     /**
29401      * @cfg {String} cls
29402      * A CSS class to apply to the button's main element.
29403      */
29404     
29405     /**
29406      * @cfg {Roo.Template} template (Optional)
29407      * An {@link Roo.Template} with which to create the Button's main element. This Template must
29408      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
29409      * require code modifications if required elements (e.g. a button) aren't present.
29410      */
29411
29412     // private
29413     render : function(renderTo){
29414         var btn;
29415         if(this.hideParent){
29416             this.parentEl = Roo.get(renderTo);
29417         }
29418         if(!this.dhconfig){
29419             if(!this.template){
29420                 if(!Roo.Button.buttonTemplate){
29421                     // hideous table template
29422                     Roo.Button.buttonTemplate = new Roo.Template(
29423                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
29424                         '<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>',
29425                         "</tr></tbody></table>");
29426                 }
29427                 this.template = Roo.Button.buttonTemplate;
29428             }
29429             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
29430             var btnEl = btn.child("button:first");
29431             btnEl.on('focus', this.onFocus, this);
29432             btnEl.on('blur', this.onBlur, this);
29433             if(this.cls){
29434                 btn.addClass(this.cls);
29435             }
29436             if(this.icon){
29437                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
29438             }
29439             if(this.iconCls){
29440                 btnEl.addClass(this.iconCls);
29441                 if(!this.cls){
29442                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29443                 }
29444             }
29445             if(this.tabIndex !== undefined){
29446                 btnEl.dom.tabIndex = this.tabIndex;
29447             }
29448             if(this.tooltip){
29449                 if(typeof this.tooltip == 'object'){
29450                     Roo.QuickTips.tips(Roo.apply({
29451                           target: btnEl.id
29452                     }, this.tooltip));
29453                 } else {
29454                     btnEl.dom[this.tooltipType] = this.tooltip;
29455                 }
29456             }
29457         }else{
29458             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
29459         }
29460         this.el = btn;
29461         if(this.id){
29462             this.el.dom.id = this.el.id = this.id;
29463         }
29464         if(this.menu){
29465             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
29466             this.menu.on("show", this.onMenuShow, this);
29467             this.menu.on("hide", this.onMenuHide, this);
29468         }
29469         btn.addClass("x-btn");
29470         if(Roo.isIE && !Roo.isIE7){
29471             this.autoWidth.defer(1, this);
29472         }else{
29473             this.autoWidth();
29474         }
29475         if(this.handleMouseEvents){
29476             btn.on("mouseover", this.onMouseOver, this);
29477             btn.on("mouseout", this.onMouseOut, this);
29478             btn.on("mousedown", this.onMouseDown, this);
29479         }
29480         btn.on(this.clickEvent, this.onClick, this);
29481         //btn.on("mouseup", this.onMouseUp, this);
29482         if(this.hidden){
29483             this.hide();
29484         }
29485         if(this.disabled){
29486             this.disable();
29487         }
29488         Roo.ButtonToggleMgr.register(this);
29489         if(this.pressed){
29490             this.el.addClass("x-btn-pressed");
29491         }
29492         if(this.repeat){
29493             var repeater = new Roo.util.ClickRepeater(btn,
29494                 typeof this.repeat == "object" ? this.repeat : {}
29495             );
29496             repeater.on("click", this.onClick,  this);
29497         }
29498         
29499         this.fireEvent('render', this);
29500         
29501     },
29502     /**
29503      * Returns the button's underlying element
29504      * @return {Roo.Element} The element
29505      */
29506     getEl : function(){
29507         return this.el;  
29508     },
29509     
29510     /**
29511      * Destroys this Button and removes any listeners.
29512      */
29513     destroy : function(){
29514         Roo.ButtonToggleMgr.unregister(this);
29515         this.el.removeAllListeners();
29516         this.purgeListeners();
29517         this.el.remove();
29518     },
29519
29520     // private
29521     autoWidth : function(){
29522         if(this.el){
29523             this.el.setWidth("auto");
29524             if(Roo.isIE7 && Roo.isStrict){
29525                 var ib = this.el.child('button');
29526                 if(ib && ib.getWidth() > 20){
29527                     ib.clip();
29528                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29529                 }
29530             }
29531             if(this.minWidth){
29532                 if(this.hidden){
29533                     this.el.beginMeasure();
29534                 }
29535                 if(this.el.getWidth() < this.minWidth){
29536                     this.el.setWidth(this.minWidth);
29537                 }
29538                 if(this.hidden){
29539                     this.el.endMeasure();
29540                 }
29541             }
29542         }
29543     },
29544
29545     /**
29546      * Assigns this button's click handler
29547      * @param {Function} handler The function to call when the button is clicked
29548      * @param {Object} scope (optional) Scope for the function passed in
29549      */
29550     setHandler : function(handler, scope){
29551         this.handler = handler;
29552         this.scope = scope;  
29553     },
29554     
29555     /**
29556      * Sets this button's text
29557      * @param {String} text The button text
29558      */
29559     setText : function(text){
29560         this.text = text;
29561         if(this.el){
29562             this.el.child("td.x-btn-center button.x-btn-text").update(text);
29563         }
29564         this.autoWidth();
29565     },
29566     
29567     /**
29568      * Gets the text for this button
29569      * @return {String} The button text
29570      */
29571     getText : function(){
29572         return this.text;  
29573     },
29574     
29575     /**
29576      * Show this button
29577      */
29578     show: function(){
29579         this.hidden = false;
29580         if(this.el){
29581             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
29582         }
29583     },
29584     
29585     /**
29586      * Hide this button
29587      */
29588     hide: function(){
29589         this.hidden = true;
29590         if(this.el){
29591             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
29592         }
29593     },
29594     
29595     /**
29596      * Convenience function for boolean show/hide
29597      * @param {Boolean} visible True to show, false to hide
29598      */
29599     setVisible: function(visible){
29600         if(visible) {
29601             this.show();
29602         }else{
29603             this.hide();
29604         }
29605     },
29606     
29607     /**
29608      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
29609      * @param {Boolean} state (optional) Force a particular state
29610      */
29611     toggle : function(state){
29612         state = state === undefined ? !this.pressed : state;
29613         if(state != this.pressed){
29614             if(state){
29615                 this.el.addClass("x-btn-pressed");
29616                 this.pressed = true;
29617                 this.fireEvent("toggle", this, true);
29618             }else{
29619                 this.el.removeClass("x-btn-pressed");
29620                 this.pressed = false;
29621                 this.fireEvent("toggle", this, false);
29622             }
29623             if(this.toggleHandler){
29624                 this.toggleHandler.call(this.scope || this, this, state);
29625             }
29626         }
29627     },
29628     
29629     /**
29630      * Focus the button
29631      */
29632     focus : function(){
29633         this.el.child('button:first').focus();
29634     },
29635     
29636     /**
29637      * Disable this button
29638      */
29639     disable : function(){
29640         if(this.el){
29641             this.el.addClass("x-btn-disabled");
29642         }
29643         this.disabled = true;
29644     },
29645     
29646     /**
29647      * Enable this button
29648      */
29649     enable : function(){
29650         if(this.el){
29651             this.el.removeClass("x-btn-disabled");
29652         }
29653         this.disabled = false;
29654     },
29655
29656     /**
29657      * Convenience function for boolean enable/disable
29658      * @param {Boolean} enabled True to enable, false to disable
29659      */
29660     setDisabled : function(v){
29661         this[v !== true ? "enable" : "disable"]();
29662     },
29663
29664     // private
29665     onClick : function(e)
29666     {
29667         if(e){
29668             e.preventDefault();
29669         }
29670         if(e.button != 0){
29671             return;
29672         }
29673         if(!this.disabled){
29674             if(this.enableToggle){
29675                 this.toggle();
29676             }
29677             if(this.menu && !this.menu.isVisible()){
29678                 this.menu.show(this.el, this.menuAlign);
29679             }
29680             this.fireEvent("click", this, e);
29681             if(this.handler){
29682                 this.el.removeClass("x-btn-over");
29683                 this.handler.call(this.scope || this, this, e);
29684             }
29685         }
29686     },
29687     // private
29688     onMouseOver : function(e){
29689         if(!this.disabled){
29690             this.el.addClass("x-btn-over");
29691             this.fireEvent('mouseover', this, e);
29692         }
29693     },
29694     // private
29695     onMouseOut : function(e){
29696         if(!e.within(this.el,  true)){
29697             this.el.removeClass("x-btn-over");
29698             this.fireEvent('mouseout', this, e);
29699         }
29700     },
29701     // private
29702     onFocus : function(e){
29703         if(!this.disabled){
29704             this.el.addClass("x-btn-focus");
29705         }
29706     },
29707     // private
29708     onBlur : function(e){
29709         this.el.removeClass("x-btn-focus");
29710     },
29711     // private
29712     onMouseDown : function(e){
29713         if(!this.disabled && e.button == 0){
29714             this.el.addClass("x-btn-click");
29715             Roo.get(document).on('mouseup', this.onMouseUp, this);
29716         }
29717     },
29718     // private
29719     onMouseUp : function(e){
29720         if(e.button == 0){
29721             this.el.removeClass("x-btn-click");
29722             Roo.get(document).un('mouseup', this.onMouseUp, this);
29723         }
29724     },
29725     // private
29726     onMenuShow : function(e){
29727         this.el.addClass("x-btn-menu-active");
29728     },
29729     // private
29730     onMenuHide : function(e){
29731         this.el.removeClass("x-btn-menu-active");
29732     }   
29733 });
29734
29735 // Private utility class used by Button
29736 Roo.ButtonToggleMgr = function(){
29737    var groups = {};
29738    
29739    function toggleGroup(btn, state){
29740        if(state){
29741            var g = groups[btn.toggleGroup];
29742            for(var i = 0, l = g.length; i < l; i++){
29743                if(g[i] != btn){
29744                    g[i].toggle(false);
29745                }
29746            }
29747        }
29748    }
29749    
29750    return {
29751        register : function(btn){
29752            if(!btn.toggleGroup){
29753                return;
29754            }
29755            var g = groups[btn.toggleGroup];
29756            if(!g){
29757                g = groups[btn.toggleGroup] = [];
29758            }
29759            g.push(btn);
29760            btn.on("toggle", toggleGroup);
29761        },
29762        
29763        unregister : function(btn){
29764            if(!btn.toggleGroup){
29765                return;
29766            }
29767            var g = groups[btn.toggleGroup];
29768            if(g){
29769                g.remove(btn);
29770                btn.un("toggle", toggleGroup);
29771            }
29772        }
29773    };
29774 }();/*
29775  * Based on:
29776  * Ext JS Library 1.1.1
29777  * Copyright(c) 2006-2007, Ext JS, LLC.
29778  *
29779  * Originally Released Under LGPL - original licence link has changed is not relivant.
29780  *
29781  * Fork - LGPL
29782  * <script type="text/javascript">
29783  */
29784  
29785 /**
29786  * @class Roo.SplitButton
29787  * @extends Roo.Button
29788  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
29789  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
29790  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
29791  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
29792  * @cfg {String} arrowTooltip The title attribute of the arrow
29793  * @constructor
29794  * Create a new menu button
29795  * @param {String/HTMLElement/Element} renderTo The element to append the button to
29796  * @param {Object} config The config object
29797  */
29798 Roo.SplitButton = function(renderTo, config){
29799     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
29800     /**
29801      * @event arrowclick
29802      * Fires when this button's arrow is clicked
29803      * @param {SplitButton} this
29804      * @param {EventObject} e The click event
29805      */
29806     this.addEvents({"arrowclick":true});
29807 };
29808
29809 Roo.extend(Roo.SplitButton, Roo.Button, {
29810     render : function(renderTo){
29811         // this is one sweet looking template!
29812         var tpl = new Roo.Template(
29813             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
29814             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
29815             '<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>',
29816             "</tbody></table></td><td>",
29817             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
29818             '<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>',
29819             "</tbody></table></td></tr></table>"
29820         );
29821         var btn = tpl.append(renderTo, [this.text, this.type], true);
29822         var btnEl = btn.child("button");
29823         if(this.cls){
29824             btn.addClass(this.cls);
29825         }
29826         if(this.icon){
29827             btnEl.setStyle('background-image', 'url(' +this.icon +')');
29828         }
29829         if(this.iconCls){
29830             btnEl.addClass(this.iconCls);
29831             if(!this.cls){
29832                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29833             }
29834         }
29835         this.el = btn;
29836         if(this.handleMouseEvents){
29837             btn.on("mouseover", this.onMouseOver, this);
29838             btn.on("mouseout", this.onMouseOut, this);
29839             btn.on("mousedown", this.onMouseDown, this);
29840             btn.on("mouseup", this.onMouseUp, this);
29841         }
29842         btn.on(this.clickEvent, this.onClick, this);
29843         if(this.tooltip){
29844             if(typeof this.tooltip == 'object'){
29845                 Roo.QuickTips.tips(Roo.apply({
29846                       target: btnEl.id
29847                 }, this.tooltip));
29848             } else {
29849                 btnEl.dom[this.tooltipType] = this.tooltip;
29850             }
29851         }
29852         if(this.arrowTooltip){
29853             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
29854         }
29855         if(this.hidden){
29856             this.hide();
29857         }
29858         if(this.disabled){
29859             this.disable();
29860         }
29861         if(this.pressed){
29862             this.el.addClass("x-btn-pressed");
29863         }
29864         if(Roo.isIE && !Roo.isIE7){
29865             this.autoWidth.defer(1, this);
29866         }else{
29867             this.autoWidth();
29868         }
29869         if(this.menu){
29870             this.menu.on("show", this.onMenuShow, this);
29871             this.menu.on("hide", this.onMenuHide, this);
29872         }
29873         this.fireEvent('render', this);
29874     },
29875
29876     // private
29877     autoWidth : function(){
29878         if(this.el){
29879             var tbl = this.el.child("table:first");
29880             var tbl2 = this.el.child("table:last");
29881             this.el.setWidth("auto");
29882             tbl.setWidth("auto");
29883             if(Roo.isIE7 && Roo.isStrict){
29884                 var ib = this.el.child('button:first');
29885                 if(ib && ib.getWidth() > 20){
29886                     ib.clip();
29887                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29888                 }
29889             }
29890             if(this.minWidth){
29891                 if(this.hidden){
29892                     this.el.beginMeasure();
29893                 }
29894                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
29895                     tbl.setWidth(this.minWidth-tbl2.getWidth());
29896                 }
29897                 if(this.hidden){
29898                     this.el.endMeasure();
29899                 }
29900             }
29901             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
29902         } 
29903     },
29904     /**
29905      * Sets this button's click handler
29906      * @param {Function} handler The function to call when the button is clicked
29907      * @param {Object} scope (optional) Scope for the function passed above
29908      */
29909     setHandler : function(handler, scope){
29910         this.handler = handler;
29911         this.scope = scope;  
29912     },
29913     
29914     /**
29915      * Sets this button's arrow click handler
29916      * @param {Function} handler The function to call when the arrow is clicked
29917      * @param {Object} scope (optional) Scope for the function passed above
29918      */
29919     setArrowHandler : function(handler, scope){
29920         this.arrowHandler = handler;
29921         this.scope = scope;  
29922     },
29923     
29924     /**
29925      * Focus the button
29926      */
29927     focus : function(){
29928         if(this.el){
29929             this.el.child("button:first").focus();
29930         }
29931     },
29932
29933     // private
29934     onClick : function(e){
29935         e.preventDefault();
29936         if(!this.disabled){
29937             if(e.getTarget(".x-btn-menu-arrow-wrap")){
29938                 if(this.menu && !this.menu.isVisible()){
29939                     this.menu.show(this.el, this.menuAlign);
29940                 }
29941                 this.fireEvent("arrowclick", this, e);
29942                 if(this.arrowHandler){
29943                     this.arrowHandler.call(this.scope || this, this, e);
29944                 }
29945             }else{
29946                 this.fireEvent("click", this, e);
29947                 if(this.handler){
29948                     this.handler.call(this.scope || this, this, e);
29949                 }
29950             }
29951         }
29952     },
29953     // private
29954     onMouseDown : function(e){
29955         if(!this.disabled){
29956             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
29957         }
29958     },
29959     // private
29960     onMouseUp : function(e){
29961         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
29962     }   
29963 });
29964
29965
29966 // backwards compat
29967 Roo.MenuButton = Roo.SplitButton;/*
29968  * Based on:
29969  * Ext JS Library 1.1.1
29970  * Copyright(c) 2006-2007, Ext JS, LLC.
29971  *
29972  * Originally Released Under LGPL - original licence link has changed is not relivant.
29973  *
29974  * Fork - LGPL
29975  * <script type="text/javascript">
29976  */
29977
29978 /**
29979  * @class Roo.Toolbar
29980  * Basic Toolbar class.
29981  * @constructor
29982  * Creates a new Toolbar
29983  * @param {Object} container The config object
29984  */ 
29985 Roo.Toolbar = function(container, buttons, config)
29986 {
29987     /// old consturctor format still supported..
29988     if(container instanceof Array){ // omit the container for later rendering
29989         buttons = container;
29990         config = buttons;
29991         container = null;
29992     }
29993     if (typeof(container) == 'object' && container.xtype) {
29994         config = container;
29995         container = config.container;
29996         buttons = config.buttons || []; // not really - use items!!
29997     }
29998     var xitems = [];
29999     if (config && config.items) {
30000         xitems = config.items;
30001         delete config.items;
30002     }
30003     Roo.apply(this, config);
30004     this.buttons = buttons;
30005     
30006     if(container){
30007         this.render(container);
30008     }
30009     this.xitems = xitems;
30010     Roo.each(xitems, function(b) {
30011         this.add(b);
30012     }, this);
30013     
30014 };
30015
30016 Roo.Toolbar.prototype = {
30017     /**
30018      * @cfg {Array} items
30019      * array of button configs or elements to add (will be converted to a MixedCollection)
30020      */
30021     
30022     /**
30023      * @cfg {String/HTMLElement/Element} container
30024      * The id or element that will contain the toolbar
30025      */
30026     // private
30027     render : function(ct){
30028         this.el = Roo.get(ct);
30029         if(this.cls){
30030             this.el.addClass(this.cls);
30031         }
30032         // using a table allows for vertical alignment
30033         // 100% width is needed by Safari...
30034         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
30035         this.tr = this.el.child("tr", true);
30036         var autoId = 0;
30037         this.items = new Roo.util.MixedCollection(false, function(o){
30038             return o.id || ("item" + (++autoId));
30039         });
30040         if(this.buttons){
30041             this.add.apply(this, this.buttons);
30042             delete this.buttons;
30043         }
30044     },
30045
30046     /**
30047      * Adds element(s) to the toolbar -- this function takes a variable number of 
30048      * arguments of mixed type and adds them to the toolbar.
30049      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
30050      * <ul>
30051      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
30052      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
30053      * <li>Field: Any form field (equivalent to {@link #addField})</li>
30054      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
30055      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
30056      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
30057      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
30058      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
30059      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
30060      * </ul>
30061      * @param {Mixed} arg2
30062      * @param {Mixed} etc.
30063      */
30064     add : function(){
30065         var a = arguments, l = a.length;
30066         for(var i = 0; i < l; i++){
30067             this._add(a[i]);
30068         }
30069     },
30070     // private..
30071     _add : function(el) {
30072         
30073         if (el.xtype) {
30074             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
30075         }
30076         
30077         if (el.applyTo){ // some kind of form field
30078             return this.addField(el);
30079         } 
30080         if (el.render){ // some kind of Toolbar.Item
30081             return this.addItem(el);
30082         }
30083         if (typeof el == "string"){ // string
30084             if(el == "separator" || el == "-"){
30085                 return this.addSeparator();
30086             }
30087             if (el == " "){
30088                 return this.addSpacer();
30089             }
30090             if(el == "->"){
30091                 return this.addFill();
30092             }
30093             return this.addText(el);
30094             
30095         }
30096         if(el.tagName){ // element
30097             return this.addElement(el);
30098         }
30099         if(typeof el == "object"){ // must be button config?
30100             return this.addButton(el);
30101         }
30102         // and now what?!?!
30103         return false;
30104         
30105     },
30106     
30107     /**
30108      * Add an Xtype element
30109      * @param {Object} xtype Xtype Object
30110      * @return {Object} created Object
30111      */
30112     addxtype : function(e){
30113         return this.add(e);  
30114     },
30115     
30116     /**
30117      * Returns the Element for this toolbar.
30118      * @return {Roo.Element}
30119      */
30120     getEl : function(){
30121         return this.el;  
30122     },
30123     
30124     /**
30125      * Adds a separator
30126      * @return {Roo.Toolbar.Item} The separator item
30127      */
30128     addSeparator : function(){
30129         return this.addItem(new Roo.Toolbar.Separator());
30130     },
30131
30132     /**
30133      * Adds a spacer element
30134      * @return {Roo.Toolbar.Spacer} The spacer item
30135      */
30136     addSpacer : function(){
30137         return this.addItem(new Roo.Toolbar.Spacer());
30138     },
30139
30140     /**
30141      * Adds a fill element that forces subsequent additions to the right side of the toolbar
30142      * @return {Roo.Toolbar.Fill} The fill item
30143      */
30144     addFill : function(){
30145         return this.addItem(new Roo.Toolbar.Fill());
30146     },
30147
30148     /**
30149      * Adds any standard HTML element to the toolbar
30150      * @param {String/HTMLElement/Element} el The element or id of the element to add
30151      * @return {Roo.Toolbar.Item} The element's item
30152      */
30153     addElement : function(el){
30154         return this.addItem(new Roo.Toolbar.Item(el));
30155     },
30156     /**
30157      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
30158      * @type Roo.util.MixedCollection  
30159      */
30160     items : false,
30161      
30162     /**
30163      * Adds any Toolbar.Item or subclass
30164      * @param {Roo.Toolbar.Item} item
30165      * @return {Roo.Toolbar.Item} The item
30166      */
30167     addItem : function(item){
30168         var td = this.nextBlock();
30169         item.render(td);
30170         this.items.add(item);
30171         return item;
30172     },
30173     
30174     /**
30175      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
30176      * @param {Object/Array} config A button config or array of configs
30177      * @return {Roo.Toolbar.Button/Array}
30178      */
30179     addButton : function(config){
30180         if(config instanceof Array){
30181             var buttons = [];
30182             for(var i = 0, len = config.length; i < len; i++) {
30183                 buttons.push(this.addButton(config[i]));
30184             }
30185             return buttons;
30186         }
30187         var b = config;
30188         if(!(config instanceof Roo.Toolbar.Button)){
30189             b = config.split ?
30190                 new Roo.Toolbar.SplitButton(config) :
30191                 new Roo.Toolbar.Button(config);
30192         }
30193         var td = this.nextBlock();
30194         b.render(td);
30195         this.items.add(b);
30196         return b;
30197     },
30198     
30199     /**
30200      * Adds text to the toolbar
30201      * @param {String} text The text to add
30202      * @return {Roo.Toolbar.Item} The element's item
30203      */
30204     addText : function(text){
30205         return this.addItem(new Roo.Toolbar.TextItem(text));
30206     },
30207     
30208     /**
30209      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
30210      * @param {Number} index The index where the item is to be inserted
30211      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
30212      * @return {Roo.Toolbar.Button/Item}
30213      */
30214     insertButton : function(index, item){
30215         if(item instanceof Array){
30216             var buttons = [];
30217             for(var i = 0, len = item.length; i < len; i++) {
30218                buttons.push(this.insertButton(index + i, item[i]));
30219             }
30220             return buttons;
30221         }
30222         if (!(item instanceof Roo.Toolbar.Button)){
30223            item = new Roo.Toolbar.Button(item);
30224         }
30225         var td = document.createElement("td");
30226         this.tr.insertBefore(td, this.tr.childNodes[index]);
30227         item.render(td);
30228         this.items.insert(index, item);
30229         return item;
30230     },
30231     
30232     /**
30233      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
30234      * @param {Object} config
30235      * @return {Roo.Toolbar.Item} The element's item
30236      */
30237     addDom : function(config, returnEl){
30238         var td = this.nextBlock();
30239         Roo.DomHelper.overwrite(td, config);
30240         var ti = new Roo.Toolbar.Item(td.firstChild);
30241         ti.render(td);
30242         this.items.add(ti);
30243         return ti;
30244     },
30245
30246     /**
30247      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
30248      * @type Roo.util.MixedCollection  
30249      */
30250     fields : false,
30251     
30252     /**
30253      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
30254      * Note: the field should not have been rendered yet. For a field that has already been
30255      * rendered, use {@link #addElement}.
30256      * @param {Roo.form.Field} field
30257      * @return {Roo.ToolbarItem}
30258      */
30259      
30260       
30261     addField : function(field) {
30262         if (!this.fields) {
30263             var autoId = 0;
30264             this.fields = new Roo.util.MixedCollection(false, function(o){
30265                 return o.id || ("item" + (++autoId));
30266             });
30267
30268         }
30269         
30270         var td = this.nextBlock();
30271         field.render(td);
30272         var ti = new Roo.Toolbar.Item(td.firstChild);
30273         ti.render(td);
30274         this.items.add(ti);
30275         this.fields.add(field);
30276         return ti;
30277     },
30278     /**
30279      * Hide the toolbar
30280      * @method hide
30281      */
30282      
30283       
30284     hide : function()
30285     {
30286         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
30287         this.el.child('div').hide();
30288     },
30289     /**
30290      * Show the toolbar
30291      * @method show
30292      */
30293     show : function()
30294     {
30295         this.el.child('div').show();
30296     },
30297       
30298     // private
30299     nextBlock : function(){
30300         var td = document.createElement("td");
30301         this.tr.appendChild(td);
30302         return td;
30303     },
30304
30305     // private
30306     destroy : function(){
30307         if(this.items){ // rendered?
30308             Roo.destroy.apply(Roo, this.items.items);
30309         }
30310         if(this.fields){ // rendered?
30311             Roo.destroy.apply(Roo, this.fields.items);
30312         }
30313         Roo.Element.uncache(this.el, this.tr);
30314     }
30315 };
30316
30317 /**
30318  * @class Roo.Toolbar.Item
30319  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
30320  * @constructor
30321  * Creates a new Item
30322  * @param {HTMLElement} el 
30323  */
30324 Roo.Toolbar.Item = function(el){
30325     var cfg = {};
30326     if (typeof (el.xtype) != 'undefined') {
30327         cfg = el;
30328         el = cfg.el;
30329     }
30330     
30331     this.el = Roo.getDom(el);
30332     this.id = Roo.id(this.el);
30333     this.hidden = false;
30334     
30335     this.addEvents({
30336          /**
30337              * @event render
30338              * Fires when the button is rendered
30339              * @param {Button} this
30340              */
30341         'render': true
30342     });
30343     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
30344 };
30345 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
30346 //Roo.Toolbar.Item.prototype = {
30347     
30348     /**
30349      * Get this item's HTML Element
30350      * @return {HTMLElement}
30351      */
30352     getEl : function(){
30353        return this.el;  
30354     },
30355
30356     // private
30357     render : function(td){
30358         
30359          this.td = td;
30360         td.appendChild(this.el);
30361         
30362         this.fireEvent('render', this);
30363     },
30364     
30365     /**
30366      * Removes and destroys this item.
30367      */
30368     destroy : function(){
30369         this.td.parentNode.removeChild(this.td);
30370     },
30371     
30372     /**
30373      * Shows this item.
30374      */
30375     show: function(){
30376         this.hidden = false;
30377         this.td.style.display = "";
30378     },
30379     
30380     /**
30381      * Hides this item.
30382      */
30383     hide: function(){
30384         this.hidden = true;
30385         this.td.style.display = "none";
30386     },
30387     
30388     /**
30389      * Convenience function for boolean show/hide.
30390      * @param {Boolean} visible true to show/false to hide
30391      */
30392     setVisible: function(visible){
30393         if(visible) {
30394             this.show();
30395         }else{
30396             this.hide();
30397         }
30398     },
30399     
30400     /**
30401      * Try to focus this item.
30402      */
30403     focus : function(){
30404         Roo.fly(this.el).focus();
30405     },
30406     
30407     /**
30408      * Disables this item.
30409      */
30410     disable : function(){
30411         Roo.fly(this.td).addClass("x-item-disabled");
30412         this.disabled = true;
30413         this.el.disabled = true;
30414     },
30415     
30416     /**
30417      * Enables this item.
30418      */
30419     enable : function(){
30420         Roo.fly(this.td).removeClass("x-item-disabled");
30421         this.disabled = false;
30422         this.el.disabled = false;
30423     }
30424 });
30425
30426
30427 /**
30428  * @class Roo.Toolbar.Separator
30429  * @extends Roo.Toolbar.Item
30430  * A simple toolbar separator class
30431  * @constructor
30432  * Creates a new Separator
30433  */
30434 Roo.Toolbar.Separator = function(cfg){
30435     
30436     var s = document.createElement("span");
30437     s.className = "ytb-sep";
30438     if (cfg) {
30439         cfg.el = s;
30440     }
30441     
30442     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
30443 };
30444 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
30445     enable:Roo.emptyFn,
30446     disable:Roo.emptyFn,
30447     focus:Roo.emptyFn
30448 });
30449
30450 /**
30451  * @class Roo.Toolbar.Spacer
30452  * @extends Roo.Toolbar.Item
30453  * A simple element that adds extra horizontal space to a toolbar.
30454  * @constructor
30455  * Creates a new Spacer
30456  */
30457 Roo.Toolbar.Spacer = function(cfg){
30458     var s = document.createElement("div");
30459     s.className = "ytb-spacer";
30460     if (cfg) {
30461         cfg.el = s;
30462     }
30463     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
30464 };
30465 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
30466     enable:Roo.emptyFn,
30467     disable:Roo.emptyFn,
30468     focus:Roo.emptyFn
30469 });
30470
30471 /**
30472  * @class Roo.Toolbar.Fill
30473  * @extends Roo.Toolbar.Spacer
30474  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
30475  * @constructor
30476  * Creates a new Spacer
30477  */
30478 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
30479     // private
30480     render : function(td){
30481         td.style.width = '100%';
30482         Roo.Toolbar.Fill.superclass.render.call(this, td);
30483     }
30484 });
30485
30486 /**
30487  * @class Roo.Toolbar.TextItem
30488  * @extends Roo.Toolbar.Item
30489  * A simple class that renders text directly into a toolbar.
30490  * @constructor
30491  * Creates a new TextItem
30492  * @cfg {string} text 
30493  */
30494 Roo.Toolbar.TextItem = function(cfg){
30495     var  text = cfg || "";
30496     if (typeof(cfg) == 'object') {
30497         text = cfg.text || "";
30498     }  else {
30499         cfg = null;
30500     }
30501     var s = document.createElement("span");
30502     s.className = "ytb-text";
30503     s.innerHTML = text;
30504     if (cfg) {
30505         cfg.el  = s;
30506     }
30507     
30508     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
30509 };
30510 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
30511     
30512      
30513     enable:Roo.emptyFn,
30514     disable:Roo.emptyFn,
30515     focus:Roo.emptyFn
30516 });
30517
30518 /**
30519  * @class Roo.Toolbar.Button
30520  * @extends Roo.Button
30521  * A button that renders into a toolbar.
30522  * @constructor
30523  * Creates a new Button
30524  * @param {Object} config A standard {@link Roo.Button} config object
30525  */
30526 Roo.Toolbar.Button = function(config){
30527     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
30528 };
30529 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
30530     render : function(td){
30531         this.td = td;
30532         Roo.Toolbar.Button.superclass.render.call(this, td);
30533     },
30534     
30535     /**
30536      * Removes and destroys this button
30537      */
30538     destroy : function(){
30539         Roo.Toolbar.Button.superclass.destroy.call(this);
30540         this.td.parentNode.removeChild(this.td);
30541     },
30542     
30543     /**
30544      * Shows this button
30545      */
30546     show: function(){
30547         this.hidden = false;
30548         this.td.style.display = "";
30549     },
30550     
30551     /**
30552      * Hides this button
30553      */
30554     hide: function(){
30555         this.hidden = true;
30556         this.td.style.display = "none";
30557     },
30558
30559     /**
30560      * Disables this item
30561      */
30562     disable : function(){
30563         Roo.fly(this.td).addClass("x-item-disabled");
30564         this.disabled = true;
30565     },
30566
30567     /**
30568      * Enables this item
30569      */
30570     enable : function(){
30571         Roo.fly(this.td).removeClass("x-item-disabled");
30572         this.disabled = false;
30573     }
30574 });
30575 // backwards compat
30576 Roo.ToolbarButton = Roo.Toolbar.Button;
30577
30578 /**
30579  * @class Roo.Toolbar.SplitButton
30580  * @extends Roo.SplitButton
30581  * A menu button that renders into a toolbar.
30582  * @constructor
30583  * Creates a new SplitButton
30584  * @param {Object} config A standard {@link Roo.SplitButton} config object
30585  */
30586 Roo.Toolbar.SplitButton = function(config){
30587     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
30588 };
30589 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
30590     render : function(td){
30591         this.td = td;
30592         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
30593     },
30594     
30595     /**
30596      * Removes and destroys this button
30597      */
30598     destroy : function(){
30599         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
30600         this.td.parentNode.removeChild(this.td);
30601     },
30602     
30603     /**
30604      * Shows this button
30605      */
30606     show: function(){
30607         this.hidden = false;
30608         this.td.style.display = "";
30609     },
30610     
30611     /**
30612      * Hides this button
30613      */
30614     hide: function(){
30615         this.hidden = true;
30616         this.td.style.display = "none";
30617     }
30618 });
30619
30620 // backwards compat
30621 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
30622  * Based on:
30623  * Ext JS Library 1.1.1
30624  * Copyright(c) 2006-2007, Ext JS, LLC.
30625  *
30626  * Originally Released Under LGPL - original licence link has changed is not relivant.
30627  *
30628  * Fork - LGPL
30629  * <script type="text/javascript">
30630  */
30631  
30632 /**
30633  * @class Roo.PagingToolbar
30634  * @extends Roo.Toolbar
30635  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
30636  * @constructor
30637  * Create a new PagingToolbar
30638  * @param {Object} config The config object
30639  */
30640 Roo.PagingToolbar = function(el, ds, config)
30641 {
30642     // old args format still supported... - xtype is prefered..
30643     if (typeof(el) == 'object' && el.xtype) {
30644         // created from xtype...
30645         config = el;
30646         ds = el.dataSource;
30647         el = config.container;
30648     }
30649     var items = [];
30650     if (config.items) {
30651         items = config.items;
30652         config.items = [];
30653     }
30654     
30655     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
30656     this.ds = ds;
30657     this.cursor = 0;
30658     this.renderButtons(this.el);
30659     this.bind(ds);
30660     
30661     // supprot items array.
30662    
30663     Roo.each(items, function(e) {
30664         this.add(Roo.factory(e));
30665     },this);
30666     
30667 };
30668
30669 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
30670     /**
30671      * @cfg {Roo.data.Store} dataSource
30672      * The underlying data store providing the paged data
30673      */
30674     /**
30675      * @cfg {String/HTMLElement/Element} container
30676      * container The id or element that will contain the toolbar
30677      */
30678     /**
30679      * @cfg {Boolean} displayInfo
30680      * True to display the displayMsg (defaults to false)
30681      */
30682     /**
30683      * @cfg {Number} pageSize
30684      * The number of records to display per page (defaults to 20)
30685      */
30686     pageSize: 20,
30687     /**
30688      * @cfg {String} displayMsg
30689      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
30690      */
30691     displayMsg : 'Displaying {0} - {1} of {2}',
30692     /**
30693      * @cfg {String} emptyMsg
30694      * The message to display when no records are found (defaults to "No data to display")
30695      */
30696     emptyMsg : 'No data to display',
30697     /**
30698      * Customizable piece of the default paging text (defaults to "Page")
30699      * @type String
30700      */
30701     beforePageText : "Page",
30702     /**
30703      * Customizable piece of the default paging text (defaults to "of %0")
30704      * @type String
30705      */
30706     afterPageText : "of {0}",
30707     /**
30708      * Customizable piece of the default paging text (defaults to "First Page")
30709      * @type String
30710      */
30711     firstText : "First Page",
30712     /**
30713      * Customizable piece of the default paging text (defaults to "Previous Page")
30714      * @type String
30715      */
30716     prevText : "Previous Page",
30717     /**
30718      * Customizable piece of the default paging text (defaults to "Next Page")
30719      * @type String
30720      */
30721     nextText : "Next Page",
30722     /**
30723      * Customizable piece of the default paging text (defaults to "Last Page")
30724      * @type String
30725      */
30726     lastText : "Last Page",
30727     /**
30728      * Customizable piece of the default paging text (defaults to "Refresh")
30729      * @type String
30730      */
30731     refreshText : "Refresh",
30732
30733     // private
30734     renderButtons : function(el){
30735         Roo.PagingToolbar.superclass.render.call(this, el);
30736         this.first = this.addButton({
30737             tooltip: this.firstText,
30738             cls: "x-btn-icon x-grid-page-first",
30739             disabled: true,
30740             handler: this.onClick.createDelegate(this, ["first"])
30741         });
30742         this.prev = this.addButton({
30743             tooltip: this.prevText,
30744             cls: "x-btn-icon x-grid-page-prev",
30745             disabled: true,
30746             handler: this.onClick.createDelegate(this, ["prev"])
30747         });
30748         //this.addSeparator();
30749         this.add(this.beforePageText);
30750         this.field = Roo.get(this.addDom({
30751            tag: "input",
30752            type: "text",
30753            size: "3",
30754            value: "1",
30755            cls: "x-grid-page-number"
30756         }).el);
30757         this.field.on("keydown", this.onPagingKeydown, this);
30758         this.field.on("focus", function(){this.dom.select();});
30759         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
30760         this.field.setHeight(18);
30761         //this.addSeparator();
30762         this.next = this.addButton({
30763             tooltip: this.nextText,
30764             cls: "x-btn-icon x-grid-page-next",
30765             disabled: true,
30766             handler: this.onClick.createDelegate(this, ["next"])
30767         });
30768         this.last = this.addButton({
30769             tooltip: this.lastText,
30770             cls: "x-btn-icon x-grid-page-last",
30771             disabled: true,
30772             handler: this.onClick.createDelegate(this, ["last"])
30773         });
30774         //this.addSeparator();
30775         this.loading = this.addButton({
30776             tooltip: this.refreshText,
30777             cls: "x-btn-icon x-grid-loading",
30778             handler: this.onClick.createDelegate(this, ["refresh"])
30779         });
30780
30781         if(this.displayInfo){
30782             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
30783         }
30784     },
30785
30786     // private
30787     updateInfo : function(){
30788         if(this.displayEl){
30789             var count = this.ds.getCount();
30790             var msg = count == 0 ?
30791                 this.emptyMsg :
30792                 String.format(
30793                     this.displayMsg,
30794                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
30795                 );
30796             this.displayEl.update(msg);
30797         }
30798     },
30799
30800     // private
30801     onLoad : function(ds, r, o){
30802        this.cursor = o.params ? o.params.start : 0;
30803        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
30804
30805        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
30806        this.field.dom.value = ap;
30807        this.first.setDisabled(ap == 1);
30808        this.prev.setDisabled(ap == 1);
30809        this.next.setDisabled(ap == ps);
30810        this.last.setDisabled(ap == ps);
30811        this.loading.enable();
30812        this.updateInfo();
30813     },
30814
30815     // private
30816     getPageData : function(){
30817         var total = this.ds.getTotalCount();
30818         return {
30819             total : total,
30820             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
30821             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
30822         };
30823     },
30824
30825     // private
30826     onLoadError : function(){
30827         this.loading.enable();
30828     },
30829
30830     // private
30831     onPagingKeydown : function(e){
30832         var k = e.getKey();
30833         var d = this.getPageData();
30834         if(k == e.RETURN){
30835             var v = this.field.dom.value, pageNum;
30836             if(!v || isNaN(pageNum = parseInt(v, 10))){
30837                 this.field.dom.value = d.activePage;
30838                 return;
30839             }
30840             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
30841             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30842             e.stopEvent();
30843         }
30844         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))
30845         {
30846           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
30847           this.field.dom.value = pageNum;
30848           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
30849           e.stopEvent();
30850         }
30851         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
30852         {
30853           var v = this.field.dom.value, pageNum; 
30854           var increment = (e.shiftKey) ? 10 : 1;
30855           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
30856             increment *= -1;
30857           }
30858           if(!v || isNaN(pageNum = parseInt(v, 10))) {
30859             this.field.dom.value = d.activePage;
30860             return;
30861           }
30862           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
30863           {
30864             this.field.dom.value = parseInt(v, 10) + increment;
30865             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
30866             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30867           }
30868           e.stopEvent();
30869         }
30870     },
30871
30872     // private
30873     beforeLoad : function(){
30874         if(this.loading){
30875             this.loading.disable();
30876         }
30877     },
30878
30879     // private
30880     onClick : function(which){
30881         var ds = this.ds;
30882         switch(which){
30883             case "first":
30884                 ds.load({params:{start: 0, limit: this.pageSize}});
30885             break;
30886             case "prev":
30887                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
30888             break;
30889             case "next":
30890                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
30891             break;
30892             case "last":
30893                 var total = ds.getTotalCount();
30894                 var extra = total % this.pageSize;
30895                 var lastStart = extra ? (total - extra) : total-this.pageSize;
30896                 ds.load({params:{start: lastStart, limit: this.pageSize}});
30897             break;
30898             case "refresh":
30899                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
30900             break;
30901         }
30902     },
30903
30904     /**
30905      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
30906      * @param {Roo.data.Store} store The data store to unbind
30907      */
30908     unbind : function(ds){
30909         ds.un("beforeload", this.beforeLoad, this);
30910         ds.un("load", this.onLoad, this);
30911         ds.un("loadexception", this.onLoadError, this);
30912         ds.un("remove", this.updateInfo, this);
30913         ds.un("add", this.updateInfo, this);
30914         this.ds = undefined;
30915     },
30916
30917     /**
30918      * Binds the paging toolbar to the specified {@link Roo.data.Store}
30919      * @param {Roo.data.Store} store The data store to bind
30920      */
30921     bind : function(ds){
30922         ds.on("beforeload", this.beforeLoad, this);
30923         ds.on("load", this.onLoad, this);
30924         ds.on("loadexception", this.onLoadError, this);
30925         ds.on("remove", this.updateInfo, this);
30926         ds.on("add", this.updateInfo, this);
30927         this.ds = ds;
30928     }
30929 });/*
30930  * Based on:
30931  * Ext JS Library 1.1.1
30932  * Copyright(c) 2006-2007, Ext JS, LLC.
30933  *
30934  * Originally Released Under LGPL - original licence link has changed is not relivant.
30935  *
30936  * Fork - LGPL
30937  * <script type="text/javascript">
30938  */
30939
30940 /**
30941  * @class Roo.Resizable
30942  * @extends Roo.util.Observable
30943  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
30944  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
30945  * 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
30946  * the element will be wrapped for you automatically.</p>
30947  * <p>Here is the list of valid resize handles:</p>
30948  * <pre>
30949 Value   Description
30950 ------  -------------------
30951  'n'     north
30952  's'     south
30953  'e'     east
30954  'w'     west
30955  'nw'    northwest
30956  'sw'    southwest
30957  'se'    southeast
30958  'ne'    northeast
30959  'hd'    horizontal drag
30960  'all'   all
30961 </pre>
30962  * <p>Here's an example showing the creation of a typical Resizable:</p>
30963  * <pre><code>
30964 var resizer = new Roo.Resizable("element-id", {
30965     handles: 'all',
30966     minWidth: 200,
30967     minHeight: 100,
30968     maxWidth: 500,
30969     maxHeight: 400,
30970     pinned: true
30971 });
30972 resizer.on("resize", myHandler);
30973 </code></pre>
30974  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
30975  * resizer.east.setDisplayed(false);</p>
30976  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
30977  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
30978  * resize operation's new size (defaults to [0, 0])
30979  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
30980  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
30981  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
30982  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
30983  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
30984  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
30985  * @cfg {Number} width The width of the element in pixels (defaults to null)
30986  * @cfg {Number} height The height of the element in pixels (defaults to null)
30987  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
30988  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
30989  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
30990  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
30991  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
30992  * in favor of the handles config option (defaults to false)
30993  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
30994  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
30995  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
30996  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
30997  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
30998  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
30999  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
31000  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
31001  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
31002  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
31003  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
31004  * @constructor
31005  * Create a new resizable component
31006  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
31007  * @param {Object} config configuration options
31008   */
31009 Roo.Resizable = function(el, config)
31010 {
31011     this.el = Roo.get(el);
31012
31013     if(config && config.wrap){
31014         config.resizeChild = this.el;
31015         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
31016         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
31017         this.el.setStyle("overflow", "hidden");
31018         this.el.setPositioning(config.resizeChild.getPositioning());
31019         config.resizeChild.clearPositioning();
31020         if(!config.width || !config.height){
31021             var csize = config.resizeChild.getSize();
31022             this.el.setSize(csize.width, csize.height);
31023         }
31024         if(config.pinned && !config.adjustments){
31025             config.adjustments = "auto";
31026         }
31027     }
31028
31029     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
31030     this.proxy.unselectable();
31031     this.proxy.enableDisplayMode('block');
31032
31033     Roo.apply(this, config);
31034
31035     if(this.pinned){
31036         this.disableTrackOver = true;
31037         this.el.addClass("x-resizable-pinned");
31038     }
31039     // if the element isn't positioned, make it relative
31040     var position = this.el.getStyle("position");
31041     if(position != "absolute" && position != "fixed"){
31042         this.el.setStyle("position", "relative");
31043     }
31044     if(!this.handles){ // no handles passed, must be legacy style
31045         this.handles = 's,e,se';
31046         if(this.multiDirectional){
31047             this.handles += ',n,w';
31048         }
31049     }
31050     if(this.handles == "all"){
31051         this.handles = "n s e w ne nw se sw";
31052     }
31053     var hs = this.handles.split(/\s*?[,;]\s*?| /);
31054     var ps = Roo.Resizable.positions;
31055     for(var i = 0, len = hs.length; i < len; i++){
31056         if(hs[i] && ps[hs[i]]){
31057             var pos = ps[hs[i]];
31058             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
31059         }
31060     }
31061     // legacy
31062     this.corner = this.southeast;
31063     
31064     // updateBox = the box can move..
31065     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
31066         this.updateBox = true;
31067     }
31068
31069     this.activeHandle = null;
31070
31071     if(this.resizeChild){
31072         if(typeof this.resizeChild == "boolean"){
31073             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
31074         }else{
31075             this.resizeChild = Roo.get(this.resizeChild, true);
31076         }
31077     }
31078     
31079     if(this.adjustments == "auto"){
31080         var rc = this.resizeChild;
31081         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
31082         if(rc && (hw || hn)){
31083             rc.position("relative");
31084             rc.setLeft(hw ? hw.el.getWidth() : 0);
31085             rc.setTop(hn ? hn.el.getHeight() : 0);
31086         }
31087         this.adjustments = [
31088             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
31089             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
31090         ];
31091     }
31092
31093     if(this.draggable){
31094         this.dd = this.dynamic ?
31095             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
31096         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
31097     }
31098
31099     // public events
31100     this.addEvents({
31101         /**
31102          * @event beforeresize
31103          * Fired before resize is allowed. Set enabled to false to cancel resize.
31104          * @param {Roo.Resizable} this
31105          * @param {Roo.EventObject} e The mousedown event
31106          */
31107         "beforeresize" : true,
31108         /**
31109          * @event resizing
31110          * Fired a resizing.
31111          * @param {Roo.Resizable} this
31112          * @param {Number} x The new x position
31113          * @param {Number} y The new y position
31114          * @param {Number} w The new w width
31115          * @param {Number} h The new h hight
31116          * @param {Roo.EventObject} e The mouseup event
31117          */
31118         "resizing" : true,
31119         /**
31120          * @event resize
31121          * Fired after a resize.
31122          * @param {Roo.Resizable} this
31123          * @param {Number} width The new width
31124          * @param {Number} height The new height
31125          * @param {Roo.EventObject} e The mouseup event
31126          */
31127         "resize" : true
31128     });
31129
31130     if(this.width !== null && this.height !== null){
31131         this.resizeTo(this.width, this.height);
31132     }else{
31133         this.updateChildSize();
31134     }
31135     if(Roo.isIE){
31136         this.el.dom.style.zoom = 1;
31137     }
31138     Roo.Resizable.superclass.constructor.call(this);
31139 };
31140
31141 Roo.extend(Roo.Resizable, Roo.util.Observable, {
31142         resizeChild : false,
31143         adjustments : [0, 0],
31144         minWidth : 5,
31145         minHeight : 5,
31146         maxWidth : 10000,
31147         maxHeight : 10000,
31148         enabled : true,
31149         animate : false,
31150         duration : .35,
31151         dynamic : false,
31152         handles : false,
31153         multiDirectional : false,
31154         disableTrackOver : false,
31155         easing : 'easeOutStrong',
31156         widthIncrement : 0,
31157         heightIncrement : 0,
31158         pinned : false,
31159         width : null,
31160         height : null,
31161         preserveRatio : false,
31162         transparent: false,
31163         minX: 0,
31164         minY: 0,
31165         draggable: false,
31166
31167         /**
31168          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
31169          */
31170         constrainTo: undefined,
31171         /**
31172          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
31173          */
31174         resizeRegion: undefined,
31175
31176
31177     /**
31178      * Perform a manual resize
31179      * @param {Number} width
31180      * @param {Number} height
31181      */
31182     resizeTo : function(width, height){
31183         this.el.setSize(width, height);
31184         this.updateChildSize();
31185         this.fireEvent("resize", this, width, height, null);
31186     },
31187
31188     // private
31189     startSizing : function(e, handle){
31190         this.fireEvent("beforeresize", this, e);
31191         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
31192
31193             if(!this.overlay){
31194                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
31195                 this.overlay.unselectable();
31196                 this.overlay.enableDisplayMode("block");
31197                 this.overlay.on("mousemove", this.onMouseMove, this);
31198                 this.overlay.on("mouseup", this.onMouseUp, this);
31199             }
31200             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
31201
31202             this.resizing = true;
31203             this.startBox = this.el.getBox();
31204             this.startPoint = e.getXY();
31205             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
31206                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
31207
31208             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
31209             this.overlay.show();
31210
31211             if(this.constrainTo) {
31212                 var ct = Roo.get(this.constrainTo);
31213                 this.resizeRegion = ct.getRegion().adjust(
31214                     ct.getFrameWidth('t'),
31215                     ct.getFrameWidth('l'),
31216                     -ct.getFrameWidth('b'),
31217                     -ct.getFrameWidth('r')
31218                 );
31219             }
31220
31221             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
31222             this.proxy.show();
31223             this.proxy.setBox(this.startBox);
31224             if(!this.dynamic){
31225                 this.proxy.setStyle('visibility', 'visible');
31226             }
31227         }
31228     },
31229
31230     // private
31231     onMouseDown : function(handle, e){
31232         if(this.enabled){
31233             e.stopEvent();
31234             this.activeHandle = handle;
31235             this.startSizing(e, handle);
31236         }
31237     },
31238
31239     // private
31240     onMouseUp : function(e){
31241         var size = this.resizeElement();
31242         this.resizing = false;
31243         this.handleOut();
31244         this.overlay.hide();
31245         this.proxy.hide();
31246         this.fireEvent("resize", this, size.width, size.height, e);
31247     },
31248
31249     // private
31250     updateChildSize : function(){
31251         
31252         if(this.resizeChild){
31253             var el = this.el;
31254             var child = this.resizeChild;
31255             var adj = this.adjustments;
31256             if(el.dom.offsetWidth){
31257                 var b = el.getSize(true);
31258                 child.setSize(b.width+adj[0], b.height+adj[1]);
31259             }
31260             // Second call here for IE
31261             // The first call enables instant resizing and
31262             // the second call corrects scroll bars if they
31263             // exist
31264             if(Roo.isIE){
31265                 setTimeout(function(){
31266                     if(el.dom.offsetWidth){
31267                         var b = el.getSize(true);
31268                         child.setSize(b.width+adj[0], b.height+adj[1]);
31269                     }
31270                 }, 10);
31271             }
31272         }
31273     },
31274
31275     // private
31276     snap : function(value, inc, min){
31277         if(!inc || !value) {
31278             return value;
31279         }
31280         var newValue = value;
31281         var m = value % inc;
31282         if(m > 0){
31283             if(m > (inc/2)){
31284                 newValue = value + (inc-m);
31285             }else{
31286                 newValue = value - m;
31287             }
31288         }
31289         return Math.max(min, newValue);
31290     },
31291
31292     // private
31293     resizeElement : function(){
31294         var box = this.proxy.getBox();
31295         if(this.updateBox){
31296             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
31297         }else{
31298             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
31299         }
31300         this.updateChildSize();
31301         if(!this.dynamic){
31302             this.proxy.hide();
31303         }
31304         return box;
31305     },
31306
31307     // private
31308     constrain : function(v, diff, m, mx){
31309         if(v - diff < m){
31310             diff = v - m;
31311         }else if(v - diff > mx){
31312             diff = mx - v;
31313         }
31314         return diff;
31315     },
31316
31317     // private
31318     onMouseMove : function(e){
31319         
31320         if(this.enabled){
31321             try{// try catch so if something goes wrong the user doesn't get hung
31322
31323             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
31324                 return;
31325             }
31326
31327             //var curXY = this.startPoint;
31328             var curSize = this.curSize || this.startBox;
31329             var x = this.startBox.x, y = this.startBox.y;
31330             var ox = x, oy = y;
31331             var w = curSize.width, h = curSize.height;
31332             var ow = w, oh = h;
31333             var mw = this.minWidth, mh = this.minHeight;
31334             var mxw = this.maxWidth, mxh = this.maxHeight;
31335             var wi = this.widthIncrement;
31336             var hi = this.heightIncrement;
31337
31338             var eventXY = e.getXY();
31339             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
31340             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
31341
31342             var pos = this.activeHandle.position;
31343
31344             switch(pos){
31345                 case "east":
31346                     w += diffX;
31347                     w = Math.min(Math.max(mw, w), mxw);
31348                     break;
31349              
31350                 case "south":
31351                     h += diffY;
31352                     h = Math.min(Math.max(mh, h), mxh);
31353                     break;
31354                 case "southeast":
31355                     w += diffX;
31356                     h += diffY;
31357                     w = Math.min(Math.max(mw, w), mxw);
31358                     h = Math.min(Math.max(mh, h), mxh);
31359                     break;
31360                 case "north":
31361                     diffY = this.constrain(h, diffY, mh, mxh);
31362                     y += diffY;
31363                     h -= diffY;
31364                     break;
31365                 case "hdrag":
31366                     
31367                     if (wi) {
31368                         var adiffX = Math.abs(diffX);
31369                         var sub = (adiffX % wi); // how much 
31370                         if (sub > (wi/2)) { // far enough to snap
31371                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
31372                         } else {
31373                             // remove difference.. 
31374                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
31375                         }
31376                     }
31377                     x += diffX;
31378                     x = Math.max(this.minX, x);
31379                     break;
31380                 case "west":
31381                     diffX = this.constrain(w, diffX, mw, mxw);
31382                     x += diffX;
31383                     w -= diffX;
31384                     break;
31385                 case "northeast":
31386                     w += diffX;
31387                     w = Math.min(Math.max(mw, w), mxw);
31388                     diffY = this.constrain(h, diffY, mh, mxh);
31389                     y += diffY;
31390                     h -= diffY;
31391                     break;
31392                 case "northwest":
31393                     diffX = this.constrain(w, diffX, mw, mxw);
31394                     diffY = this.constrain(h, diffY, mh, mxh);
31395                     y += diffY;
31396                     h -= diffY;
31397                     x += diffX;
31398                     w -= diffX;
31399                     break;
31400                case "southwest":
31401                     diffX = this.constrain(w, diffX, mw, mxw);
31402                     h += diffY;
31403                     h = Math.min(Math.max(mh, h), mxh);
31404                     x += diffX;
31405                     w -= diffX;
31406                     break;
31407             }
31408
31409             var sw = this.snap(w, wi, mw);
31410             var sh = this.snap(h, hi, mh);
31411             if(sw != w || sh != h){
31412                 switch(pos){
31413                     case "northeast":
31414                         y -= sh - h;
31415                     break;
31416                     case "north":
31417                         y -= sh - h;
31418                         break;
31419                     case "southwest":
31420                         x -= sw - w;
31421                     break;
31422                     case "west":
31423                         x -= sw - w;
31424                         break;
31425                     case "northwest":
31426                         x -= sw - w;
31427                         y -= sh - h;
31428                     break;
31429                 }
31430                 w = sw;
31431                 h = sh;
31432             }
31433
31434             if(this.preserveRatio){
31435                 switch(pos){
31436                     case "southeast":
31437                     case "east":
31438                         h = oh * (w/ow);
31439                         h = Math.min(Math.max(mh, h), mxh);
31440                         w = ow * (h/oh);
31441                        break;
31442                     case "south":
31443                         w = ow * (h/oh);
31444                         w = Math.min(Math.max(mw, w), mxw);
31445                         h = oh * (w/ow);
31446                         break;
31447                     case "northeast":
31448                         w = ow * (h/oh);
31449                         w = Math.min(Math.max(mw, w), mxw);
31450                         h = oh * (w/ow);
31451                     break;
31452                     case "north":
31453                         var tw = w;
31454                         w = ow * (h/oh);
31455                         w = Math.min(Math.max(mw, w), mxw);
31456                         h = oh * (w/ow);
31457                         x += (tw - w) / 2;
31458                         break;
31459                     case "southwest":
31460                         h = oh * (w/ow);
31461                         h = Math.min(Math.max(mh, h), mxh);
31462                         var tw = w;
31463                         w = ow * (h/oh);
31464                         x += tw - w;
31465                         break;
31466                     case "west":
31467                         var th = h;
31468                         h = oh * (w/ow);
31469                         h = Math.min(Math.max(mh, h), mxh);
31470                         y += (th - h) / 2;
31471                         var tw = w;
31472                         w = ow * (h/oh);
31473                         x += tw - w;
31474                        break;
31475                     case "northwest":
31476                         var tw = w;
31477                         var th = h;
31478                         h = oh * (w/ow);
31479                         h = Math.min(Math.max(mh, h), mxh);
31480                         w = ow * (h/oh);
31481                         y += th - h;
31482                         x += tw - w;
31483                        break;
31484
31485                 }
31486             }
31487             if (pos == 'hdrag') {
31488                 w = ow;
31489             }
31490             this.proxy.setBounds(x, y, w, h);
31491             if(this.dynamic){
31492                 this.resizeElement();
31493             }
31494             }catch(e){}
31495         }
31496         this.fireEvent("resizing", this, x, y, w, h, e);
31497     },
31498
31499     // private
31500     handleOver : function(){
31501         if(this.enabled){
31502             this.el.addClass("x-resizable-over");
31503         }
31504     },
31505
31506     // private
31507     handleOut : function(){
31508         if(!this.resizing){
31509             this.el.removeClass("x-resizable-over");
31510         }
31511     },
31512
31513     /**
31514      * Returns the element this component is bound to.
31515      * @return {Roo.Element}
31516      */
31517     getEl : function(){
31518         return this.el;
31519     },
31520
31521     /**
31522      * Returns the resizeChild element (or null).
31523      * @return {Roo.Element}
31524      */
31525     getResizeChild : function(){
31526         return this.resizeChild;
31527     },
31528     groupHandler : function()
31529     {
31530         
31531     },
31532     /**
31533      * Destroys this resizable. If the element was wrapped and
31534      * removeEl is not true then the element remains.
31535      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
31536      */
31537     destroy : function(removeEl){
31538         this.proxy.remove();
31539         if(this.overlay){
31540             this.overlay.removeAllListeners();
31541             this.overlay.remove();
31542         }
31543         var ps = Roo.Resizable.positions;
31544         for(var k in ps){
31545             if(typeof ps[k] != "function" && this[ps[k]]){
31546                 var h = this[ps[k]];
31547                 h.el.removeAllListeners();
31548                 h.el.remove();
31549             }
31550         }
31551         if(removeEl){
31552             this.el.update("");
31553             this.el.remove();
31554         }
31555     }
31556 });
31557
31558 // private
31559 // hash to map config positions to true positions
31560 Roo.Resizable.positions = {
31561     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
31562     hd: "hdrag"
31563 };
31564
31565 // private
31566 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
31567     if(!this.tpl){
31568         // only initialize the template if resizable is used
31569         var tpl = Roo.DomHelper.createTemplate(
31570             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
31571         );
31572         tpl.compile();
31573         Roo.Resizable.Handle.prototype.tpl = tpl;
31574     }
31575     this.position = pos;
31576     this.rz = rz;
31577     // show north drag fro topdra
31578     var handlepos = pos == 'hdrag' ? 'north' : pos;
31579     
31580     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
31581     if (pos == 'hdrag') {
31582         this.el.setStyle('cursor', 'pointer');
31583     }
31584     this.el.unselectable();
31585     if(transparent){
31586         this.el.setOpacity(0);
31587     }
31588     this.el.on("mousedown", this.onMouseDown, this);
31589     if(!disableTrackOver){
31590         this.el.on("mouseover", this.onMouseOver, this);
31591         this.el.on("mouseout", this.onMouseOut, this);
31592     }
31593 };
31594
31595 // private
31596 Roo.Resizable.Handle.prototype = {
31597     afterResize : function(rz){
31598         Roo.log('after?');
31599         // do nothing
31600     },
31601     // private
31602     onMouseDown : function(e){
31603         this.rz.onMouseDown(this, e);
31604     },
31605     // private
31606     onMouseOver : function(e){
31607         this.rz.handleOver(this, e);
31608     },
31609     // private
31610     onMouseOut : function(e){
31611         this.rz.handleOut(this, e);
31612     }
31613 };/*
31614  * Based on:
31615  * Ext JS Library 1.1.1
31616  * Copyright(c) 2006-2007, Ext JS, LLC.
31617  *
31618  * Originally Released Under LGPL - original licence link has changed is not relivant.
31619  *
31620  * Fork - LGPL
31621  * <script type="text/javascript">
31622  */
31623
31624 /**
31625  * @class Roo.Editor
31626  * @extends Roo.Component
31627  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
31628  * @constructor
31629  * Create a new Editor
31630  * @param {Roo.form.Field} field The Field object (or descendant)
31631  * @param {Object} config The config object
31632  */
31633 Roo.Editor = function(field, config){
31634     Roo.Editor.superclass.constructor.call(this, config);
31635     this.field = field;
31636     this.addEvents({
31637         /**
31638              * @event beforestartedit
31639              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
31640              * false from the handler of this event.
31641              * @param {Editor} this
31642              * @param {Roo.Element} boundEl The underlying element bound to this editor
31643              * @param {Mixed} value The field value being set
31644              */
31645         "beforestartedit" : true,
31646         /**
31647              * @event startedit
31648              * Fires when this editor is displayed
31649              * @param {Roo.Element} boundEl The underlying element bound to this editor
31650              * @param {Mixed} value The starting field value
31651              */
31652         "startedit" : true,
31653         /**
31654              * @event beforecomplete
31655              * Fires after a change has been made to the field, but before the change is reflected in the underlying
31656              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
31657              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
31658              * event will not fire since no edit actually occurred.
31659              * @param {Editor} this
31660              * @param {Mixed} value The current field value
31661              * @param {Mixed} startValue The original field value
31662              */
31663         "beforecomplete" : true,
31664         /**
31665              * @event complete
31666              * Fires after editing is complete and any changed value has been written to the underlying field.
31667              * @param {Editor} this
31668              * @param {Mixed} value The current field value
31669              * @param {Mixed} startValue The original field value
31670              */
31671         "complete" : true,
31672         /**
31673          * @event specialkey
31674          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
31675          * {@link Roo.EventObject#getKey} to determine which key was pressed.
31676          * @param {Roo.form.Field} this
31677          * @param {Roo.EventObject} e The event object
31678          */
31679         "specialkey" : true
31680     });
31681 };
31682
31683 Roo.extend(Roo.Editor, Roo.Component, {
31684     /**
31685      * @cfg {Boolean/String} autosize
31686      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
31687      * or "height" to adopt the height only (defaults to false)
31688      */
31689     /**
31690      * @cfg {Boolean} revertInvalid
31691      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
31692      * validation fails (defaults to true)
31693      */
31694     /**
31695      * @cfg {Boolean} ignoreNoChange
31696      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
31697      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
31698      * will never be ignored.
31699      */
31700     /**
31701      * @cfg {Boolean} hideEl
31702      * False to keep the bound element visible while the editor is displayed (defaults to true)
31703      */
31704     /**
31705      * @cfg {Mixed} value
31706      * The data value of the underlying field (defaults to "")
31707      */
31708     value : "",
31709     /**
31710      * @cfg {String} alignment
31711      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
31712      */
31713     alignment: "c-c?",
31714     /**
31715      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
31716      * for bottom-right shadow (defaults to "frame")
31717      */
31718     shadow : "frame",
31719     /**
31720      * @cfg {Boolean} constrain True to constrain the editor to the viewport
31721      */
31722     constrain : false,
31723     /**
31724      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
31725      */
31726     completeOnEnter : false,
31727     /**
31728      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
31729      */
31730     cancelOnEsc : false,
31731     /**
31732      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
31733      */
31734     updateEl : false,
31735
31736     // private
31737     onRender : function(ct, position){
31738         this.el = new Roo.Layer({
31739             shadow: this.shadow,
31740             cls: "x-editor",
31741             parentEl : ct,
31742             shim : this.shim,
31743             shadowOffset:4,
31744             id: this.id,
31745             constrain: this.constrain
31746         });
31747         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
31748         if(this.field.msgTarget != 'title'){
31749             this.field.msgTarget = 'qtip';
31750         }
31751         this.field.render(this.el);
31752         if(Roo.isGecko){
31753             this.field.el.dom.setAttribute('autocomplete', 'off');
31754         }
31755         this.field.on("specialkey", this.onSpecialKey, this);
31756         if(this.swallowKeys){
31757             this.field.el.swallowEvent(['keydown','keypress']);
31758         }
31759         this.field.show();
31760         this.field.on("blur", this.onBlur, this);
31761         if(this.field.grow){
31762             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
31763         }
31764     },
31765
31766     onSpecialKey : function(field, e)
31767     {
31768         //Roo.log('editor onSpecialKey');
31769         if(this.completeOnEnter && e.getKey() == e.ENTER){
31770             e.stopEvent();
31771             this.completeEdit();
31772             return;
31773         }
31774         // do not fire special key otherwise it might hide close the editor...
31775         if(e.getKey() == e.ENTER){    
31776             return;
31777         }
31778         if(this.cancelOnEsc && e.getKey() == e.ESC){
31779             this.cancelEdit();
31780             return;
31781         } 
31782         this.fireEvent('specialkey', field, e);
31783     
31784     },
31785
31786     /**
31787      * Starts the editing process and shows the editor.
31788      * @param {String/HTMLElement/Element} el The element to edit
31789      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
31790       * to the innerHTML of el.
31791      */
31792     startEdit : function(el, value){
31793         if(this.editing){
31794             this.completeEdit();
31795         }
31796         this.boundEl = Roo.get(el);
31797         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
31798         if(!this.rendered){
31799             this.render(this.parentEl || document.body);
31800         }
31801         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
31802             return;
31803         }
31804         this.startValue = v;
31805         this.field.setValue(v);
31806         if(this.autoSize){
31807             var sz = this.boundEl.getSize();
31808             switch(this.autoSize){
31809                 case "width":
31810                 this.setSize(sz.width,  "");
31811                 break;
31812                 case "height":
31813                 this.setSize("",  sz.height);
31814                 break;
31815                 default:
31816                 this.setSize(sz.width,  sz.height);
31817             }
31818         }
31819         this.el.alignTo(this.boundEl, this.alignment);
31820         this.editing = true;
31821         if(Roo.QuickTips){
31822             Roo.QuickTips.disable();
31823         }
31824         this.show();
31825     },
31826
31827     /**
31828      * Sets the height and width of this editor.
31829      * @param {Number} width The new width
31830      * @param {Number} height The new height
31831      */
31832     setSize : function(w, h){
31833         this.field.setSize(w, h);
31834         if(this.el){
31835             this.el.sync();
31836         }
31837     },
31838
31839     /**
31840      * Realigns the editor to the bound field based on the current alignment config value.
31841      */
31842     realign : function(){
31843         this.el.alignTo(this.boundEl, this.alignment);
31844     },
31845
31846     /**
31847      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
31848      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
31849      */
31850     completeEdit : function(remainVisible){
31851         if(!this.editing){
31852             return;
31853         }
31854         var v = this.getValue();
31855         if(this.revertInvalid !== false && !this.field.isValid()){
31856             v = this.startValue;
31857             this.cancelEdit(true);
31858         }
31859         if(String(v) === String(this.startValue) && this.ignoreNoChange){
31860             this.editing = false;
31861             this.hide();
31862             return;
31863         }
31864         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
31865             this.editing = false;
31866             if(this.updateEl && this.boundEl){
31867                 this.boundEl.update(v);
31868             }
31869             if(remainVisible !== true){
31870                 this.hide();
31871             }
31872             this.fireEvent("complete", this, v, this.startValue);
31873         }
31874     },
31875
31876     // private
31877     onShow : function(){
31878         this.el.show();
31879         if(this.hideEl !== false){
31880             this.boundEl.hide();
31881         }
31882         this.field.show();
31883         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
31884             this.fixIEFocus = true;
31885             this.deferredFocus.defer(50, this);
31886         }else{
31887             this.field.focus();
31888         }
31889         this.fireEvent("startedit", this.boundEl, this.startValue);
31890     },
31891
31892     deferredFocus : function(){
31893         if(this.editing){
31894             this.field.focus();
31895         }
31896     },
31897
31898     /**
31899      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
31900      * reverted to the original starting value.
31901      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
31902      * cancel (defaults to false)
31903      */
31904     cancelEdit : function(remainVisible){
31905         if(this.editing){
31906             this.setValue(this.startValue);
31907             if(remainVisible !== true){
31908                 this.hide();
31909             }
31910         }
31911     },
31912
31913     // private
31914     onBlur : function(){
31915         if(this.allowBlur !== true && this.editing){
31916             this.completeEdit();
31917         }
31918     },
31919
31920     // private
31921     onHide : function(){
31922         if(this.editing){
31923             this.completeEdit();
31924             return;
31925         }
31926         this.field.blur();
31927         if(this.field.collapse){
31928             this.field.collapse();
31929         }
31930         this.el.hide();
31931         if(this.hideEl !== false){
31932             this.boundEl.show();
31933         }
31934         if(Roo.QuickTips){
31935             Roo.QuickTips.enable();
31936         }
31937     },
31938
31939     /**
31940      * Sets the data value of the editor
31941      * @param {Mixed} value Any valid value supported by the underlying field
31942      */
31943     setValue : function(v){
31944         this.field.setValue(v);
31945     },
31946
31947     /**
31948      * Gets the data value of the editor
31949      * @return {Mixed} The data value
31950      */
31951     getValue : function(){
31952         return this.field.getValue();
31953     }
31954 });/*
31955  * Based on:
31956  * Ext JS Library 1.1.1
31957  * Copyright(c) 2006-2007, Ext JS, LLC.
31958  *
31959  * Originally Released Under LGPL - original licence link has changed is not relivant.
31960  *
31961  * Fork - LGPL
31962  * <script type="text/javascript">
31963  */
31964  
31965 /**
31966  * @class Roo.BasicDialog
31967  * @extends Roo.util.Observable
31968  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
31969  * <pre><code>
31970 var dlg = new Roo.BasicDialog("my-dlg", {
31971     height: 200,
31972     width: 300,
31973     minHeight: 100,
31974     minWidth: 150,
31975     modal: true,
31976     proxyDrag: true,
31977     shadow: true
31978 });
31979 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
31980 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
31981 dlg.addButton('Cancel', dlg.hide, dlg);
31982 dlg.show();
31983 </code></pre>
31984   <b>A Dialog should always be a direct child of the body element.</b>
31985  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
31986  * @cfg {String} title Default text to display in the title bar (defaults to null)
31987  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31988  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31989  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
31990  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
31991  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
31992  * (defaults to null with no animation)
31993  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
31994  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
31995  * property for valid values (defaults to 'all')
31996  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
31997  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
31998  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
31999  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
32000  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
32001  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
32002  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
32003  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
32004  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
32005  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
32006  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
32007  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
32008  * draggable = true (defaults to false)
32009  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
32010  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
32011  * shadow (defaults to false)
32012  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
32013  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
32014  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
32015  * @cfg {Array} buttons Array of buttons
32016  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
32017  * @constructor
32018  * Create a new BasicDialog.
32019  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
32020  * @param {Object} config Configuration options
32021  */
32022 Roo.BasicDialog = function(el, config){
32023     this.el = Roo.get(el);
32024     var dh = Roo.DomHelper;
32025     if(!this.el && config && config.autoCreate){
32026         if(typeof config.autoCreate == "object"){
32027             if(!config.autoCreate.id){
32028                 config.autoCreate.id = el;
32029             }
32030             this.el = dh.append(document.body,
32031                         config.autoCreate, true);
32032         }else{
32033             this.el = dh.append(document.body,
32034                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
32035         }
32036     }
32037     el = this.el;
32038     el.setDisplayed(true);
32039     el.hide = this.hideAction;
32040     this.id = el.id;
32041     el.addClass("x-dlg");
32042
32043     Roo.apply(this, config);
32044
32045     this.proxy = el.createProxy("x-dlg-proxy");
32046     this.proxy.hide = this.hideAction;
32047     this.proxy.setOpacity(.5);
32048     this.proxy.hide();
32049
32050     if(config.width){
32051         el.setWidth(config.width);
32052     }
32053     if(config.height){
32054         el.setHeight(config.height);
32055     }
32056     this.size = el.getSize();
32057     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
32058         this.xy = [config.x,config.y];
32059     }else{
32060         this.xy = el.getCenterXY(true);
32061     }
32062     /** The header element @type Roo.Element */
32063     this.header = el.child("> .x-dlg-hd");
32064     /** The body element @type Roo.Element */
32065     this.body = el.child("> .x-dlg-bd");
32066     /** The footer element @type Roo.Element */
32067     this.footer = el.child("> .x-dlg-ft");
32068
32069     if(!this.header){
32070         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
32071     }
32072     if(!this.body){
32073         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
32074     }
32075
32076     this.header.unselectable();
32077     if(this.title){
32078         this.header.update(this.title);
32079     }
32080     // this element allows the dialog to be focused for keyboard event
32081     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
32082     this.focusEl.swallowEvent("click", true);
32083
32084     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
32085
32086     // wrap the body and footer for special rendering
32087     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
32088     if(this.footer){
32089         this.bwrap.dom.appendChild(this.footer.dom);
32090     }
32091
32092     this.bg = this.el.createChild({
32093         tag: "div", cls:"x-dlg-bg",
32094         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
32095     });
32096     this.centerBg = this.bg.child("div.x-dlg-bg-center");
32097
32098
32099     if(this.autoScroll !== false && !this.autoTabs){
32100         this.body.setStyle("overflow", "auto");
32101     }
32102
32103     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
32104
32105     if(this.closable !== false){
32106         this.el.addClass("x-dlg-closable");
32107         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
32108         this.close.on("click", this.closeClick, this);
32109         this.close.addClassOnOver("x-dlg-close-over");
32110     }
32111     if(this.collapsible !== false){
32112         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
32113         this.collapseBtn.on("click", this.collapseClick, this);
32114         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
32115         this.header.on("dblclick", this.collapseClick, this);
32116     }
32117     if(this.resizable !== false){
32118         this.el.addClass("x-dlg-resizable");
32119         this.resizer = new Roo.Resizable(el, {
32120             minWidth: this.minWidth || 80,
32121             minHeight:this.minHeight || 80,
32122             handles: this.resizeHandles || "all",
32123             pinned: true
32124         });
32125         this.resizer.on("beforeresize", this.beforeResize, this);
32126         this.resizer.on("resize", this.onResize, this);
32127     }
32128     if(this.draggable !== false){
32129         el.addClass("x-dlg-draggable");
32130         if (!this.proxyDrag) {
32131             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
32132         }
32133         else {
32134             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
32135         }
32136         dd.setHandleElId(this.header.id);
32137         dd.endDrag = this.endMove.createDelegate(this);
32138         dd.startDrag = this.startMove.createDelegate(this);
32139         dd.onDrag = this.onDrag.createDelegate(this);
32140         dd.scroll = false;
32141         this.dd = dd;
32142     }
32143     if(this.modal){
32144         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
32145         this.mask.enableDisplayMode("block");
32146         this.mask.hide();
32147         this.el.addClass("x-dlg-modal");
32148     }
32149     if(this.shadow){
32150         this.shadow = new Roo.Shadow({
32151             mode : typeof this.shadow == "string" ? this.shadow : "sides",
32152             offset : this.shadowOffset
32153         });
32154     }else{
32155         this.shadowOffset = 0;
32156     }
32157     if(Roo.useShims && this.shim !== false){
32158         this.shim = this.el.createShim();
32159         this.shim.hide = this.hideAction;
32160         this.shim.hide();
32161     }else{
32162         this.shim = false;
32163     }
32164     if(this.autoTabs){
32165         this.initTabs();
32166     }
32167     if (this.buttons) { 
32168         var bts= this.buttons;
32169         this.buttons = [];
32170         Roo.each(bts, function(b) {
32171             this.addButton(b);
32172         }, this);
32173     }
32174     
32175     
32176     this.addEvents({
32177         /**
32178          * @event keydown
32179          * Fires when a key is pressed
32180          * @param {Roo.BasicDialog} this
32181          * @param {Roo.EventObject} e
32182          */
32183         "keydown" : true,
32184         /**
32185          * @event move
32186          * Fires when this dialog is moved by the user.
32187          * @param {Roo.BasicDialog} this
32188          * @param {Number} x The new page X
32189          * @param {Number} y The new page Y
32190          */
32191         "move" : true,
32192         /**
32193          * @event resize
32194          * Fires when this dialog is resized by the user.
32195          * @param {Roo.BasicDialog} this
32196          * @param {Number} width The new width
32197          * @param {Number} height The new height
32198          */
32199         "resize" : true,
32200         /**
32201          * @event beforehide
32202          * Fires before this dialog is hidden.
32203          * @param {Roo.BasicDialog} this
32204          */
32205         "beforehide" : true,
32206         /**
32207          * @event hide
32208          * Fires when this dialog is hidden.
32209          * @param {Roo.BasicDialog} this
32210          */
32211         "hide" : true,
32212         /**
32213          * @event beforeshow
32214          * Fires before this dialog is shown.
32215          * @param {Roo.BasicDialog} this
32216          */
32217         "beforeshow" : true,
32218         /**
32219          * @event show
32220          * Fires when this dialog is shown.
32221          * @param {Roo.BasicDialog} this
32222          */
32223         "show" : true
32224     });
32225     el.on("keydown", this.onKeyDown, this);
32226     el.on("mousedown", this.toFront, this);
32227     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
32228     this.el.hide();
32229     Roo.DialogManager.register(this);
32230     Roo.BasicDialog.superclass.constructor.call(this);
32231 };
32232
32233 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
32234     shadowOffset: Roo.isIE ? 6 : 5,
32235     minHeight: 80,
32236     minWidth: 200,
32237     minButtonWidth: 75,
32238     defaultButton: null,
32239     buttonAlign: "right",
32240     tabTag: 'div',
32241     firstShow: true,
32242
32243     /**
32244      * Sets the dialog title text
32245      * @param {String} text The title text to display
32246      * @return {Roo.BasicDialog} this
32247      */
32248     setTitle : function(text){
32249         this.header.update(text);
32250         return this;
32251     },
32252
32253     // private
32254     closeClick : function(){
32255         this.hide();
32256     },
32257
32258     // private
32259     collapseClick : function(){
32260         this[this.collapsed ? "expand" : "collapse"]();
32261     },
32262
32263     /**
32264      * Collapses the dialog to its minimized state (only the title bar is visible).
32265      * Equivalent to the user clicking the collapse dialog button.
32266      */
32267     collapse : function(){
32268         if(!this.collapsed){
32269             this.collapsed = true;
32270             this.el.addClass("x-dlg-collapsed");
32271             this.restoreHeight = this.el.getHeight();
32272             this.resizeTo(this.el.getWidth(), this.header.getHeight());
32273         }
32274     },
32275
32276     /**
32277      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
32278      * clicking the expand dialog button.
32279      */
32280     expand : function(){
32281         if(this.collapsed){
32282             this.collapsed = false;
32283             this.el.removeClass("x-dlg-collapsed");
32284             this.resizeTo(this.el.getWidth(), this.restoreHeight);
32285         }
32286     },
32287
32288     /**
32289      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
32290      * @return {Roo.TabPanel} The tabs component
32291      */
32292     initTabs : function(){
32293         var tabs = this.getTabs();
32294         while(tabs.getTab(0)){
32295             tabs.removeTab(0);
32296         }
32297         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
32298             var dom = el.dom;
32299             tabs.addTab(Roo.id(dom), dom.title);
32300             dom.title = "";
32301         });
32302         tabs.activate(0);
32303         return tabs;
32304     },
32305
32306     // private
32307     beforeResize : function(){
32308         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
32309     },
32310
32311     // private
32312     onResize : function(){
32313         this.refreshSize();
32314         this.syncBodyHeight();
32315         this.adjustAssets();
32316         this.focus();
32317         this.fireEvent("resize", this, this.size.width, this.size.height);
32318     },
32319
32320     // private
32321     onKeyDown : function(e){
32322         if(this.isVisible()){
32323             this.fireEvent("keydown", this, e);
32324         }
32325     },
32326
32327     /**
32328      * Resizes the dialog.
32329      * @param {Number} width
32330      * @param {Number} height
32331      * @return {Roo.BasicDialog} this
32332      */
32333     resizeTo : function(width, height){
32334         this.el.setSize(width, height);
32335         this.size = {width: width, height: height};
32336         this.syncBodyHeight();
32337         if(this.fixedcenter){
32338             this.center();
32339         }
32340         if(this.isVisible()){
32341             this.constrainXY();
32342             this.adjustAssets();
32343         }
32344         this.fireEvent("resize", this, width, height);
32345         return this;
32346     },
32347
32348
32349     /**
32350      * Resizes the dialog to fit the specified content size.
32351      * @param {Number} width
32352      * @param {Number} height
32353      * @return {Roo.BasicDialog} this
32354      */
32355     setContentSize : function(w, h){
32356         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
32357         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
32358         //if(!this.el.isBorderBox()){
32359             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
32360             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
32361         //}
32362         if(this.tabs){
32363             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
32364             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
32365         }
32366         this.resizeTo(w, h);
32367         return this;
32368     },
32369
32370     /**
32371      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
32372      * executed in response to a particular key being pressed while the dialog is active.
32373      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
32374      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
32375      * @param {Function} fn The function to call
32376      * @param {Object} scope (optional) The scope of the function
32377      * @return {Roo.BasicDialog} this
32378      */
32379     addKeyListener : function(key, fn, scope){
32380         var keyCode, shift, ctrl, alt;
32381         if(typeof key == "object" && !(key instanceof Array)){
32382             keyCode = key["key"];
32383             shift = key["shift"];
32384             ctrl = key["ctrl"];
32385             alt = key["alt"];
32386         }else{
32387             keyCode = key;
32388         }
32389         var handler = function(dlg, e){
32390             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
32391                 var k = e.getKey();
32392                 if(keyCode instanceof Array){
32393                     for(var i = 0, len = keyCode.length; i < len; i++){
32394                         if(keyCode[i] == k){
32395                           fn.call(scope || window, dlg, k, e);
32396                           return;
32397                         }
32398                     }
32399                 }else{
32400                     if(k == keyCode){
32401                         fn.call(scope || window, dlg, k, e);
32402                     }
32403                 }
32404             }
32405         };
32406         this.on("keydown", handler);
32407         return this;
32408     },
32409
32410     /**
32411      * Returns the TabPanel component (creates it if it doesn't exist).
32412      * Note: If you wish to simply check for the existence of tabs without creating them,
32413      * check for a null 'tabs' property.
32414      * @return {Roo.TabPanel} The tabs component
32415      */
32416     getTabs : function(){
32417         if(!this.tabs){
32418             this.el.addClass("x-dlg-auto-tabs");
32419             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
32420             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
32421         }
32422         return this.tabs;
32423     },
32424
32425     /**
32426      * Adds a button to the footer section of the dialog.
32427      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
32428      * object or a valid Roo.DomHelper element config
32429      * @param {Function} handler The function called when the button is clicked
32430      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
32431      * @return {Roo.Button} The new button
32432      */
32433     addButton : function(config, handler, scope){
32434         var dh = Roo.DomHelper;
32435         if(!this.footer){
32436             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
32437         }
32438         if(!this.btnContainer){
32439             var tb = this.footer.createChild({
32440
32441                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
32442                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
32443             }, null, true);
32444             this.btnContainer = tb.firstChild.firstChild.firstChild;
32445         }
32446         var bconfig = {
32447             handler: handler,
32448             scope: scope,
32449             minWidth: this.minButtonWidth,
32450             hideParent:true
32451         };
32452         if(typeof config == "string"){
32453             bconfig.text = config;
32454         }else{
32455             if(config.tag){
32456                 bconfig.dhconfig = config;
32457             }else{
32458                 Roo.apply(bconfig, config);
32459             }
32460         }
32461         var fc = false;
32462         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
32463             bconfig.position = Math.max(0, bconfig.position);
32464             fc = this.btnContainer.childNodes[bconfig.position];
32465         }
32466          
32467         var btn = new Roo.Button(
32468             fc ? 
32469                 this.btnContainer.insertBefore(document.createElement("td"),fc)
32470                 : this.btnContainer.appendChild(document.createElement("td")),
32471             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
32472             bconfig
32473         );
32474         this.syncBodyHeight();
32475         if(!this.buttons){
32476             /**
32477              * Array of all the buttons that have been added to this dialog via addButton
32478              * @type Array
32479              */
32480             this.buttons = [];
32481         }
32482         this.buttons.push(btn);
32483         return btn;
32484     },
32485
32486     /**
32487      * Sets the default button to be focused when the dialog is displayed.
32488      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
32489      * @return {Roo.BasicDialog} this
32490      */
32491     setDefaultButton : function(btn){
32492         this.defaultButton = btn;
32493         return this;
32494     },
32495
32496     // private
32497     getHeaderFooterHeight : function(safe){
32498         var height = 0;
32499         if(this.header){
32500            height += this.header.getHeight();
32501         }
32502         if(this.footer){
32503            var fm = this.footer.getMargins();
32504             height += (this.footer.getHeight()+fm.top+fm.bottom);
32505         }
32506         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
32507         height += this.centerBg.getPadding("tb");
32508         return height;
32509     },
32510
32511     // private
32512     syncBodyHeight : function()
32513     {
32514         var bd = this.body, // the text
32515             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
32516             bw = this.bwrap;
32517         var height = this.size.height - this.getHeaderFooterHeight(false);
32518         bd.setHeight(height-bd.getMargins("tb"));
32519         var hh = this.header.getHeight();
32520         var h = this.size.height-hh;
32521         cb.setHeight(h);
32522         
32523         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
32524         bw.setHeight(h-cb.getPadding("tb"));
32525         
32526         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
32527         bd.setWidth(bw.getWidth(true));
32528         if(this.tabs){
32529             this.tabs.syncHeight();
32530             if(Roo.isIE){
32531                 this.tabs.el.repaint();
32532             }
32533         }
32534     },
32535
32536     /**
32537      * Restores the previous state of the dialog if Roo.state is configured.
32538      * @return {Roo.BasicDialog} this
32539      */
32540     restoreState : function(){
32541         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
32542         if(box && box.width){
32543             this.xy = [box.x, box.y];
32544             this.resizeTo(box.width, box.height);
32545         }
32546         return this;
32547     },
32548
32549     // private
32550     beforeShow : function(){
32551         this.expand();
32552         if(this.fixedcenter){
32553             this.xy = this.el.getCenterXY(true);
32554         }
32555         if(this.modal){
32556             Roo.get(document.body).addClass("x-body-masked");
32557             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32558             this.mask.show();
32559         }
32560         this.constrainXY();
32561     },
32562
32563     // private
32564     animShow : function(){
32565         var b = Roo.get(this.animateTarget).getBox();
32566         this.proxy.setSize(b.width, b.height);
32567         this.proxy.setLocation(b.x, b.y);
32568         this.proxy.show();
32569         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
32570                     true, .35, this.showEl.createDelegate(this));
32571     },
32572
32573     /**
32574      * Shows the dialog.
32575      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
32576      * @return {Roo.BasicDialog} this
32577      */
32578     show : function(animateTarget){
32579         if (this.fireEvent("beforeshow", this) === false){
32580             return;
32581         }
32582         if(this.syncHeightBeforeShow){
32583             this.syncBodyHeight();
32584         }else if(this.firstShow){
32585             this.firstShow = false;
32586             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
32587         }
32588         this.animateTarget = animateTarget || this.animateTarget;
32589         if(!this.el.isVisible()){
32590             this.beforeShow();
32591             if(this.animateTarget && Roo.get(this.animateTarget)){
32592                 this.animShow();
32593             }else{
32594                 this.showEl();
32595             }
32596         }
32597         return this;
32598     },
32599
32600     // private
32601     showEl : function(){
32602         this.proxy.hide();
32603         this.el.setXY(this.xy);
32604         this.el.show();
32605         this.adjustAssets(true);
32606         this.toFront();
32607         this.focus();
32608         // IE peekaboo bug - fix found by Dave Fenwick
32609         if(Roo.isIE){
32610             this.el.repaint();
32611         }
32612         this.fireEvent("show", this);
32613     },
32614
32615     /**
32616      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
32617      * dialog itself will receive focus.
32618      */
32619     focus : function(){
32620         if(this.defaultButton){
32621             this.defaultButton.focus();
32622         }else{
32623             this.focusEl.focus();
32624         }
32625     },
32626
32627     // private
32628     constrainXY : function(){
32629         if(this.constraintoviewport !== false){
32630             if(!this.viewSize){
32631                 if(this.container){
32632                     var s = this.container.getSize();
32633                     this.viewSize = [s.width, s.height];
32634                 }else{
32635                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
32636                 }
32637             }
32638             var s = Roo.get(this.container||document).getScroll();
32639
32640             var x = this.xy[0], y = this.xy[1];
32641             var w = this.size.width, h = this.size.height;
32642             var vw = this.viewSize[0], vh = this.viewSize[1];
32643             // only move it if it needs it
32644             var moved = false;
32645             // first validate right/bottom
32646             if(x + w > vw+s.left){
32647                 x = vw - w;
32648                 moved = true;
32649             }
32650             if(y + h > vh+s.top){
32651                 y = vh - h;
32652                 moved = true;
32653             }
32654             // then make sure top/left isn't negative
32655             if(x < s.left){
32656                 x = s.left;
32657                 moved = true;
32658             }
32659             if(y < s.top){
32660                 y = s.top;
32661                 moved = true;
32662             }
32663             if(moved){
32664                 // cache xy
32665                 this.xy = [x, y];
32666                 if(this.isVisible()){
32667                     this.el.setLocation(x, y);
32668                     this.adjustAssets();
32669                 }
32670             }
32671         }
32672     },
32673
32674     // private
32675     onDrag : function(){
32676         if(!this.proxyDrag){
32677             this.xy = this.el.getXY();
32678             this.adjustAssets();
32679         }
32680     },
32681
32682     // private
32683     adjustAssets : function(doShow){
32684         var x = this.xy[0], y = this.xy[1];
32685         var w = this.size.width, h = this.size.height;
32686         if(doShow === true){
32687             if(this.shadow){
32688                 this.shadow.show(this.el);
32689             }
32690             if(this.shim){
32691                 this.shim.show();
32692             }
32693         }
32694         if(this.shadow && this.shadow.isVisible()){
32695             this.shadow.show(this.el);
32696         }
32697         if(this.shim && this.shim.isVisible()){
32698             this.shim.setBounds(x, y, w, h);
32699         }
32700     },
32701
32702     // private
32703     adjustViewport : function(w, h){
32704         if(!w || !h){
32705             w = Roo.lib.Dom.getViewWidth();
32706             h = Roo.lib.Dom.getViewHeight();
32707         }
32708         // cache the size
32709         this.viewSize = [w, h];
32710         if(this.modal && this.mask.isVisible()){
32711             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
32712             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32713         }
32714         if(this.isVisible()){
32715             this.constrainXY();
32716         }
32717     },
32718
32719     /**
32720      * Destroys this dialog and all its supporting elements (including any tabs, shim,
32721      * shadow, proxy, mask, etc.)  Also removes all event listeners.
32722      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32723      */
32724     destroy : function(removeEl){
32725         if(this.isVisible()){
32726             this.animateTarget = null;
32727             this.hide();
32728         }
32729         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
32730         if(this.tabs){
32731             this.tabs.destroy(removeEl);
32732         }
32733         Roo.destroy(
32734              this.shim,
32735              this.proxy,
32736              this.resizer,
32737              this.close,
32738              this.mask
32739         );
32740         if(this.dd){
32741             this.dd.unreg();
32742         }
32743         if(this.buttons){
32744            for(var i = 0, len = this.buttons.length; i < len; i++){
32745                this.buttons[i].destroy();
32746            }
32747         }
32748         this.el.removeAllListeners();
32749         if(removeEl === true){
32750             this.el.update("");
32751             this.el.remove();
32752         }
32753         Roo.DialogManager.unregister(this);
32754     },
32755
32756     // private
32757     startMove : function(){
32758         if(this.proxyDrag){
32759             this.proxy.show();
32760         }
32761         if(this.constraintoviewport !== false){
32762             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
32763         }
32764     },
32765
32766     // private
32767     endMove : function(){
32768         if(!this.proxyDrag){
32769             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
32770         }else{
32771             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
32772             this.proxy.hide();
32773         }
32774         this.refreshSize();
32775         this.adjustAssets();
32776         this.focus();
32777         this.fireEvent("move", this, this.xy[0], this.xy[1]);
32778     },
32779
32780     /**
32781      * Brings this dialog to the front of any other visible dialogs
32782      * @return {Roo.BasicDialog} this
32783      */
32784     toFront : function(){
32785         Roo.DialogManager.bringToFront(this);
32786         return this;
32787     },
32788
32789     /**
32790      * Sends this dialog to the back (under) of any other visible dialogs
32791      * @return {Roo.BasicDialog} this
32792      */
32793     toBack : function(){
32794         Roo.DialogManager.sendToBack(this);
32795         return this;
32796     },
32797
32798     /**
32799      * Centers this dialog in the viewport
32800      * @return {Roo.BasicDialog} this
32801      */
32802     center : function(){
32803         var xy = this.el.getCenterXY(true);
32804         this.moveTo(xy[0], xy[1]);
32805         return this;
32806     },
32807
32808     /**
32809      * Moves the dialog's top-left corner to the specified point
32810      * @param {Number} x
32811      * @param {Number} y
32812      * @return {Roo.BasicDialog} this
32813      */
32814     moveTo : function(x, y){
32815         this.xy = [x,y];
32816         if(this.isVisible()){
32817             this.el.setXY(this.xy);
32818             this.adjustAssets();
32819         }
32820         return this;
32821     },
32822
32823     /**
32824      * Aligns the dialog to the specified element
32825      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32826      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
32827      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32828      * @return {Roo.BasicDialog} this
32829      */
32830     alignTo : function(element, position, offsets){
32831         this.xy = this.el.getAlignToXY(element, position, offsets);
32832         if(this.isVisible()){
32833             this.el.setXY(this.xy);
32834             this.adjustAssets();
32835         }
32836         return this;
32837     },
32838
32839     /**
32840      * Anchors an element to another element and realigns it when the window is resized.
32841      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32842      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
32843      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32844      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
32845      * is a number, it is used as the buffer delay (defaults to 50ms).
32846      * @return {Roo.BasicDialog} this
32847      */
32848     anchorTo : function(el, alignment, offsets, monitorScroll){
32849         var action = function(){
32850             this.alignTo(el, alignment, offsets);
32851         };
32852         Roo.EventManager.onWindowResize(action, this);
32853         var tm = typeof monitorScroll;
32854         if(tm != 'undefined'){
32855             Roo.EventManager.on(window, 'scroll', action, this,
32856                 {buffer: tm == 'number' ? monitorScroll : 50});
32857         }
32858         action.call(this);
32859         return this;
32860     },
32861
32862     /**
32863      * Returns true if the dialog is visible
32864      * @return {Boolean}
32865      */
32866     isVisible : function(){
32867         return this.el.isVisible();
32868     },
32869
32870     // private
32871     animHide : function(callback){
32872         var b = Roo.get(this.animateTarget).getBox();
32873         this.proxy.show();
32874         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
32875         this.el.hide();
32876         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
32877                     this.hideEl.createDelegate(this, [callback]));
32878     },
32879
32880     /**
32881      * Hides the dialog.
32882      * @param {Function} callback (optional) Function to call when the dialog is hidden
32883      * @return {Roo.BasicDialog} this
32884      */
32885     hide : function(callback){
32886         if (this.fireEvent("beforehide", this) === false){
32887             return;
32888         }
32889         if(this.shadow){
32890             this.shadow.hide();
32891         }
32892         if(this.shim) {
32893           this.shim.hide();
32894         }
32895         // sometimes animateTarget seems to get set.. causing problems...
32896         // this just double checks..
32897         if(this.animateTarget && Roo.get(this.animateTarget)) {
32898            this.animHide(callback);
32899         }else{
32900             this.el.hide();
32901             this.hideEl(callback);
32902         }
32903         return this;
32904     },
32905
32906     // private
32907     hideEl : function(callback){
32908         this.proxy.hide();
32909         if(this.modal){
32910             this.mask.hide();
32911             Roo.get(document.body).removeClass("x-body-masked");
32912         }
32913         this.fireEvent("hide", this);
32914         if(typeof callback == "function"){
32915             callback();
32916         }
32917     },
32918
32919     // private
32920     hideAction : function(){
32921         this.setLeft("-10000px");
32922         this.setTop("-10000px");
32923         this.setStyle("visibility", "hidden");
32924     },
32925
32926     // private
32927     refreshSize : function(){
32928         this.size = this.el.getSize();
32929         this.xy = this.el.getXY();
32930         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
32931     },
32932
32933     // private
32934     // z-index is managed by the DialogManager and may be overwritten at any time
32935     setZIndex : function(index){
32936         if(this.modal){
32937             this.mask.setStyle("z-index", index);
32938         }
32939         if(this.shim){
32940             this.shim.setStyle("z-index", ++index);
32941         }
32942         if(this.shadow){
32943             this.shadow.setZIndex(++index);
32944         }
32945         this.el.setStyle("z-index", ++index);
32946         if(this.proxy){
32947             this.proxy.setStyle("z-index", ++index);
32948         }
32949         if(this.resizer){
32950             this.resizer.proxy.setStyle("z-index", ++index);
32951         }
32952
32953         this.lastZIndex = index;
32954     },
32955
32956     /**
32957      * Returns the element for this dialog
32958      * @return {Roo.Element} The underlying dialog Element
32959      */
32960     getEl : function(){
32961         return this.el;
32962     }
32963 });
32964
32965 /**
32966  * @class Roo.DialogManager
32967  * Provides global access to BasicDialogs that have been created and
32968  * support for z-indexing (layering) multiple open dialogs.
32969  */
32970 Roo.DialogManager = function(){
32971     var list = {};
32972     var accessList = [];
32973     var front = null;
32974
32975     // private
32976     var sortDialogs = function(d1, d2){
32977         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
32978     };
32979
32980     // private
32981     var orderDialogs = function(){
32982         accessList.sort(sortDialogs);
32983         var seed = Roo.DialogManager.zseed;
32984         for(var i = 0, len = accessList.length; i < len; i++){
32985             var dlg = accessList[i];
32986             if(dlg){
32987                 dlg.setZIndex(seed + (i*10));
32988             }
32989         }
32990     };
32991
32992     return {
32993         /**
32994          * The starting z-index for BasicDialogs (defaults to 9000)
32995          * @type Number The z-index value
32996          */
32997         zseed : 9000,
32998
32999         // private
33000         register : function(dlg){
33001             list[dlg.id] = dlg;
33002             accessList.push(dlg);
33003         },
33004
33005         // private
33006         unregister : function(dlg){
33007             delete list[dlg.id];
33008             var i=0;
33009             var len=0;
33010             if(!accessList.indexOf){
33011                 for(  i = 0, len = accessList.length; i < len; i++){
33012                     if(accessList[i] == dlg){
33013                         accessList.splice(i, 1);
33014                         return;
33015                     }
33016                 }
33017             }else{
33018                  i = accessList.indexOf(dlg);
33019                 if(i != -1){
33020                     accessList.splice(i, 1);
33021                 }
33022             }
33023         },
33024
33025         /**
33026          * Gets a registered dialog by id
33027          * @param {String/Object} id The id of the dialog or a dialog
33028          * @return {Roo.BasicDialog} this
33029          */
33030         get : function(id){
33031             return typeof id == "object" ? id : list[id];
33032         },
33033
33034         /**
33035          * Brings the specified dialog to the front
33036          * @param {String/Object} dlg The id of the dialog or a dialog
33037          * @return {Roo.BasicDialog} this
33038          */
33039         bringToFront : function(dlg){
33040             dlg = this.get(dlg);
33041             if(dlg != front){
33042                 front = dlg;
33043                 dlg._lastAccess = new Date().getTime();
33044                 orderDialogs();
33045             }
33046             return dlg;
33047         },
33048
33049         /**
33050          * Sends the specified dialog to the back
33051          * @param {String/Object} dlg The id of the dialog or a dialog
33052          * @return {Roo.BasicDialog} this
33053          */
33054         sendToBack : function(dlg){
33055             dlg = this.get(dlg);
33056             dlg._lastAccess = -(new Date().getTime());
33057             orderDialogs();
33058             return dlg;
33059         },
33060
33061         /**
33062          * Hides all dialogs
33063          */
33064         hideAll : function(){
33065             for(var id in list){
33066                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
33067                     list[id].hide();
33068                 }
33069             }
33070         }
33071     };
33072 }();
33073
33074 /**
33075  * @class Roo.LayoutDialog
33076  * @extends Roo.BasicDialog
33077  * Dialog which provides adjustments for working with a layout in a Dialog.
33078  * Add your necessary layout config options to the dialog's config.<br>
33079  * Example usage (including a nested layout):
33080  * <pre><code>
33081 if(!dialog){
33082     dialog = new Roo.LayoutDialog("download-dlg", {
33083         modal: true,
33084         width:600,
33085         height:450,
33086         shadow:true,
33087         minWidth:500,
33088         minHeight:350,
33089         autoTabs:true,
33090         proxyDrag:true,
33091         // layout config merges with the dialog config
33092         center:{
33093             tabPosition: "top",
33094             alwaysShowTabs: true
33095         }
33096     });
33097     dialog.addKeyListener(27, dialog.hide, dialog);
33098     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
33099     dialog.addButton("Build It!", this.getDownload, this);
33100
33101     // we can even add nested layouts
33102     var innerLayout = new Roo.BorderLayout("dl-inner", {
33103         east: {
33104             initialSize: 200,
33105             autoScroll:true,
33106             split:true
33107         },
33108         center: {
33109             autoScroll:true
33110         }
33111     });
33112     innerLayout.beginUpdate();
33113     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
33114     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
33115     innerLayout.endUpdate(true);
33116
33117     var layout = dialog.getLayout();
33118     layout.beginUpdate();
33119     layout.add("center", new Roo.ContentPanel("standard-panel",
33120                         {title: "Download the Source", fitToFrame:true}));
33121     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
33122                {title: "Build your own roo.js"}));
33123     layout.getRegion("center").showPanel(sp);
33124     layout.endUpdate();
33125 }
33126 </code></pre>
33127     * @constructor
33128     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
33129     * @param {Object} config configuration options
33130   */
33131 Roo.LayoutDialog = function(el, cfg){
33132     
33133     var config=  cfg;
33134     if (typeof(cfg) == 'undefined') {
33135         config = Roo.apply({}, el);
33136         // not sure why we use documentElement here.. - it should always be body.
33137         // IE7 borks horribly if we use documentElement.
33138         // webkit also does not like documentElement - it creates a body element...
33139         el = Roo.get( document.body || document.documentElement ).createChild();
33140         //config.autoCreate = true;
33141     }
33142     
33143     
33144     config.autoTabs = false;
33145     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
33146     this.body.setStyle({overflow:"hidden", position:"relative"});
33147     this.layout = new Roo.BorderLayout(this.body.dom, config);
33148     this.layout.monitorWindowResize = false;
33149     this.el.addClass("x-dlg-auto-layout");
33150     // fix case when center region overwrites center function
33151     this.center = Roo.BasicDialog.prototype.center;
33152     this.on("show", this.layout.layout, this.layout, true);
33153     if (config.items) {
33154         var xitems = config.items;
33155         delete config.items;
33156         Roo.each(xitems, this.addxtype, this);
33157     }
33158     
33159     
33160 };
33161 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
33162     /**
33163      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
33164      * @deprecated
33165      */
33166     endUpdate : function(){
33167         this.layout.endUpdate();
33168     },
33169
33170     /**
33171      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
33172      *  @deprecated
33173      */
33174     beginUpdate : function(){
33175         this.layout.beginUpdate();
33176     },
33177
33178     /**
33179      * Get the BorderLayout for this dialog
33180      * @return {Roo.BorderLayout}
33181      */
33182     getLayout : function(){
33183         return this.layout;
33184     },
33185
33186     showEl : function(){
33187         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
33188         if(Roo.isIE7){
33189             this.layout.layout();
33190         }
33191     },
33192
33193     // private
33194     // Use the syncHeightBeforeShow config option to control this automatically
33195     syncBodyHeight : function(){
33196         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
33197         if(this.layout){this.layout.layout();}
33198     },
33199     
33200       /**
33201      * Add an xtype element (actually adds to the layout.)
33202      * @return {Object} xdata xtype object data.
33203      */
33204     
33205     addxtype : function(c) {
33206         return this.layout.addxtype(c);
33207     }
33208 });/*
33209  * Based on:
33210  * Ext JS Library 1.1.1
33211  * Copyright(c) 2006-2007, Ext JS, LLC.
33212  *
33213  * Originally Released Under LGPL - original licence link has changed is not relivant.
33214  *
33215  * Fork - LGPL
33216  * <script type="text/javascript">
33217  */
33218  
33219 /**
33220  * @class Roo.MessageBox
33221  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
33222  * Example usage:
33223  *<pre><code>
33224 // Basic alert:
33225 Roo.Msg.alert('Status', 'Changes saved successfully.');
33226
33227 // Prompt for user data:
33228 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
33229     if (btn == 'ok'){
33230         // process text value...
33231     }
33232 });
33233
33234 // Show a dialog using config options:
33235 Roo.Msg.show({
33236    title:'Save Changes?',
33237    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
33238    buttons: Roo.Msg.YESNOCANCEL,
33239    fn: processResult,
33240    animEl: 'elId'
33241 });
33242 </code></pre>
33243  * @singleton
33244  */
33245 Roo.MessageBox = function(){
33246     var dlg, opt, mask, waitTimer;
33247     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
33248     var buttons, activeTextEl, bwidth;
33249
33250     // private
33251     var handleButton = function(button){
33252         dlg.hide();
33253         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
33254     };
33255
33256     // private
33257     var handleHide = function(){
33258         if(opt && opt.cls){
33259             dlg.el.removeClass(opt.cls);
33260         }
33261         if(waitTimer){
33262             Roo.TaskMgr.stop(waitTimer);
33263             waitTimer = null;
33264         }
33265     };
33266
33267     // private
33268     var updateButtons = function(b){
33269         var width = 0;
33270         if(!b){
33271             buttons["ok"].hide();
33272             buttons["cancel"].hide();
33273             buttons["yes"].hide();
33274             buttons["no"].hide();
33275             dlg.footer.dom.style.display = 'none';
33276             return width;
33277         }
33278         dlg.footer.dom.style.display = '';
33279         for(var k in buttons){
33280             if(typeof buttons[k] != "function"){
33281                 if(b[k]){
33282                     buttons[k].show();
33283                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
33284                     width += buttons[k].el.getWidth()+15;
33285                 }else{
33286                     buttons[k].hide();
33287                 }
33288             }
33289         }
33290         return width;
33291     };
33292
33293     // private
33294     var handleEsc = function(d, k, e){
33295         if(opt && opt.closable !== false){
33296             dlg.hide();
33297         }
33298         if(e){
33299             e.stopEvent();
33300         }
33301     };
33302
33303     return {
33304         /**
33305          * Returns a reference to the underlying {@link Roo.BasicDialog} element
33306          * @return {Roo.BasicDialog} The BasicDialog element
33307          */
33308         getDialog : function(){
33309            if(!dlg){
33310                 dlg = new Roo.BasicDialog("x-msg-box", {
33311                     autoCreate : true,
33312                     shadow: true,
33313                     draggable: true,
33314                     resizable:false,
33315                     constraintoviewport:false,
33316                     fixedcenter:true,
33317                     collapsible : false,
33318                     shim:true,
33319                     modal: true,
33320                     width:400, height:100,
33321                     buttonAlign:"center",
33322                     closeClick : function(){
33323                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
33324                             handleButton("no");
33325                         }else{
33326                             handleButton("cancel");
33327                         }
33328                     }
33329                 });
33330                 dlg.on("hide", handleHide);
33331                 mask = dlg.mask;
33332                 dlg.addKeyListener(27, handleEsc);
33333                 buttons = {};
33334                 var bt = this.buttonText;
33335                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
33336                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
33337                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
33338                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
33339                 bodyEl = dlg.body.createChild({
33340
33341                     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>'
33342                 });
33343                 msgEl = bodyEl.dom.firstChild;
33344                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
33345                 textboxEl.enableDisplayMode();
33346                 textboxEl.addKeyListener([10,13], function(){
33347                     if(dlg.isVisible() && opt && opt.buttons){
33348                         if(opt.buttons.ok){
33349                             handleButton("ok");
33350                         }else if(opt.buttons.yes){
33351                             handleButton("yes");
33352                         }
33353                     }
33354                 });
33355                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
33356                 textareaEl.enableDisplayMode();
33357                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
33358                 progressEl.enableDisplayMode();
33359                 var pf = progressEl.dom.firstChild;
33360                 if (pf) {
33361                     pp = Roo.get(pf.firstChild);
33362                     pp.setHeight(pf.offsetHeight);
33363                 }
33364                 
33365             }
33366             return dlg;
33367         },
33368
33369         /**
33370          * Updates the message box body text
33371          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
33372          * the XHTML-compliant non-breaking space character '&amp;#160;')
33373          * @return {Roo.MessageBox} This message box
33374          */
33375         updateText : function(text){
33376             if(!dlg.isVisible() && !opt.width){
33377                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
33378             }
33379             msgEl.innerHTML = text || '&#160;';
33380       
33381             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
33382             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
33383             var w = Math.max(
33384                     Math.min(opt.width || cw , this.maxWidth), 
33385                     Math.max(opt.minWidth || this.minWidth, bwidth)
33386             );
33387             if(opt.prompt){
33388                 activeTextEl.setWidth(w);
33389             }
33390             if(dlg.isVisible()){
33391                 dlg.fixedcenter = false;
33392             }
33393             // to big, make it scroll. = But as usual stupid IE does not support
33394             // !important..
33395             
33396             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
33397                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
33398                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
33399             } else {
33400                 bodyEl.dom.style.height = '';
33401                 bodyEl.dom.style.overflowY = '';
33402             }
33403             if (cw > w) {
33404                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
33405             } else {
33406                 bodyEl.dom.style.overflowX = '';
33407             }
33408             
33409             dlg.setContentSize(w, bodyEl.getHeight());
33410             if(dlg.isVisible()){
33411                 dlg.fixedcenter = true;
33412             }
33413             return this;
33414         },
33415
33416         /**
33417          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
33418          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
33419          * @param {Number} value Any number between 0 and 1 (e.g., .5)
33420          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
33421          * @return {Roo.MessageBox} This message box
33422          */
33423         updateProgress : function(value, text){
33424             if(text){
33425                 this.updateText(text);
33426             }
33427             if (pp) { // weird bug on my firefox - for some reason this is not defined
33428                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
33429             }
33430             return this;
33431         },        
33432
33433         /**
33434          * Returns true if the message box is currently displayed
33435          * @return {Boolean} True if the message box is visible, else false
33436          */
33437         isVisible : function(){
33438             return dlg && dlg.isVisible();  
33439         },
33440
33441         /**
33442          * Hides the message box if it is displayed
33443          */
33444         hide : function(){
33445             if(this.isVisible()){
33446                 dlg.hide();
33447             }  
33448         },
33449
33450         /**
33451          * Displays a new message box, or reinitializes an existing message box, based on the config options
33452          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
33453          * The following config object properties are supported:
33454          * <pre>
33455 Property    Type             Description
33456 ----------  ---------------  ------------------------------------------------------------------------------------
33457 animEl            String/Element   An id or Element from which the message box should animate as it opens and
33458                                    closes (defaults to undefined)
33459 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
33460                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
33461 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
33462                                    progress and wait dialogs will ignore this property and always hide the
33463                                    close button as they can only be closed programmatically.
33464 cls               String           A custom CSS class to apply to the message box element
33465 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
33466                                    displayed (defaults to 75)
33467 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
33468                                    function will be btn (the name of the button that was clicked, if applicable,
33469                                    e.g. "ok"), and text (the value of the active text field, if applicable).
33470                                    Progress and wait dialogs will ignore this option since they do not respond to
33471                                    user actions and can only be closed programmatically, so any required function
33472                                    should be called by the same code after it closes the dialog.
33473 icon              String           A CSS class that provides a background image to be used as an icon for
33474                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
33475 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
33476 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
33477 modal             Boolean          False to allow user interaction with the page while the message box is
33478                                    displayed (defaults to true)
33479 msg               String           A string that will replace the existing message box body text (defaults
33480                                    to the XHTML-compliant non-breaking space character '&#160;')
33481 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
33482 progress          Boolean          True to display a progress bar (defaults to false)
33483 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
33484 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
33485 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
33486 title             String           The title text
33487 value             String           The string value to set into the active textbox element if displayed
33488 wait              Boolean          True to display a progress bar (defaults to false)
33489 width             Number           The width of the dialog in pixels
33490 </pre>
33491          *
33492          * Example usage:
33493          * <pre><code>
33494 Roo.Msg.show({
33495    title: 'Address',
33496    msg: 'Please enter your address:',
33497    width: 300,
33498    buttons: Roo.MessageBox.OKCANCEL,
33499    multiline: true,
33500    fn: saveAddress,
33501    animEl: 'addAddressBtn'
33502 });
33503 </code></pre>
33504          * @param {Object} config Configuration options
33505          * @return {Roo.MessageBox} This message box
33506          */
33507         show : function(options)
33508         {
33509             
33510             // this causes nightmares if you show one dialog after another
33511             // especially on callbacks..
33512              
33513             if(this.isVisible()){
33514                 
33515                 this.hide();
33516                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
33517                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
33518                 Roo.log("New Dialog Message:" +  options.msg )
33519                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
33520                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
33521                 
33522             }
33523             var d = this.getDialog();
33524             opt = options;
33525             d.setTitle(opt.title || "&#160;");
33526             d.close.setDisplayed(opt.closable !== false);
33527             activeTextEl = textboxEl;
33528             opt.prompt = opt.prompt || (opt.multiline ? true : false);
33529             if(opt.prompt){
33530                 if(opt.multiline){
33531                     textboxEl.hide();
33532                     textareaEl.show();
33533                     textareaEl.setHeight(typeof opt.multiline == "number" ?
33534                         opt.multiline : this.defaultTextHeight);
33535                     activeTextEl = textareaEl;
33536                 }else{
33537                     textboxEl.show();
33538                     textareaEl.hide();
33539                 }
33540             }else{
33541                 textboxEl.hide();
33542                 textareaEl.hide();
33543             }
33544             progressEl.setDisplayed(opt.progress === true);
33545             this.updateProgress(0);
33546             activeTextEl.dom.value = opt.value || "";
33547             if(opt.prompt){
33548                 dlg.setDefaultButton(activeTextEl);
33549             }else{
33550                 var bs = opt.buttons;
33551                 var db = null;
33552                 if(bs && bs.ok){
33553                     db = buttons["ok"];
33554                 }else if(bs && bs.yes){
33555                     db = buttons["yes"];
33556                 }
33557                 dlg.setDefaultButton(db);
33558             }
33559             bwidth = updateButtons(opt.buttons);
33560             this.updateText(opt.msg);
33561             if(opt.cls){
33562                 d.el.addClass(opt.cls);
33563             }
33564             d.proxyDrag = opt.proxyDrag === true;
33565             d.modal = opt.modal !== false;
33566             d.mask = opt.modal !== false ? mask : false;
33567             if(!d.isVisible()){
33568                 // force it to the end of the z-index stack so it gets a cursor in FF
33569                 document.body.appendChild(dlg.el.dom);
33570                 d.animateTarget = null;
33571                 d.show(options.animEl);
33572             }
33573             return this;
33574         },
33575
33576         /**
33577          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
33578          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
33579          * and closing the message box when the process is complete.
33580          * @param {String} title The title bar text
33581          * @param {String} msg The message box body text
33582          * @return {Roo.MessageBox} This message box
33583          */
33584         progress : function(title, msg){
33585             this.show({
33586                 title : title,
33587                 msg : msg,
33588                 buttons: false,
33589                 progress:true,
33590                 closable:false,
33591                 minWidth: this.minProgressWidth,
33592                 modal : true
33593             });
33594             return this;
33595         },
33596
33597         /**
33598          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
33599          * If a callback function is passed it will be called after the user clicks the button, and the
33600          * id of the button that was clicked will be passed as the only parameter to the callback
33601          * (could also be the top-right close button).
33602          * @param {String} title The title bar text
33603          * @param {String} msg The message box body text
33604          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33605          * @param {Object} scope (optional) The scope of the callback function
33606          * @return {Roo.MessageBox} This message box
33607          */
33608         alert : function(title, msg, fn, scope){
33609             this.show({
33610                 title : title,
33611                 msg : msg,
33612                 buttons: this.OK,
33613                 fn: fn,
33614                 scope : scope,
33615                 modal : true
33616             });
33617             return this;
33618         },
33619
33620         /**
33621          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
33622          * interaction while waiting for a long-running process to complete that does not have defined intervals.
33623          * You are responsible for closing the message box when the process is complete.
33624          * @param {String} msg The message box body text
33625          * @param {String} title (optional) The title bar text
33626          * @return {Roo.MessageBox} This message box
33627          */
33628         wait : function(msg, title){
33629             this.show({
33630                 title : title,
33631                 msg : msg,
33632                 buttons: false,
33633                 closable:false,
33634                 progress:true,
33635                 modal:true,
33636                 width:300,
33637                 wait:true
33638             });
33639             waitTimer = Roo.TaskMgr.start({
33640                 run: function(i){
33641                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
33642                 },
33643                 interval: 1000
33644             });
33645             return this;
33646         },
33647
33648         /**
33649          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
33650          * If a callback function is passed it will be called after the user clicks either button, and the id of the
33651          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
33652          * @param {String} title The title bar text
33653          * @param {String} msg The message box body text
33654          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33655          * @param {Object} scope (optional) The scope of the callback function
33656          * @return {Roo.MessageBox} This message box
33657          */
33658         confirm : function(title, msg, fn, scope){
33659             this.show({
33660                 title : title,
33661                 msg : msg,
33662                 buttons: this.YESNO,
33663                 fn: fn,
33664                 scope : scope,
33665                 modal : true
33666             });
33667             return this;
33668         },
33669
33670         /**
33671          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
33672          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
33673          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
33674          * (could also be the top-right close button) and the text that was entered will be passed as the two
33675          * parameters to the callback.
33676          * @param {String} title The title bar text
33677          * @param {String} msg The message box body text
33678          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33679          * @param {Object} scope (optional) The scope of the callback function
33680          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
33681          * property, or the height in pixels to create the textbox (defaults to false / single-line)
33682          * @return {Roo.MessageBox} This message box
33683          */
33684         prompt : function(title, msg, fn, scope, multiline){
33685             this.show({
33686                 title : title,
33687                 msg : msg,
33688                 buttons: this.OKCANCEL,
33689                 fn: fn,
33690                 minWidth:250,
33691                 scope : scope,
33692                 prompt:true,
33693                 multiline: multiline,
33694                 modal : true
33695             });
33696             return this;
33697         },
33698
33699         /**
33700          * Button config that displays a single OK button
33701          * @type Object
33702          */
33703         OK : {ok:true},
33704         /**
33705          * Button config that displays Yes and No buttons
33706          * @type Object
33707          */
33708         YESNO : {yes:true, no:true},
33709         /**
33710          * Button config that displays OK and Cancel buttons
33711          * @type Object
33712          */
33713         OKCANCEL : {ok:true, cancel:true},
33714         /**
33715          * Button config that displays Yes, No and Cancel buttons
33716          * @type Object
33717          */
33718         YESNOCANCEL : {yes:true, no:true, cancel:true},
33719
33720         /**
33721          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
33722          * @type Number
33723          */
33724         defaultTextHeight : 75,
33725         /**
33726          * The maximum width in pixels of the message box (defaults to 600)
33727          * @type Number
33728          */
33729         maxWidth : 600,
33730         /**
33731          * The minimum width in pixels of the message box (defaults to 100)
33732          * @type Number
33733          */
33734         minWidth : 100,
33735         /**
33736          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
33737          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
33738          * @type Number
33739          */
33740         minProgressWidth : 250,
33741         /**
33742          * An object containing the default button text strings that can be overriden for localized language support.
33743          * Supported properties are: ok, cancel, yes and no.
33744          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
33745          * @type Object
33746          */
33747         buttonText : {
33748             ok : "OK",
33749             cancel : "Cancel",
33750             yes : "Yes",
33751             no : "No"
33752         }
33753     };
33754 }();
33755
33756 /**
33757  * Shorthand for {@link Roo.MessageBox}
33758  */
33759 Roo.Msg = Roo.MessageBox;/*
33760  * Based on:
33761  * Ext JS Library 1.1.1
33762  * Copyright(c) 2006-2007, Ext JS, LLC.
33763  *
33764  * Originally Released Under LGPL - original licence link has changed is not relivant.
33765  *
33766  * Fork - LGPL
33767  * <script type="text/javascript">
33768  */
33769 /**
33770  * @class Roo.QuickTips
33771  * Provides attractive and customizable tooltips for any element.
33772  * @singleton
33773  */
33774 Roo.QuickTips = function(){
33775     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
33776     var ce, bd, xy, dd;
33777     var visible = false, disabled = true, inited = false;
33778     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
33779     
33780     var onOver = function(e){
33781         if(disabled){
33782             return;
33783         }
33784         var t = e.getTarget();
33785         if(!t || t.nodeType !== 1 || t == document || t == document.body){
33786             return;
33787         }
33788         if(ce && t == ce.el){
33789             clearTimeout(hideProc);
33790             return;
33791         }
33792         if(t && tagEls[t.id]){
33793             tagEls[t.id].el = t;
33794             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
33795             return;
33796         }
33797         var ttp, et = Roo.fly(t);
33798         var ns = cfg.namespace;
33799         if(tm.interceptTitles && t.title){
33800             ttp = t.title;
33801             t.qtip = ttp;
33802             t.removeAttribute("title");
33803             e.preventDefault();
33804         }else{
33805             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
33806         }
33807         if(ttp){
33808             showProc = show.defer(tm.showDelay, tm, [{
33809                 el: t, 
33810                 text: ttp.replace(/\\n/g,'<br/>'),
33811                 width: et.getAttributeNS(ns, cfg.width),
33812                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
33813                 title: et.getAttributeNS(ns, cfg.title),
33814                     cls: et.getAttributeNS(ns, cfg.cls)
33815             }]);
33816         }
33817     };
33818     
33819     var onOut = function(e){
33820         clearTimeout(showProc);
33821         var t = e.getTarget();
33822         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
33823             hideProc = setTimeout(hide, tm.hideDelay);
33824         }
33825     };
33826     
33827     var onMove = function(e){
33828         if(disabled){
33829             return;
33830         }
33831         xy = e.getXY();
33832         xy[1] += 18;
33833         if(tm.trackMouse && ce){
33834             el.setXY(xy);
33835         }
33836     };
33837     
33838     var onDown = function(e){
33839         clearTimeout(showProc);
33840         clearTimeout(hideProc);
33841         if(!e.within(el)){
33842             if(tm.hideOnClick){
33843                 hide();
33844                 tm.disable();
33845                 tm.enable.defer(100, tm);
33846             }
33847         }
33848     };
33849     
33850     var getPad = function(){
33851         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
33852     };
33853
33854     var show = function(o){
33855         if(disabled){
33856             return;
33857         }
33858         clearTimeout(dismissProc);
33859         ce = o;
33860         if(removeCls){ // in case manually hidden
33861             el.removeClass(removeCls);
33862             removeCls = null;
33863         }
33864         if(ce.cls){
33865             el.addClass(ce.cls);
33866             removeCls = ce.cls;
33867         }
33868         if(ce.title){
33869             tipTitle.update(ce.title);
33870             tipTitle.show();
33871         }else{
33872             tipTitle.update('');
33873             tipTitle.hide();
33874         }
33875         el.dom.style.width  = tm.maxWidth+'px';
33876         //tipBody.dom.style.width = '';
33877         tipBodyText.update(o.text);
33878         var p = getPad(), w = ce.width;
33879         if(!w){
33880             var td = tipBodyText.dom;
33881             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
33882             if(aw > tm.maxWidth){
33883                 w = tm.maxWidth;
33884             }else if(aw < tm.minWidth){
33885                 w = tm.minWidth;
33886             }else{
33887                 w = aw;
33888             }
33889         }
33890         //tipBody.setWidth(w);
33891         el.setWidth(parseInt(w, 10) + p);
33892         if(ce.autoHide === false){
33893             close.setDisplayed(true);
33894             if(dd){
33895                 dd.unlock();
33896             }
33897         }else{
33898             close.setDisplayed(false);
33899             if(dd){
33900                 dd.lock();
33901             }
33902         }
33903         if(xy){
33904             el.avoidY = xy[1]-18;
33905             el.setXY(xy);
33906         }
33907         if(tm.animate){
33908             el.setOpacity(.1);
33909             el.setStyle("visibility", "visible");
33910             el.fadeIn({callback: afterShow});
33911         }else{
33912             afterShow();
33913         }
33914     };
33915     
33916     var afterShow = function(){
33917         if(ce){
33918             el.show();
33919             esc.enable();
33920             if(tm.autoDismiss && ce.autoHide !== false){
33921                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
33922             }
33923         }
33924     };
33925     
33926     var hide = function(noanim){
33927         clearTimeout(dismissProc);
33928         clearTimeout(hideProc);
33929         ce = null;
33930         if(el.isVisible()){
33931             esc.disable();
33932             if(noanim !== true && tm.animate){
33933                 el.fadeOut({callback: afterHide});
33934             }else{
33935                 afterHide();
33936             } 
33937         }
33938     };
33939     
33940     var afterHide = function(){
33941         el.hide();
33942         if(removeCls){
33943             el.removeClass(removeCls);
33944             removeCls = null;
33945         }
33946     };
33947     
33948     return {
33949         /**
33950         * @cfg {Number} minWidth
33951         * The minimum width of the quick tip (defaults to 40)
33952         */
33953        minWidth : 40,
33954         /**
33955         * @cfg {Number} maxWidth
33956         * The maximum width of the quick tip (defaults to 300)
33957         */
33958        maxWidth : 300,
33959         /**
33960         * @cfg {Boolean} interceptTitles
33961         * True to automatically use the element's DOM title value if available (defaults to false)
33962         */
33963        interceptTitles : false,
33964         /**
33965         * @cfg {Boolean} trackMouse
33966         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
33967         */
33968        trackMouse : false,
33969         /**
33970         * @cfg {Boolean} hideOnClick
33971         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
33972         */
33973        hideOnClick : true,
33974         /**
33975         * @cfg {Number} showDelay
33976         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
33977         */
33978        showDelay : 500,
33979         /**
33980         * @cfg {Number} hideDelay
33981         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
33982         */
33983        hideDelay : 200,
33984         /**
33985         * @cfg {Boolean} autoHide
33986         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
33987         * Used in conjunction with hideDelay.
33988         */
33989        autoHide : true,
33990         /**
33991         * @cfg {Boolean}
33992         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
33993         * (defaults to true).  Used in conjunction with autoDismissDelay.
33994         */
33995        autoDismiss : true,
33996         /**
33997         * @cfg {Number}
33998         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
33999         */
34000        autoDismissDelay : 5000,
34001        /**
34002         * @cfg {Boolean} animate
34003         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
34004         */
34005        animate : false,
34006
34007        /**
34008         * @cfg {String} title
34009         * Title text to display (defaults to '').  This can be any valid HTML markup.
34010         */
34011         title: '',
34012        /**
34013         * @cfg {String} text
34014         * Body text to display (defaults to '').  This can be any valid HTML markup.
34015         */
34016         text : '',
34017        /**
34018         * @cfg {String} cls
34019         * A CSS class to apply to the base quick tip element (defaults to '').
34020         */
34021         cls : '',
34022        /**
34023         * @cfg {Number} width
34024         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
34025         * minWidth or maxWidth.
34026         */
34027         width : null,
34028
34029     /**
34030      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
34031      * or display QuickTips in a page.
34032      */
34033        init : function(){
34034           tm = Roo.QuickTips;
34035           cfg = tm.tagConfig;
34036           if(!inited){
34037               if(!Roo.isReady){ // allow calling of init() before onReady
34038                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
34039                   return;
34040               }
34041               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
34042               el.fxDefaults = {stopFx: true};
34043               // maximum custom styling
34044               //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>');
34045               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>');              
34046               tipTitle = el.child('h3');
34047               tipTitle.enableDisplayMode("block");
34048               tipBody = el.child('div.x-tip-bd');
34049               tipBodyText = el.child('div.x-tip-bd-inner');
34050               //bdLeft = el.child('div.x-tip-bd-left');
34051               //bdRight = el.child('div.x-tip-bd-right');
34052               close = el.child('div.x-tip-close');
34053               close.enableDisplayMode("block");
34054               close.on("click", hide);
34055               var d = Roo.get(document);
34056               d.on("mousedown", onDown);
34057               d.on("mouseover", onOver);
34058               d.on("mouseout", onOut);
34059               d.on("mousemove", onMove);
34060               esc = d.addKeyListener(27, hide);
34061               esc.disable();
34062               if(Roo.dd.DD){
34063                   dd = el.initDD("default", null, {
34064                       onDrag : function(){
34065                           el.sync();  
34066                       }
34067                   });
34068                   dd.setHandleElId(tipTitle.id);
34069                   dd.lock();
34070               }
34071               inited = true;
34072           }
34073           this.enable(); 
34074        },
34075
34076     /**
34077      * Configures a new quick tip instance and assigns it to a target element.  The following config options
34078      * are supported:
34079      * <pre>
34080 Property    Type                   Description
34081 ----------  ---------------------  ------------------------------------------------------------------------
34082 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
34083      * </ul>
34084      * @param {Object} config The config object
34085      */
34086        register : function(config){
34087            var cs = config instanceof Array ? config : arguments;
34088            for(var i = 0, len = cs.length; i < len; i++) {
34089                var c = cs[i];
34090                var target = c.target;
34091                if(target){
34092                    if(target instanceof Array){
34093                        for(var j = 0, jlen = target.length; j < jlen; j++){
34094                            tagEls[target[j]] = c;
34095                        }
34096                    }else{
34097                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
34098                    }
34099                }
34100            }
34101        },
34102
34103     /**
34104      * Removes this quick tip from its element and destroys it.
34105      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
34106      */
34107        unregister : function(el){
34108            delete tagEls[Roo.id(el)];
34109        },
34110
34111     /**
34112      * Enable this quick tip.
34113      */
34114        enable : function(){
34115            if(inited && disabled){
34116                locks.pop();
34117                if(locks.length < 1){
34118                    disabled = false;
34119                }
34120            }
34121        },
34122
34123     /**
34124      * Disable this quick tip.
34125      */
34126        disable : function(){
34127           disabled = true;
34128           clearTimeout(showProc);
34129           clearTimeout(hideProc);
34130           clearTimeout(dismissProc);
34131           if(ce){
34132               hide(true);
34133           }
34134           locks.push(1);
34135        },
34136
34137     /**
34138      * Returns true if the quick tip is enabled, else false.
34139      */
34140        isEnabled : function(){
34141             return !disabled;
34142        },
34143
34144         // private
34145        tagConfig : {
34146            namespace : "roo", // was ext?? this may break..
34147            alt_namespace : "ext",
34148            attribute : "qtip",
34149            width : "width",
34150            target : "target",
34151            title : "qtitle",
34152            hide : "hide",
34153            cls : "qclass"
34154        }
34155    };
34156 }();
34157
34158 // backwards compat
34159 Roo.QuickTips.tips = Roo.QuickTips.register;/*
34160  * Based on:
34161  * Ext JS Library 1.1.1
34162  * Copyright(c) 2006-2007, Ext JS, LLC.
34163  *
34164  * Originally Released Under LGPL - original licence link has changed is not relivant.
34165  *
34166  * Fork - LGPL
34167  * <script type="text/javascript">
34168  */
34169  
34170
34171 /**
34172  * @class Roo.tree.TreePanel
34173  * @extends Roo.data.Tree
34174
34175  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
34176  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
34177  * @cfg {Boolean} enableDD true to enable drag and drop
34178  * @cfg {Boolean} enableDrag true to enable just drag
34179  * @cfg {Boolean} enableDrop true to enable just drop
34180  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
34181  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
34182  * @cfg {String} ddGroup The DD group this TreePanel belongs to
34183  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
34184  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
34185  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
34186  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
34187  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
34188  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
34189  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
34190  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
34191  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
34192  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
34193  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
34194  * @cfg {Function} renderer DEPRECATED - use TreeLoader:create event / 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>
34195  * @cfg {Function} rendererTip DEPRECATED - use TreeLoader:create event / 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>
34196  * 
34197  * @constructor
34198  * @param {String/HTMLElement/Element} el The container element
34199  * @param {Object} config
34200  */
34201 Roo.tree.TreePanel = function(el, config){
34202     var root = false;
34203     var loader = false;
34204     if (config.root) {
34205         root = config.root;
34206         delete config.root;
34207     }
34208     if (config.loader) {
34209         loader = config.loader;
34210         delete config.loader;
34211     }
34212     
34213     Roo.apply(this, config);
34214     Roo.tree.TreePanel.superclass.constructor.call(this);
34215     this.el = Roo.get(el);
34216     this.el.addClass('x-tree');
34217     //console.log(root);
34218     if (root) {
34219         this.setRootNode( Roo.factory(root, Roo.tree));
34220     }
34221     if (loader) {
34222         this.loader = Roo.factory(loader, Roo.tree);
34223     }
34224    /**
34225     * Read-only. The id of the container element becomes this TreePanel's id.
34226     */
34227     this.id = this.el.id;
34228     this.addEvents({
34229         /**
34230         * @event beforeload
34231         * Fires before a node is loaded, return false to cancel
34232         * @param {Node} node The node being loaded
34233         */
34234         "beforeload" : true,
34235         /**
34236         * @event load
34237         * Fires when a node is loaded
34238         * @param {Node} node The node that was loaded
34239         */
34240         "load" : true,
34241         /**
34242         * @event textchange
34243         * Fires when the text for a node is changed
34244         * @param {Node} node The node
34245         * @param {String} text The new text
34246         * @param {String} oldText The old text
34247         */
34248         "textchange" : true,
34249         /**
34250         * @event beforeexpand
34251         * Fires before a node is expanded, return false to cancel.
34252         * @param {Node} node The node
34253         * @param {Boolean} deep
34254         * @param {Boolean} anim
34255         */
34256         "beforeexpand" : true,
34257         /**
34258         * @event beforecollapse
34259         * Fires before a node is collapsed, return false to cancel.
34260         * @param {Node} node The node
34261         * @param {Boolean} deep
34262         * @param {Boolean} anim
34263         */
34264         "beforecollapse" : true,
34265         /**
34266         * @event expand
34267         * Fires when a node is expanded
34268         * @param {Node} node The node
34269         */
34270         "expand" : true,
34271         /**
34272         * @event disabledchange
34273         * Fires when the disabled status of a node changes
34274         * @param {Node} node The node
34275         * @param {Boolean} disabled
34276         */
34277         "disabledchange" : true,
34278         /**
34279         * @event collapse
34280         * Fires when a node is collapsed
34281         * @param {Node} node The node
34282         */
34283         "collapse" : true,
34284         /**
34285         * @event beforeclick
34286         * Fires before click processing on a node. Return false to cancel the default action.
34287         * @param {Node} node The node
34288         * @param {Roo.EventObject} e The event object
34289         */
34290         "beforeclick":true,
34291         /**
34292         * @event checkchange
34293         * Fires when a node with a checkbox's checked property changes
34294         * @param {Node} this This node
34295         * @param {Boolean} checked
34296         */
34297         "checkchange":true,
34298         /**
34299         * @event click
34300         * Fires when a node is clicked
34301         * @param {Node} node The node
34302         * @param {Roo.EventObject} e The event object
34303         */
34304         "click":true,
34305         /**
34306         * @event dblclick
34307         * Fires when a node is double clicked
34308         * @param {Node} node The node
34309         * @param {Roo.EventObject} e The event object
34310         */
34311         "dblclick":true,
34312         /**
34313         * @event contextmenu
34314         * Fires when a node is right clicked
34315         * @param {Node} node The node
34316         * @param {Roo.EventObject} e The event object
34317         */
34318         "contextmenu":true,
34319         /**
34320         * @event beforechildrenrendered
34321         * Fires right before the child nodes for a node are rendered
34322         * @param {Node} node The node
34323         */
34324         "beforechildrenrendered":true,
34325         /**
34326         * @event startdrag
34327         * Fires when a node starts being dragged
34328         * @param {Roo.tree.TreePanel} this
34329         * @param {Roo.tree.TreeNode} node
34330         * @param {event} e The raw browser event
34331         */ 
34332        "startdrag" : true,
34333        /**
34334         * @event enddrag
34335         * Fires when a drag operation is complete
34336         * @param {Roo.tree.TreePanel} this
34337         * @param {Roo.tree.TreeNode} node
34338         * @param {event} e The raw browser event
34339         */
34340        "enddrag" : true,
34341        /**
34342         * @event dragdrop
34343         * Fires when a dragged node is dropped on a valid DD target
34344         * @param {Roo.tree.TreePanel} this
34345         * @param {Roo.tree.TreeNode} node
34346         * @param {DD} dd The dd it was dropped on
34347         * @param {event} e The raw browser event
34348         */
34349        "dragdrop" : true,
34350        /**
34351         * @event beforenodedrop
34352         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
34353         * passed to handlers has the following properties:<br />
34354         * <ul style="padding:5px;padding-left:16px;">
34355         * <li>tree - The TreePanel</li>
34356         * <li>target - The node being targeted for the drop</li>
34357         * <li>data - The drag data from the drag source</li>
34358         * <li>point - The point of the drop - append, above or below</li>
34359         * <li>source - The drag source</li>
34360         * <li>rawEvent - Raw mouse event</li>
34361         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
34362         * to be inserted by setting them on this object.</li>
34363         * <li>cancel - Set this to true to cancel the drop.</li>
34364         * </ul>
34365         * @param {Object} dropEvent
34366         */
34367        "beforenodedrop" : true,
34368        /**
34369         * @event nodedrop
34370         * Fires after a DD object is dropped on a node in this tree. The dropEvent
34371         * passed to handlers has the following properties:<br />
34372         * <ul style="padding:5px;padding-left:16px;">
34373         * <li>tree - The TreePanel</li>
34374         * <li>target - The node being targeted for the drop</li>
34375         * <li>data - The drag data from the drag source</li>
34376         * <li>point - The point of the drop - append, above or below</li>
34377         * <li>source - The drag source</li>
34378         * <li>rawEvent - Raw mouse event</li>
34379         * <li>dropNode - Dropped node(s).</li>
34380         * </ul>
34381         * @param {Object} dropEvent
34382         */
34383        "nodedrop" : true,
34384         /**
34385         * @event nodedragover
34386         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
34387         * passed to handlers has the following properties:<br />
34388         * <ul style="padding:5px;padding-left:16px;">
34389         * <li>tree - The TreePanel</li>
34390         * <li>target - The node being targeted for the drop</li>
34391         * <li>data - The drag data from the drag source</li>
34392         * <li>point - The point of the drop - append, above or below</li>
34393         * <li>source - The drag source</li>
34394         * <li>rawEvent - Raw mouse event</li>
34395         * <li>dropNode - Drop node(s) provided by the source.</li>
34396         * <li>cancel - Set this to true to signal drop not allowed.</li>
34397         * </ul>
34398         * @param {Object} dragOverEvent
34399         */
34400        "nodedragover" : true,
34401        /**
34402         * @event appendnode
34403         * Fires when append node to the tree
34404         * @param {Roo.tree.TreePanel} this
34405         * @param {Roo.tree.TreeNode} node
34406         * @param {Number} index The index of the newly appended node
34407         */
34408        "appendnode" : true
34409         
34410     });
34411     if(this.singleExpand){
34412        this.on("beforeexpand", this.restrictExpand, this);
34413     }
34414     if (this.editor) {
34415         this.editor.tree = this;
34416         this.editor = Roo.factory(this.editor, Roo.tree);
34417     }
34418     
34419     if (this.selModel) {
34420         this.selModel = Roo.factory(this.selModel, Roo.tree);
34421     }
34422    
34423 };
34424 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
34425     rootVisible : true,
34426     animate: Roo.enableFx,
34427     lines : true,
34428     enableDD : false,
34429     hlDrop : Roo.enableFx,
34430   
34431     renderer: false,
34432     
34433     rendererTip: false,
34434     // private
34435     restrictExpand : function(node){
34436         var p = node.parentNode;
34437         if(p){
34438             if(p.expandedChild && p.expandedChild.parentNode == p){
34439                 p.expandedChild.collapse();
34440             }
34441             p.expandedChild = node;
34442         }
34443     },
34444
34445     // private override
34446     setRootNode : function(node){
34447         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
34448         if(!this.rootVisible){
34449             node.ui = new Roo.tree.RootTreeNodeUI(node);
34450         }
34451         return node;
34452     },
34453
34454     /**
34455      * Returns the container element for this TreePanel
34456      */
34457     getEl : function(){
34458         return this.el;
34459     },
34460
34461     /**
34462      * Returns the default TreeLoader for this TreePanel
34463      */
34464     getLoader : function(){
34465         return this.loader;
34466     },
34467
34468     /**
34469      * Expand all nodes
34470      */
34471     expandAll : function(){
34472         this.root.expand(true);
34473     },
34474
34475     /**
34476      * Collapse all nodes
34477      */
34478     collapseAll : function(){
34479         this.root.collapse(true);
34480     },
34481
34482     /**
34483      * Returns the selection model used by this TreePanel
34484      */
34485     getSelectionModel : function(){
34486         if(!this.selModel){
34487             this.selModel = new Roo.tree.DefaultSelectionModel();
34488         }
34489         return this.selModel;
34490     },
34491
34492     /**
34493      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
34494      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
34495      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
34496      * @return {Array}
34497      */
34498     getChecked : function(a, startNode){
34499         startNode = startNode || this.root;
34500         var r = [];
34501         var f = function(){
34502             if(this.attributes.checked){
34503                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
34504             }
34505         }
34506         startNode.cascade(f);
34507         return r;
34508     },
34509
34510     /**
34511      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34512      * @param {String} path
34513      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34514      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
34515      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
34516      */
34517     expandPath : function(path, attr, callback){
34518         attr = attr || "id";
34519         var keys = path.split(this.pathSeparator);
34520         var curNode = this.root;
34521         if(curNode.attributes[attr] != keys[1]){ // invalid root
34522             if(callback){
34523                 callback(false, null);
34524             }
34525             return;
34526         }
34527         var index = 1;
34528         var f = function(){
34529             if(++index == keys.length){
34530                 if(callback){
34531                     callback(true, curNode);
34532                 }
34533                 return;
34534             }
34535             var c = curNode.findChild(attr, keys[index]);
34536             if(!c){
34537                 if(callback){
34538                     callback(false, curNode);
34539                 }
34540                 return;
34541             }
34542             curNode = c;
34543             c.expand(false, false, f);
34544         };
34545         curNode.expand(false, false, f);
34546     },
34547
34548     /**
34549      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34550      * @param {String} path
34551      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34552      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
34553      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
34554      */
34555     selectPath : function(path, attr, callback){
34556         attr = attr || "id";
34557         var keys = path.split(this.pathSeparator);
34558         var v = keys.pop();
34559         if(keys.length > 0){
34560             var f = function(success, node){
34561                 if(success && node){
34562                     var n = node.findChild(attr, v);
34563                     if(n){
34564                         n.select();
34565                         if(callback){
34566                             callback(true, n);
34567                         }
34568                     }else if(callback){
34569                         callback(false, n);
34570                     }
34571                 }else{
34572                     if(callback){
34573                         callback(false, n);
34574                     }
34575                 }
34576             };
34577             this.expandPath(keys.join(this.pathSeparator), attr, f);
34578         }else{
34579             this.root.select();
34580             if(callback){
34581                 callback(true, this.root);
34582             }
34583         }
34584     },
34585
34586     getTreeEl : function(){
34587         return this.el;
34588     },
34589
34590     /**
34591      * Trigger rendering of this TreePanel
34592      */
34593     render : function(){
34594         if (this.innerCt) {
34595             return this; // stop it rendering more than once!!
34596         }
34597         
34598         this.innerCt = this.el.createChild({tag:"ul",
34599                cls:"x-tree-root-ct " +
34600                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
34601
34602         if(this.containerScroll){
34603             Roo.dd.ScrollManager.register(this.el);
34604         }
34605         if((this.enableDD || this.enableDrop) && !this.dropZone){
34606            /**
34607             * The dropZone used by this tree if drop is enabled
34608             * @type Roo.tree.TreeDropZone
34609             */
34610              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
34611                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
34612            });
34613         }
34614         if((this.enableDD || this.enableDrag) && !this.dragZone){
34615            /**
34616             * The dragZone used by this tree if drag is enabled
34617             * @type Roo.tree.TreeDragZone
34618             */
34619             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
34620                ddGroup: this.ddGroup || "TreeDD",
34621                scroll: this.ddScroll
34622            });
34623         }
34624         this.getSelectionModel().init(this);
34625         if (!this.root) {
34626             Roo.log("ROOT not set in tree");
34627             return this;
34628         }
34629         this.root.render();
34630         if(!this.rootVisible){
34631             this.root.renderChildren();
34632         }
34633         return this;
34634     }
34635 });/*
34636  * Based on:
34637  * Ext JS Library 1.1.1
34638  * Copyright(c) 2006-2007, Ext JS, LLC.
34639  *
34640  * Originally Released Under LGPL - original licence link has changed is not relivant.
34641  *
34642  * Fork - LGPL
34643  * <script type="text/javascript">
34644  */
34645  
34646
34647 /**
34648  * @class Roo.tree.DefaultSelectionModel
34649  * @extends Roo.util.Observable
34650  * The default single selection for a TreePanel.
34651  * @param {Object} cfg Configuration
34652  */
34653 Roo.tree.DefaultSelectionModel = function(cfg){
34654    this.selNode = null;
34655    
34656    
34657    
34658    this.addEvents({
34659        /**
34660         * @event selectionchange
34661         * Fires when the selected node changes
34662         * @param {DefaultSelectionModel} this
34663         * @param {TreeNode} node the new selection
34664         */
34665        "selectionchange" : true,
34666
34667        /**
34668         * @event beforeselect
34669         * Fires before the selected node changes, return false to cancel the change
34670         * @param {DefaultSelectionModel} this
34671         * @param {TreeNode} node the new selection
34672         * @param {TreeNode} node the old selection
34673         */
34674        "beforeselect" : true
34675    });
34676    
34677     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
34678 };
34679
34680 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
34681     init : function(tree){
34682         this.tree = tree;
34683         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34684         tree.on("click", this.onNodeClick, this);
34685     },
34686     
34687     onNodeClick : function(node, e){
34688         if (e.ctrlKey && this.selNode == node)  {
34689             this.unselect(node);
34690             return;
34691         }
34692         this.select(node);
34693     },
34694     
34695     /**
34696      * Select a node.
34697      * @param {TreeNode} node The node to select
34698      * @return {TreeNode} The selected node
34699      */
34700     select : function(node){
34701         var last = this.selNode;
34702         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
34703             if(last){
34704                 last.ui.onSelectedChange(false);
34705             }
34706             this.selNode = node;
34707             node.ui.onSelectedChange(true);
34708             this.fireEvent("selectionchange", this, node, last);
34709         }
34710         return node;
34711     },
34712     
34713     /**
34714      * Deselect a node.
34715      * @param {TreeNode} node The node to unselect
34716      */
34717     unselect : function(node){
34718         if(this.selNode == node){
34719             this.clearSelections();
34720         }    
34721     },
34722     
34723     /**
34724      * Clear all selections
34725      */
34726     clearSelections : function(){
34727         var n = this.selNode;
34728         if(n){
34729             n.ui.onSelectedChange(false);
34730             this.selNode = null;
34731             this.fireEvent("selectionchange", this, null);
34732         }
34733         return n;
34734     },
34735     
34736     /**
34737      * Get the selected node
34738      * @return {TreeNode} The selected node
34739      */
34740     getSelectedNode : function(){
34741         return this.selNode;    
34742     },
34743     
34744     /**
34745      * Returns true if the node is selected
34746      * @param {TreeNode} node The node to check
34747      * @return {Boolean}
34748      */
34749     isSelected : function(node){
34750         return this.selNode == node;  
34751     },
34752
34753     /**
34754      * Selects the node above the selected node in the tree, intelligently walking the nodes
34755      * @return TreeNode The new selection
34756      */
34757     selectPrevious : function(){
34758         var s = this.selNode || this.lastSelNode;
34759         if(!s){
34760             return null;
34761         }
34762         var ps = s.previousSibling;
34763         if(ps){
34764             if(!ps.isExpanded() || ps.childNodes.length < 1){
34765                 return this.select(ps);
34766             } else{
34767                 var lc = ps.lastChild;
34768                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
34769                     lc = lc.lastChild;
34770                 }
34771                 return this.select(lc);
34772             }
34773         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
34774             return this.select(s.parentNode);
34775         }
34776         return null;
34777     },
34778
34779     /**
34780      * Selects the node above the selected node in the tree, intelligently walking the nodes
34781      * @return TreeNode The new selection
34782      */
34783     selectNext : function(){
34784         var s = this.selNode || this.lastSelNode;
34785         if(!s){
34786             return null;
34787         }
34788         if(s.firstChild && s.isExpanded()){
34789              return this.select(s.firstChild);
34790          }else if(s.nextSibling){
34791              return this.select(s.nextSibling);
34792          }else if(s.parentNode){
34793             var newS = null;
34794             s.parentNode.bubble(function(){
34795                 if(this.nextSibling){
34796                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
34797                     return false;
34798                 }
34799             });
34800             return newS;
34801          }
34802         return null;
34803     },
34804
34805     onKeyDown : function(e){
34806         var s = this.selNode || this.lastSelNode;
34807         // undesirable, but required
34808         var sm = this;
34809         if(!s){
34810             return;
34811         }
34812         var k = e.getKey();
34813         switch(k){
34814              case e.DOWN:
34815                  e.stopEvent();
34816                  this.selectNext();
34817              break;
34818              case e.UP:
34819                  e.stopEvent();
34820                  this.selectPrevious();
34821              break;
34822              case e.RIGHT:
34823                  e.preventDefault();
34824                  if(s.hasChildNodes()){
34825                      if(!s.isExpanded()){
34826                          s.expand();
34827                      }else if(s.firstChild){
34828                          this.select(s.firstChild, e);
34829                      }
34830                  }
34831              break;
34832              case e.LEFT:
34833                  e.preventDefault();
34834                  if(s.hasChildNodes() && s.isExpanded()){
34835                      s.collapse();
34836                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
34837                      this.select(s.parentNode, e);
34838                  }
34839              break;
34840         };
34841     }
34842 });
34843
34844 /**
34845  * @class Roo.tree.MultiSelectionModel
34846  * @extends Roo.util.Observable
34847  * Multi selection for a TreePanel.
34848  * @param {Object} cfg Configuration
34849  */
34850 Roo.tree.MultiSelectionModel = function(){
34851    this.selNodes = [];
34852    this.selMap = {};
34853    this.addEvents({
34854        /**
34855         * @event selectionchange
34856         * Fires when the selected nodes change
34857         * @param {MultiSelectionModel} this
34858         * @param {Array} nodes Array of the selected nodes
34859         */
34860        "selectionchange" : true
34861    });
34862    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
34863    
34864 };
34865
34866 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
34867     init : function(tree){
34868         this.tree = tree;
34869         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34870         tree.on("click", this.onNodeClick, this);
34871     },
34872     
34873     onNodeClick : function(node, e){
34874         this.select(node, e, e.ctrlKey);
34875     },
34876     
34877     /**
34878      * Select a node.
34879      * @param {TreeNode} node The node to select
34880      * @param {EventObject} e (optional) An event associated with the selection
34881      * @param {Boolean} keepExisting True to retain existing selections
34882      * @return {TreeNode} The selected node
34883      */
34884     select : function(node, e, keepExisting){
34885         if(keepExisting !== true){
34886             this.clearSelections(true);
34887         }
34888         if(this.isSelected(node)){
34889             this.lastSelNode = node;
34890             return node;
34891         }
34892         this.selNodes.push(node);
34893         this.selMap[node.id] = node;
34894         this.lastSelNode = node;
34895         node.ui.onSelectedChange(true);
34896         this.fireEvent("selectionchange", this, this.selNodes);
34897         return node;
34898     },
34899     
34900     /**
34901      * Deselect a node.
34902      * @param {TreeNode} node The node to unselect
34903      */
34904     unselect : function(node){
34905         if(this.selMap[node.id]){
34906             node.ui.onSelectedChange(false);
34907             var sn = this.selNodes;
34908             var index = -1;
34909             if(sn.indexOf){
34910                 index = sn.indexOf(node);
34911             }else{
34912                 for(var i = 0, len = sn.length; i < len; i++){
34913                     if(sn[i] == node){
34914                         index = i;
34915                         break;
34916                     }
34917                 }
34918             }
34919             if(index != -1){
34920                 this.selNodes.splice(index, 1);
34921             }
34922             delete this.selMap[node.id];
34923             this.fireEvent("selectionchange", this, this.selNodes);
34924         }
34925     },
34926     
34927     /**
34928      * Clear all selections
34929      */
34930     clearSelections : function(suppressEvent){
34931         var sn = this.selNodes;
34932         if(sn.length > 0){
34933             for(var i = 0, len = sn.length; i < len; i++){
34934                 sn[i].ui.onSelectedChange(false);
34935             }
34936             this.selNodes = [];
34937             this.selMap = {};
34938             if(suppressEvent !== true){
34939                 this.fireEvent("selectionchange", this, this.selNodes);
34940             }
34941         }
34942     },
34943     
34944     /**
34945      * Returns true if the node is selected
34946      * @param {TreeNode} node The node to check
34947      * @return {Boolean}
34948      */
34949     isSelected : function(node){
34950         return this.selMap[node.id] ? true : false;  
34951     },
34952     
34953     /**
34954      * Returns an array of the selected nodes
34955      * @return {Array}
34956      */
34957     getSelectedNodes : function(){
34958         return this.selNodes;    
34959     },
34960
34961     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
34962
34963     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
34964
34965     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
34966 });/*
34967  * Based on:
34968  * Ext JS Library 1.1.1
34969  * Copyright(c) 2006-2007, Ext JS, LLC.
34970  *
34971  * Originally Released Under LGPL - original licence link has changed is not relivant.
34972  *
34973  * Fork - LGPL
34974  * <script type="text/javascript">
34975  */
34976  
34977 /**
34978  * @class Roo.tree.TreeNode
34979  * @extends Roo.data.Node
34980  * @cfg {String} text The text for this node
34981  * @cfg {Boolean} expanded true to start the node expanded
34982  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
34983  * @cfg {Boolean} allowDrop false if this node cannot be drop on
34984  * @cfg {Boolean} disabled true to start the node disabled
34985  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
34986  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
34987  * @cfg {String} cls A css class to be added to the node
34988  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
34989  * @cfg {String} href URL of the link used for the node (defaults to #)
34990  * @cfg {String} hrefTarget target frame for the link
34991  * @cfg {String} qtip An Ext QuickTip for the node
34992  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
34993  * @cfg {Boolean} singleClickExpand True for single click expand on this node
34994  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
34995  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
34996  * (defaults to undefined with no checkbox rendered)
34997  * @constructor
34998  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
34999  */
35000 Roo.tree.TreeNode = function(attributes){
35001     attributes = attributes || {};
35002     if(typeof attributes == "string"){
35003         attributes = {text: attributes};
35004     }
35005     this.childrenRendered = false;
35006     this.rendered = false;
35007     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
35008     this.expanded = attributes.expanded === true;
35009     this.isTarget = attributes.isTarget !== false;
35010     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
35011     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
35012
35013     /**
35014      * Read-only. The text for this node. To change it use setText().
35015      * @type String
35016      */
35017     this.text = attributes.text;
35018     /**
35019      * True if this node is disabled.
35020      * @type Boolean
35021      */
35022     this.disabled = attributes.disabled === true;
35023
35024     this.addEvents({
35025         /**
35026         * @event textchange
35027         * Fires when the text for this node is changed
35028         * @param {Node} this This node
35029         * @param {String} text The new text
35030         * @param {String} oldText The old text
35031         */
35032         "textchange" : true,
35033         /**
35034         * @event beforeexpand
35035         * Fires before this node is expanded, return false to cancel.
35036         * @param {Node} this This node
35037         * @param {Boolean} deep
35038         * @param {Boolean} anim
35039         */
35040         "beforeexpand" : true,
35041         /**
35042         * @event beforecollapse
35043         * Fires before this node is collapsed, return false to cancel.
35044         * @param {Node} this This node
35045         * @param {Boolean} deep
35046         * @param {Boolean} anim
35047         */
35048         "beforecollapse" : true,
35049         /**
35050         * @event expand
35051         * Fires when this node is expanded
35052         * @param {Node} this This node
35053         */
35054         "expand" : true,
35055         /**
35056         * @event disabledchange
35057         * Fires when the disabled status of this node changes
35058         * @param {Node} this This node
35059         * @param {Boolean} disabled
35060         */
35061         "disabledchange" : true,
35062         /**
35063         * @event collapse
35064         * Fires when this node is collapsed
35065         * @param {Node} this This node
35066         */
35067         "collapse" : true,
35068         /**
35069         * @event beforeclick
35070         * Fires before click processing. Return false to cancel the default action.
35071         * @param {Node} this This node
35072         * @param {Roo.EventObject} e The event object
35073         */
35074         "beforeclick":true,
35075         /**
35076         * @event checkchange
35077         * Fires when a node with a checkbox's checked property changes
35078         * @param {Node} this This node
35079         * @param {Boolean} checked
35080         */
35081         "checkchange":true,
35082         /**
35083         * @event click
35084         * Fires when this node is clicked
35085         * @param {Node} this This node
35086         * @param {Roo.EventObject} e The event object
35087         */
35088         "click":true,
35089         /**
35090         * @event dblclick
35091         * Fires when this node is double clicked
35092         * @param {Node} this This node
35093         * @param {Roo.EventObject} e The event object
35094         */
35095         "dblclick":true,
35096         /**
35097         * @event contextmenu
35098         * Fires when this node is right clicked
35099         * @param {Node} this This node
35100         * @param {Roo.EventObject} e The event object
35101         */
35102         "contextmenu":true,
35103         /**
35104         * @event beforechildrenrendered
35105         * Fires right before the child nodes for this node are rendered
35106         * @param {Node} this This node
35107         */
35108         "beforechildrenrendered":true
35109     });
35110
35111     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
35112
35113     /**
35114      * Read-only. The UI for this node
35115      * @type TreeNodeUI
35116      */
35117     this.ui = new uiClass(this);
35118     
35119     // finally support items[]
35120     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
35121         return;
35122     }
35123     
35124     
35125     Roo.each(this.attributes.items, function(c) {
35126         this.appendChild(Roo.factory(c,Roo.Tree));
35127     }, this);
35128     delete this.attributes.items;
35129     
35130     
35131     
35132 };
35133 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
35134     preventHScroll: true,
35135     /**
35136      * Returns true if this node is expanded
35137      * @return {Boolean}
35138      */
35139     isExpanded : function(){
35140         return this.expanded;
35141     },
35142
35143     /**
35144      * Returns the UI object for this node
35145      * @return {TreeNodeUI}
35146      */
35147     getUI : function(){
35148         return this.ui;
35149     },
35150
35151     // private override
35152     setFirstChild : function(node){
35153         var of = this.firstChild;
35154         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
35155         if(this.childrenRendered && of && node != of){
35156             of.renderIndent(true, true);
35157         }
35158         if(this.rendered){
35159             this.renderIndent(true, true);
35160         }
35161     },
35162
35163     // private override
35164     setLastChild : function(node){
35165         var ol = this.lastChild;
35166         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
35167         if(this.childrenRendered && ol && node != ol){
35168             ol.renderIndent(true, true);
35169         }
35170         if(this.rendered){
35171             this.renderIndent(true, true);
35172         }
35173     },
35174
35175     // these methods are overridden to provide lazy rendering support
35176     // private override
35177     appendChild : function()
35178     {
35179         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
35180         if(node && this.childrenRendered){
35181             node.render();
35182         }
35183         this.ui.updateExpandIcon();
35184         return node;
35185     },
35186
35187     // private override
35188     removeChild : function(node){
35189         this.ownerTree.getSelectionModel().unselect(node);
35190         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
35191         // if it's been rendered remove dom node
35192         if(this.childrenRendered){
35193             node.ui.remove();
35194         }
35195         if(this.childNodes.length < 1){
35196             this.collapse(false, false);
35197         }else{
35198             this.ui.updateExpandIcon();
35199         }
35200         if(!this.firstChild) {
35201             this.childrenRendered = false;
35202         }
35203         return node;
35204     },
35205
35206     // private override
35207     insertBefore : function(node, refNode){
35208         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
35209         if(newNode && refNode && this.childrenRendered){
35210             node.render();
35211         }
35212         this.ui.updateExpandIcon();
35213         return newNode;
35214     },
35215
35216     /**
35217      * Sets the text for this node
35218      * @param {String} text
35219      */
35220     setText : function(text){
35221         var oldText = this.text;
35222         this.text = text;
35223         this.attributes.text = text;
35224         if(this.rendered){ // event without subscribing
35225             this.ui.onTextChange(this, text, oldText);
35226         }
35227         this.fireEvent("textchange", this, text, oldText);
35228     },
35229
35230     /**
35231      * Triggers selection of this node
35232      */
35233     select : function(){
35234         this.getOwnerTree().getSelectionModel().select(this);
35235     },
35236
35237     /**
35238      * Triggers deselection of this node
35239      */
35240     unselect : function(){
35241         this.getOwnerTree().getSelectionModel().unselect(this);
35242     },
35243
35244     /**
35245      * Returns true if this node is selected
35246      * @return {Boolean}
35247      */
35248     isSelected : function(){
35249         return this.getOwnerTree().getSelectionModel().isSelected(this);
35250     },
35251
35252     /**
35253      * Expand this node.
35254      * @param {Boolean} deep (optional) True to expand all children as well
35255      * @param {Boolean} anim (optional) false to cancel the default animation
35256      * @param {Function} callback (optional) A callback to be called when
35257      * expanding this node completes (does not wait for deep expand to complete).
35258      * Called with 1 parameter, this node.
35259      */
35260     expand : function(deep, anim, callback){
35261         if(!this.expanded){
35262             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
35263                 return;
35264             }
35265             if(!this.childrenRendered){
35266                 this.renderChildren();
35267             }
35268             this.expanded = true;
35269             
35270             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
35271                 this.ui.animExpand(function(){
35272                     this.fireEvent("expand", this);
35273                     if(typeof callback == "function"){
35274                         callback(this);
35275                     }
35276                     if(deep === true){
35277                         this.expandChildNodes(true);
35278                     }
35279                 }.createDelegate(this));
35280                 return;
35281             }else{
35282                 this.ui.expand();
35283                 this.fireEvent("expand", this);
35284                 if(typeof callback == "function"){
35285                     callback(this);
35286                 }
35287             }
35288         }else{
35289            if(typeof callback == "function"){
35290                callback(this);
35291            }
35292         }
35293         if(deep === true){
35294             this.expandChildNodes(true);
35295         }
35296     },
35297
35298     isHiddenRoot : function(){
35299         return this.isRoot && !this.getOwnerTree().rootVisible;
35300     },
35301
35302     /**
35303      * Collapse this node.
35304      * @param {Boolean} deep (optional) True to collapse all children as well
35305      * @param {Boolean} anim (optional) false to cancel the default animation
35306      */
35307     collapse : function(deep, anim){
35308         if(this.expanded && !this.isHiddenRoot()){
35309             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
35310                 return;
35311             }
35312             this.expanded = false;
35313             if((this.getOwnerTree().animate && anim !== false) || anim){
35314                 this.ui.animCollapse(function(){
35315                     this.fireEvent("collapse", this);
35316                     if(deep === true){
35317                         this.collapseChildNodes(true);
35318                     }
35319                 }.createDelegate(this));
35320                 return;
35321             }else{
35322                 this.ui.collapse();
35323                 this.fireEvent("collapse", this);
35324             }
35325         }
35326         if(deep === true){
35327             var cs = this.childNodes;
35328             for(var i = 0, len = cs.length; i < len; i++) {
35329                 cs[i].collapse(true, false);
35330             }
35331         }
35332     },
35333
35334     // private
35335     delayedExpand : function(delay){
35336         if(!this.expandProcId){
35337             this.expandProcId = this.expand.defer(delay, this);
35338         }
35339     },
35340
35341     // private
35342     cancelExpand : function(){
35343         if(this.expandProcId){
35344             clearTimeout(this.expandProcId);
35345         }
35346         this.expandProcId = false;
35347     },
35348
35349     /**
35350      * Toggles expanded/collapsed state of the node
35351      */
35352     toggle : function(){
35353         if(this.expanded){
35354             this.collapse();
35355         }else{
35356             this.expand();
35357         }
35358     },
35359
35360     /**
35361      * Ensures all parent nodes are expanded
35362      */
35363     ensureVisible : function(callback){
35364         var tree = this.getOwnerTree();
35365         tree.expandPath(this.parentNode.getPath(), false, function(){
35366             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
35367             Roo.callback(callback);
35368         }.createDelegate(this));
35369     },
35370
35371     /**
35372      * Expand all child nodes
35373      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
35374      */
35375     expandChildNodes : function(deep){
35376         var cs = this.childNodes;
35377         for(var i = 0, len = cs.length; i < len; i++) {
35378                 cs[i].expand(deep);
35379         }
35380     },
35381
35382     /**
35383      * Collapse all child nodes
35384      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
35385      */
35386     collapseChildNodes : function(deep){
35387         var cs = this.childNodes;
35388         for(var i = 0, len = cs.length; i < len; i++) {
35389                 cs[i].collapse(deep);
35390         }
35391     },
35392
35393     /**
35394      * Disables this node
35395      */
35396     disable : function(){
35397         this.disabled = true;
35398         this.unselect();
35399         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35400             this.ui.onDisableChange(this, true);
35401         }
35402         this.fireEvent("disabledchange", this, true);
35403     },
35404
35405     /**
35406      * Enables this node
35407      */
35408     enable : function(){
35409         this.disabled = false;
35410         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35411             this.ui.onDisableChange(this, false);
35412         }
35413         this.fireEvent("disabledchange", this, false);
35414     },
35415
35416     // private
35417     renderChildren : function(suppressEvent){
35418         if(suppressEvent !== false){
35419             this.fireEvent("beforechildrenrendered", this);
35420         }
35421         var cs = this.childNodes;
35422         for(var i = 0, len = cs.length; i < len; i++){
35423             cs[i].render(true);
35424         }
35425         this.childrenRendered = true;
35426     },
35427
35428     // private
35429     sort : function(fn, scope){
35430         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
35431         if(this.childrenRendered){
35432             var cs = this.childNodes;
35433             for(var i = 0, len = cs.length; i < len; i++){
35434                 cs[i].render(true);
35435             }
35436         }
35437     },
35438
35439     // private
35440     render : function(bulkRender){
35441         this.ui.render(bulkRender);
35442         if(!this.rendered){
35443             this.rendered = true;
35444             if(this.expanded){
35445                 this.expanded = false;
35446                 this.expand(false, false);
35447             }
35448         }
35449     },
35450
35451     // private
35452     renderIndent : function(deep, refresh){
35453         if(refresh){
35454             this.ui.childIndent = null;
35455         }
35456         this.ui.renderIndent();
35457         if(deep === true && this.childrenRendered){
35458             var cs = this.childNodes;
35459             for(var i = 0, len = cs.length; i < len; i++){
35460                 cs[i].renderIndent(true, refresh);
35461             }
35462         }
35463     }
35464 });/*
35465  * Based on:
35466  * Ext JS Library 1.1.1
35467  * Copyright(c) 2006-2007, Ext JS, LLC.
35468  *
35469  * Originally Released Under LGPL - original licence link has changed is not relivant.
35470  *
35471  * Fork - LGPL
35472  * <script type="text/javascript">
35473  */
35474  
35475 /**
35476  * @class Roo.tree.AsyncTreeNode
35477  * @extends Roo.tree.TreeNode
35478  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
35479  * @constructor
35480  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
35481  */
35482  Roo.tree.AsyncTreeNode = function(config){
35483     this.loaded = false;
35484     this.loading = false;
35485     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
35486     /**
35487     * @event beforeload
35488     * Fires before this node is loaded, return false to cancel
35489     * @param {Node} this This node
35490     */
35491     this.addEvents({'beforeload':true, 'load': true});
35492     /**
35493     * @event load
35494     * Fires when this node is loaded
35495     * @param {Node} this This node
35496     */
35497     /**
35498      * The loader used by this node (defaults to using the tree's defined loader)
35499      * @type TreeLoader
35500      * @property loader
35501      */
35502 };
35503 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
35504     expand : function(deep, anim, callback){
35505         if(this.loading){ // if an async load is already running, waiting til it's done
35506             var timer;
35507             var f = function(){
35508                 if(!this.loading){ // done loading
35509                     clearInterval(timer);
35510                     this.expand(deep, anim, callback);
35511                 }
35512             }.createDelegate(this);
35513             timer = setInterval(f, 200);
35514             return;
35515         }
35516         if(!this.loaded){
35517             if(this.fireEvent("beforeload", this) === false){
35518                 return;
35519             }
35520             this.loading = true;
35521             this.ui.beforeLoad(this);
35522             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
35523             if(loader){
35524                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
35525                 return;
35526             }
35527         }
35528         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
35529     },
35530     
35531     /**
35532      * Returns true if this node is currently loading
35533      * @return {Boolean}
35534      */
35535     isLoading : function(){
35536         return this.loading;  
35537     },
35538     
35539     loadComplete : function(deep, anim, callback){
35540         this.loading = false;
35541         this.loaded = true;
35542         this.ui.afterLoad(this);
35543         this.fireEvent("load", this);
35544         this.expand(deep, anim, callback);
35545     },
35546     
35547     /**
35548      * Returns true if this node has been loaded
35549      * @return {Boolean}
35550      */
35551     isLoaded : function(){
35552         return this.loaded;
35553     },
35554     
35555     hasChildNodes : function(){
35556         if(!this.isLeaf() && !this.loaded){
35557             return true;
35558         }else{
35559             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
35560         }
35561     },
35562
35563     /**
35564      * Trigger a reload for this node
35565      * @param {Function} callback
35566      */
35567     reload : function(callback){
35568         this.collapse(false, false);
35569         while(this.firstChild){
35570             this.removeChild(this.firstChild);
35571         }
35572         this.childrenRendered = false;
35573         this.loaded = false;
35574         if(this.isHiddenRoot()){
35575             this.expanded = false;
35576         }
35577         this.expand(false, false, callback);
35578     }
35579 });/*
35580  * Based on:
35581  * Ext JS Library 1.1.1
35582  * Copyright(c) 2006-2007, Ext JS, LLC.
35583  *
35584  * Originally Released Under LGPL - original licence link has changed is not relivant.
35585  *
35586  * Fork - LGPL
35587  * <script type="text/javascript">
35588  */
35589  
35590 /**
35591  * @class Roo.tree.TreeNodeUI
35592  * @constructor
35593  * @param {Object} node The node to render
35594  * The TreeNode UI implementation is separate from the
35595  * tree implementation. Unless you are customizing the tree UI,
35596  * you should never have to use this directly.
35597  */
35598 Roo.tree.TreeNodeUI = function(node){
35599     this.node = node;
35600     this.rendered = false;
35601     this.animating = false;
35602     this.emptyIcon = Roo.BLANK_IMAGE_URL;
35603 };
35604
35605 Roo.tree.TreeNodeUI.prototype = {
35606     removeChild : function(node){
35607         if(this.rendered){
35608             this.ctNode.removeChild(node.ui.getEl());
35609         }
35610     },
35611
35612     beforeLoad : function(){
35613          this.addClass("x-tree-node-loading");
35614     },
35615
35616     afterLoad : function(){
35617          this.removeClass("x-tree-node-loading");
35618     },
35619
35620     onTextChange : function(node, text, oldText){
35621         if(this.rendered){
35622             this.textNode.innerHTML = text;
35623         }
35624     },
35625
35626     onDisableChange : function(node, state){
35627         this.disabled = state;
35628         if(state){
35629             this.addClass("x-tree-node-disabled");
35630         }else{
35631             this.removeClass("x-tree-node-disabled");
35632         }
35633     },
35634
35635     onSelectedChange : function(state){
35636         if(state){
35637             this.focus();
35638             this.addClass("x-tree-selected");
35639         }else{
35640             //this.blur();
35641             this.removeClass("x-tree-selected");
35642         }
35643     },
35644
35645     onMove : function(tree, node, oldParent, newParent, index, refNode){
35646         this.childIndent = null;
35647         if(this.rendered){
35648             var targetNode = newParent.ui.getContainer();
35649             if(!targetNode){//target not rendered
35650                 this.holder = document.createElement("div");
35651                 this.holder.appendChild(this.wrap);
35652                 return;
35653             }
35654             var insertBefore = refNode ? refNode.ui.getEl() : null;
35655             if(insertBefore){
35656                 targetNode.insertBefore(this.wrap, insertBefore);
35657             }else{
35658                 targetNode.appendChild(this.wrap);
35659             }
35660             this.node.renderIndent(true);
35661         }
35662     },
35663
35664     addClass : function(cls){
35665         if(this.elNode){
35666             Roo.fly(this.elNode).addClass(cls);
35667         }
35668     },
35669
35670     removeClass : function(cls){
35671         if(this.elNode){
35672             Roo.fly(this.elNode).removeClass(cls);
35673         }
35674     },
35675
35676     remove : function(){
35677         if(this.rendered){
35678             this.holder = document.createElement("div");
35679             this.holder.appendChild(this.wrap);
35680         }
35681     },
35682
35683     fireEvent : function(){
35684         return this.node.fireEvent.apply(this.node, arguments);
35685     },
35686
35687     initEvents : function(){
35688         this.node.on("move", this.onMove, this);
35689         var E = Roo.EventManager;
35690         var a = this.anchor;
35691
35692         var el = Roo.fly(a, '_treeui');
35693
35694         if(Roo.isOpera){ // opera render bug ignores the CSS
35695             el.setStyle("text-decoration", "none");
35696         }
35697
35698         el.on("click", this.onClick, this);
35699         el.on("dblclick", this.onDblClick, this);
35700
35701         if(this.checkbox){
35702             Roo.EventManager.on(this.checkbox,
35703                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
35704         }
35705
35706         el.on("contextmenu", this.onContextMenu, this);
35707
35708         var icon = Roo.fly(this.iconNode);
35709         icon.on("click", this.onClick, this);
35710         icon.on("dblclick", this.onDblClick, this);
35711         icon.on("contextmenu", this.onContextMenu, this);
35712         E.on(this.ecNode, "click", this.ecClick, this, true);
35713
35714         if(this.node.disabled){
35715             this.addClass("x-tree-node-disabled");
35716         }
35717         if(this.node.hidden){
35718             this.addClass("x-tree-node-disabled");
35719         }
35720         var ot = this.node.getOwnerTree();
35721         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
35722         if(dd && (!this.node.isRoot || ot.rootVisible)){
35723             Roo.dd.Registry.register(this.elNode, {
35724                 node: this.node,
35725                 handles: this.getDDHandles(),
35726                 isHandle: false
35727             });
35728         }
35729     },
35730
35731     getDDHandles : function(){
35732         return [this.iconNode, this.textNode];
35733     },
35734
35735     hide : function(){
35736         if(this.rendered){
35737             this.wrap.style.display = "none";
35738         }
35739     },
35740
35741     show : function(){
35742         if(this.rendered){
35743             this.wrap.style.display = "";
35744         }
35745     },
35746
35747     onContextMenu : function(e){
35748         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
35749             e.preventDefault();
35750             this.focus();
35751             this.fireEvent("contextmenu", this.node, e);
35752         }
35753     },
35754
35755     onClick : function(e){
35756         if(this.dropping){
35757             e.stopEvent();
35758             return;
35759         }
35760         if(this.fireEvent("beforeclick", this.node, e) !== false){
35761             if(!this.disabled && this.node.attributes.href){
35762                 this.fireEvent("click", this.node, e);
35763                 return;
35764             }
35765             e.preventDefault();
35766             if(this.disabled){
35767                 return;
35768             }
35769
35770             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
35771                 this.node.toggle();
35772             }
35773
35774             this.fireEvent("click", this.node, e);
35775         }else{
35776             e.stopEvent();
35777         }
35778     },
35779
35780     onDblClick : function(e){
35781         e.preventDefault();
35782         if(this.disabled){
35783             return;
35784         }
35785         if(this.checkbox){
35786             this.toggleCheck();
35787         }
35788         if(!this.animating && this.node.hasChildNodes()){
35789             this.node.toggle();
35790         }
35791         this.fireEvent("dblclick", this.node, e);
35792     },
35793
35794     onCheckChange : function(){
35795         var checked = this.checkbox.checked;
35796         this.node.attributes.checked = checked;
35797         this.fireEvent('checkchange', this.node, checked);
35798     },
35799
35800     ecClick : function(e){
35801         if(!this.animating && this.node.hasChildNodes()){
35802             this.node.toggle();
35803         }
35804     },
35805
35806     startDrop : function(){
35807         this.dropping = true;
35808     },
35809
35810     // delayed drop so the click event doesn't get fired on a drop
35811     endDrop : function(){
35812        setTimeout(function(){
35813            this.dropping = false;
35814        }.createDelegate(this), 50);
35815     },
35816
35817     expand : function(){
35818         this.updateExpandIcon();
35819         this.ctNode.style.display = "";
35820     },
35821
35822     focus : function(){
35823         if(!this.node.preventHScroll){
35824             try{this.anchor.focus();
35825             }catch(e){}
35826         }else if(!Roo.isIE){
35827             try{
35828                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
35829                 var l = noscroll.scrollLeft;
35830                 this.anchor.focus();
35831                 noscroll.scrollLeft = l;
35832             }catch(e){}
35833         }
35834     },
35835
35836     toggleCheck : function(value){
35837         var cb = this.checkbox;
35838         if(cb){
35839             cb.checked = (value === undefined ? !cb.checked : value);
35840         }
35841     },
35842
35843     blur : function(){
35844         try{
35845             this.anchor.blur();
35846         }catch(e){}
35847     },
35848
35849     animExpand : function(callback){
35850         var ct = Roo.get(this.ctNode);
35851         ct.stopFx();
35852         if(!this.node.hasChildNodes()){
35853             this.updateExpandIcon();
35854             this.ctNode.style.display = "";
35855             Roo.callback(callback);
35856             return;
35857         }
35858         this.animating = true;
35859         this.updateExpandIcon();
35860
35861         ct.slideIn('t', {
35862            callback : function(){
35863                this.animating = false;
35864                Roo.callback(callback);
35865             },
35866             scope: this,
35867             duration: this.node.ownerTree.duration || .25
35868         });
35869     },
35870
35871     highlight : function(){
35872         var tree = this.node.getOwnerTree();
35873         Roo.fly(this.wrap).highlight(
35874             tree.hlColor || "C3DAF9",
35875             {endColor: tree.hlBaseColor}
35876         );
35877     },
35878
35879     collapse : function(){
35880         this.updateExpandIcon();
35881         this.ctNode.style.display = "none";
35882     },
35883
35884     animCollapse : function(callback){
35885         var ct = Roo.get(this.ctNode);
35886         ct.enableDisplayMode('block');
35887         ct.stopFx();
35888
35889         this.animating = true;
35890         this.updateExpandIcon();
35891
35892         ct.slideOut('t', {
35893             callback : function(){
35894                this.animating = false;
35895                Roo.callback(callback);
35896             },
35897             scope: this,
35898             duration: this.node.ownerTree.duration || .25
35899         });
35900     },
35901
35902     getContainer : function(){
35903         return this.ctNode;
35904     },
35905
35906     getEl : function(){
35907         return this.wrap;
35908     },
35909
35910     appendDDGhost : function(ghostNode){
35911         ghostNode.appendChild(this.elNode.cloneNode(true));
35912     },
35913
35914     getDDRepairXY : function(){
35915         return Roo.lib.Dom.getXY(this.iconNode);
35916     },
35917
35918     onRender : function(){
35919         this.render();
35920     },
35921
35922     render : function(bulkRender){
35923         var n = this.node, a = n.attributes;
35924         var targetNode = n.parentNode ?
35925               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
35926
35927         if(!this.rendered){
35928             this.rendered = true;
35929
35930             this.renderElements(n, a, targetNode, bulkRender);
35931
35932             if(a.qtip){
35933                if(this.textNode.setAttributeNS){
35934                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
35935                    if(a.qtipTitle){
35936                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
35937                    }
35938                }else{
35939                    this.textNode.setAttribute("ext:qtip", a.qtip);
35940                    if(a.qtipTitle){
35941                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
35942                    }
35943                }
35944             }else if(a.qtipCfg){
35945                 a.qtipCfg.target = Roo.id(this.textNode);
35946                 Roo.QuickTips.register(a.qtipCfg);
35947             }
35948             this.initEvents();
35949             if(!this.node.expanded){
35950                 this.updateExpandIcon();
35951             }
35952         }else{
35953             if(bulkRender === true) {
35954                 targetNode.appendChild(this.wrap);
35955             }
35956         }
35957     },
35958
35959     renderElements : function(n, a, targetNode, bulkRender)
35960     {
35961         // add some indent caching, this helps performance when rendering a large tree
35962         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35963         var t = n.getOwnerTree();
35964         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
35965         if (typeof(n.attributes.html) != 'undefined') {
35966             txt = n.attributes.html;
35967         }
35968         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
35969         var cb = typeof a.checked == 'boolean';
35970         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35971         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
35972             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
35973             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
35974             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
35975             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
35976             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
35977              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
35978                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
35979             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35980             "</li>"];
35981
35982         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35983             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35984                                 n.nextSibling.ui.getEl(), buf.join(""));
35985         }else{
35986             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35987         }
35988
35989         this.elNode = this.wrap.childNodes[0];
35990         this.ctNode = this.wrap.childNodes[1];
35991         var cs = this.elNode.childNodes;
35992         this.indentNode = cs[0];
35993         this.ecNode = cs[1];
35994         this.iconNode = cs[2];
35995         var index = 3;
35996         if(cb){
35997             this.checkbox = cs[3];
35998             index++;
35999         }
36000         this.anchor = cs[index];
36001         this.textNode = cs[index].firstChild;
36002     },
36003
36004     getAnchor : function(){
36005         return this.anchor;
36006     },
36007
36008     getTextEl : function(){
36009         return this.textNode;
36010     },
36011
36012     getIconEl : function(){
36013         return this.iconNode;
36014     },
36015
36016     isChecked : function(){
36017         return this.checkbox ? this.checkbox.checked : false;
36018     },
36019
36020     updateExpandIcon : function(){
36021         if(this.rendered){
36022             var n = this.node, c1, c2;
36023             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
36024             var hasChild = n.hasChildNodes();
36025             if(hasChild){
36026                 if(n.expanded){
36027                     cls += "-minus";
36028                     c1 = "x-tree-node-collapsed";
36029                     c2 = "x-tree-node-expanded";
36030                 }else{
36031                     cls += "-plus";
36032                     c1 = "x-tree-node-expanded";
36033                     c2 = "x-tree-node-collapsed";
36034                 }
36035                 if(this.wasLeaf){
36036                     this.removeClass("x-tree-node-leaf");
36037                     this.wasLeaf = false;
36038                 }
36039                 if(this.c1 != c1 || this.c2 != c2){
36040                     Roo.fly(this.elNode).replaceClass(c1, c2);
36041                     this.c1 = c1; this.c2 = c2;
36042                 }
36043             }else{
36044                 // this changes non-leafs into leafs if they have no children.
36045                 // it's not very rational behaviour..
36046                 
36047                 if(!this.wasLeaf && this.node.leaf){
36048                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
36049                     delete this.c1;
36050                     delete this.c2;
36051                     this.wasLeaf = true;
36052                 }
36053             }
36054             var ecc = "x-tree-ec-icon "+cls;
36055             if(this.ecc != ecc){
36056                 this.ecNode.className = ecc;
36057                 this.ecc = ecc;
36058             }
36059         }
36060     },
36061
36062     getChildIndent : function(){
36063         if(!this.childIndent){
36064             var buf = [];
36065             var p = this.node;
36066             while(p){
36067                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
36068                     if(!p.isLast()) {
36069                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
36070                     } else {
36071                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
36072                     }
36073                 }
36074                 p = p.parentNode;
36075             }
36076             this.childIndent = buf.join("");
36077         }
36078         return this.childIndent;
36079     },
36080
36081     renderIndent : function(){
36082         if(this.rendered){
36083             var indent = "";
36084             var p = this.node.parentNode;
36085             if(p){
36086                 indent = p.ui.getChildIndent();
36087             }
36088             if(this.indentMarkup != indent){ // don't rerender if not required
36089                 this.indentNode.innerHTML = indent;
36090                 this.indentMarkup = indent;
36091             }
36092             this.updateExpandIcon();
36093         }
36094     }
36095 };
36096
36097 Roo.tree.RootTreeNodeUI = function(){
36098     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
36099 };
36100 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
36101     render : function(){
36102         if(!this.rendered){
36103             var targetNode = this.node.ownerTree.innerCt.dom;
36104             this.node.expanded = true;
36105             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
36106             this.wrap = this.ctNode = targetNode.firstChild;
36107         }
36108     },
36109     collapse : function(){
36110     },
36111     expand : function(){
36112     }
36113 });/*
36114  * Based on:
36115  * Ext JS Library 1.1.1
36116  * Copyright(c) 2006-2007, Ext JS, LLC.
36117  *
36118  * Originally Released Under LGPL - original licence link has changed is not relivant.
36119  *
36120  * Fork - LGPL
36121  * <script type="text/javascript">
36122  */
36123 /**
36124  * @class Roo.tree.TreeLoader
36125  * @extends Roo.util.Observable
36126  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
36127  * nodes from a specified URL. The response must be a javascript Array definition
36128  * who's elements are node definition objects. eg:
36129  * <pre><code>
36130 {  success : true,
36131    data :      [
36132    
36133     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
36134     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
36135     ]
36136 }
36137
36138
36139 </code></pre>
36140  * <br><br>
36141  * The old style respose with just an array is still supported, but not recommended.
36142  * <br><br>
36143  *
36144  * A server request is sent, and child nodes are loaded only when a node is expanded.
36145  * The loading node's id is passed to the server under the parameter name "node" to
36146  * enable the server to produce the correct child nodes.
36147  * <br><br>
36148  * To pass extra parameters, an event handler may be attached to the "beforeload"
36149  * event, and the parameters specified in the TreeLoader's baseParams property:
36150  * <pre><code>
36151     myTreeLoader.on("beforeload", function(treeLoader, node) {
36152         this.baseParams.category = node.attributes.category;
36153     }, this);
36154     
36155 </code></pre>
36156  *
36157  * This would pass an HTTP parameter called "category" to the server containing
36158  * the value of the Node's "category" attribute.
36159  * @constructor
36160  * Creates a new Treeloader.
36161  * @param {Object} config A config object containing config properties.
36162  */
36163 Roo.tree.TreeLoader = function(config){
36164     this.baseParams = {};
36165     this.requestMethod = "POST";
36166     Roo.apply(this, config);
36167
36168     this.addEvents({
36169     
36170         /**
36171          * @event beforeload
36172          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
36173          * @param {Object} This TreeLoader object.
36174          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36175          * @param {Object} callback The callback function specified in the {@link #load} call.
36176          */
36177         beforeload : true,
36178         /**
36179          * @event load
36180          * Fires when the node has been successfuly loaded.
36181          * @param {Object} This TreeLoader object.
36182          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36183          * @param {Object} response The response object containing the data from the server.
36184          */
36185         load : true,
36186         /**
36187          * @event loadexception
36188          * Fires if the network request failed.
36189          * @param {Object} This TreeLoader object.
36190          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36191          * @param {Object} response The response object containing the data from the server.
36192          */
36193         loadexception : true,
36194         /**
36195          * @event create
36196          * Fires before a node is created, enabling you to return custom Node types 
36197          * @param {Object} This TreeLoader object.
36198          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
36199          */
36200         create : true
36201     });
36202
36203     Roo.tree.TreeLoader.superclass.constructor.call(this);
36204 };
36205
36206 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
36207     /**
36208     * @cfg {String} dataUrl The URL from which to request a Json string which
36209     * specifies an array of node definition object representing the child nodes
36210     * to be loaded.
36211     */
36212     /**
36213     * @cfg {String} requestMethod either GET or POST
36214     * defaults to POST (due to BC)
36215     * to be loaded.
36216     */
36217     /**
36218     * @cfg {Object} baseParams (optional) An object containing properties which
36219     * specify HTTP parameters to be passed to each request for child nodes.
36220     */
36221     /**
36222     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
36223     * created by this loader. If the attributes sent by the server have an attribute in this object,
36224     * they take priority.
36225     */
36226     /**
36227     * @cfg {Object} uiProviders (optional) An object containing properties which
36228     * 
36229     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
36230     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
36231     * <i>uiProvider</i> attribute of a returned child node is a string rather
36232     * than a reference to a TreeNodeUI implementation, this that string value
36233     * is used as a property name in the uiProviders object. You can define the provider named
36234     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
36235     */
36236     uiProviders : {},
36237
36238     /**
36239     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
36240     * child nodes before loading.
36241     */
36242     clearOnLoad : true,
36243
36244     /**
36245     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
36246     * property on loading, rather than expecting an array. (eg. more compatible to a standard
36247     * Grid query { data : [ .....] }
36248     */
36249     
36250     root : false,
36251      /**
36252     * @cfg {String} queryParam (optional) 
36253     * Name of the query as it will be passed on the querystring (defaults to 'node')
36254     * eg. the request will be ?node=[id]
36255     */
36256     
36257     
36258     queryParam: false,
36259     
36260     /**
36261      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
36262      * This is called automatically when a node is expanded, but may be used to reload
36263      * a node (or append new children if the {@link #clearOnLoad} option is false.)
36264      * @param {Roo.tree.TreeNode} node
36265      * @param {Function} callback
36266      */
36267     load : function(node, callback){
36268         if(this.clearOnLoad){
36269             while(node.firstChild){
36270                 node.removeChild(node.firstChild);
36271             }
36272         }
36273         if(node.attributes.children){ // preloaded json children
36274             var cs = node.attributes.children;
36275             for(var i = 0, len = cs.length; i < len; i++){
36276                 node.appendChild(this.createNode(cs[i]));
36277             }
36278             if(typeof callback == "function"){
36279                 callback();
36280             }
36281         }else if(this.dataUrl){
36282             this.requestData(node, callback);
36283         }
36284     },
36285
36286     getParams: function(node){
36287         var buf = [], bp = this.baseParams;
36288         for(var key in bp){
36289             if(typeof bp[key] != "function"){
36290                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
36291             }
36292         }
36293         var n = this.queryParam === false ? 'node' : this.queryParam;
36294         buf.push(n + "=", encodeURIComponent(node.id));
36295         return buf.join("");
36296     },
36297
36298     requestData : function(node, callback){
36299         if(this.fireEvent("beforeload", this, node, callback) !== false){
36300             this.transId = Roo.Ajax.request({
36301                 method:this.requestMethod,
36302                 url: this.dataUrl||this.url,
36303                 success: this.handleResponse,
36304                 failure: this.handleFailure,
36305                 scope: this,
36306                 argument: {callback: callback, node: node},
36307                 params: this.getParams(node)
36308             });
36309         }else{
36310             // if the load is cancelled, make sure we notify
36311             // the node that we are done
36312             if(typeof callback == "function"){
36313                 callback();
36314             }
36315         }
36316     },
36317
36318     isLoading : function(){
36319         return this.transId ? true : false;
36320     },
36321
36322     abort : function(){
36323         if(this.isLoading()){
36324             Roo.Ajax.abort(this.transId);
36325         }
36326     },
36327
36328     // private
36329     createNode : function(attr)
36330     {
36331         // apply baseAttrs, nice idea Corey!
36332         if(this.baseAttrs){
36333             Roo.applyIf(attr, this.baseAttrs);
36334         }
36335         if(this.applyLoader !== false){
36336             attr.loader = this;
36337         }
36338         // uiProvider = depreciated..
36339         
36340         if(typeof(attr.uiProvider) == 'string'){
36341            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
36342                 /**  eval:var:attr */ eval(attr.uiProvider);
36343         }
36344         if(typeof(this.uiProviders['default']) != 'undefined') {
36345             attr.uiProvider = this.uiProviders['default'];
36346         }
36347         
36348         this.fireEvent('create', this, attr);
36349         
36350         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
36351         return(attr.leaf ?
36352                         new Roo.tree.TreeNode(attr) :
36353                         new Roo.tree.AsyncTreeNode(attr));
36354     },
36355
36356     processResponse : function(response, node, callback)
36357     {
36358         var json = response.responseText;
36359         try {
36360             
36361             var o = Roo.decode(json);
36362             
36363             if (this.root === false && typeof(o.success) != undefined) {
36364                 this.root = 'data'; // the default behaviour for list like data..
36365                 }
36366                 
36367             if (this.root !== false &&  !o.success) {
36368                 // it's a failure condition.
36369                 var a = response.argument;
36370                 this.fireEvent("loadexception", this, a.node, response);
36371                 Roo.log("Load failed - should have a handler really");
36372                 return;
36373             }
36374             
36375             
36376             
36377             if (this.root !== false) {
36378                  o = o[this.root];
36379             }
36380             
36381             for(var i = 0, len = o.length; i < len; i++){
36382                 var n = this.createNode(o[i]);
36383                 if(n){
36384                     node.appendChild(n);
36385                 }
36386             }
36387             if(typeof callback == "function"){
36388                 callback(this, node);
36389             }
36390         }catch(e){
36391             this.handleFailure(response);
36392         }
36393     },
36394
36395     handleResponse : function(response){
36396         this.transId = false;
36397         var a = response.argument;
36398         this.processResponse(response, a.node, a.callback);
36399         this.fireEvent("load", this, a.node, response);
36400     },
36401
36402     handleFailure : function(response)
36403     {
36404         // should handle failure better..
36405         this.transId = false;
36406         var a = response.argument;
36407         this.fireEvent("loadexception", this, a.node, response);
36408         if(typeof a.callback == "function"){
36409             a.callback(this, a.node);
36410         }
36411     }
36412 });/*
36413  * Based on:
36414  * Ext JS Library 1.1.1
36415  * Copyright(c) 2006-2007, Ext JS, LLC.
36416  *
36417  * Originally Released Under LGPL - original licence link has changed is not relivant.
36418  *
36419  * Fork - LGPL
36420  * <script type="text/javascript">
36421  */
36422
36423 /**
36424 * @class Roo.tree.TreeFilter
36425 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
36426 * @param {TreePanel} tree
36427 * @param {Object} config (optional)
36428  */
36429 Roo.tree.TreeFilter = function(tree, config){
36430     this.tree = tree;
36431     this.filtered = {};
36432     Roo.apply(this, config);
36433 };
36434
36435 Roo.tree.TreeFilter.prototype = {
36436     clearBlank:false,
36437     reverse:false,
36438     autoClear:false,
36439     remove:false,
36440
36441      /**
36442      * Filter the data by a specific attribute.
36443      * @param {String/RegExp} value Either string that the attribute value
36444      * should start with or a RegExp to test against the attribute
36445      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
36446      * @param {TreeNode} startNode (optional) The node to start the filter at.
36447      */
36448     filter : function(value, attr, startNode){
36449         attr = attr || "text";
36450         var f;
36451         if(typeof value == "string"){
36452             var vlen = value.length;
36453             // auto clear empty filter
36454             if(vlen == 0 && this.clearBlank){
36455                 this.clear();
36456                 return;
36457             }
36458             value = value.toLowerCase();
36459             f = function(n){
36460                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
36461             };
36462         }else if(value.exec){ // regex?
36463             f = function(n){
36464                 return value.test(n.attributes[attr]);
36465             };
36466         }else{
36467             throw 'Illegal filter type, must be string or regex';
36468         }
36469         this.filterBy(f, null, startNode);
36470         },
36471
36472     /**
36473      * Filter by a function. The passed function will be called with each
36474      * node in the tree (or from the startNode). If the function returns true, the node is kept
36475      * otherwise it is filtered. If a node is filtered, its children are also filtered.
36476      * @param {Function} fn The filter function
36477      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
36478      */
36479     filterBy : function(fn, scope, startNode){
36480         startNode = startNode || this.tree.root;
36481         if(this.autoClear){
36482             this.clear();
36483         }
36484         var af = this.filtered, rv = this.reverse;
36485         var f = function(n){
36486             if(n == startNode){
36487                 return true;
36488             }
36489             if(af[n.id]){
36490                 return false;
36491             }
36492             var m = fn.call(scope || n, n);
36493             if(!m || rv){
36494                 af[n.id] = n;
36495                 n.ui.hide();
36496                 return false;
36497             }
36498             return true;
36499         };
36500         startNode.cascade(f);
36501         if(this.remove){
36502            for(var id in af){
36503                if(typeof id != "function"){
36504                    var n = af[id];
36505                    if(n && n.parentNode){
36506                        n.parentNode.removeChild(n);
36507                    }
36508                }
36509            }
36510         }
36511     },
36512
36513     /**
36514      * Clears the current filter. Note: with the "remove" option
36515      * set a filter cannot be cleared.
36516      */
36517     clear : function(){
36518         var t = this.tree;
36519         var af = this.filtered;
36520         for(var id in af){
36521             if(typeof id != "function"){
36522                 var n = af[id];
36523                 if(n){
36524                     n.ui.show();
36525                 }
36526             }
36527         }
36528         this.filtered = {};
36529     }
36530 };
36531 /*
36532  * Based on:
36533  * Ext JS Library 1.1.1
36534  * Copyright(c) 2006-2007, Ext JS, LLC.
36535  *
36536  * Originally Released Under LGPL - original licence link has changed is not relivant.
36537  *
36538  * Fork - LGPL
36539  * <script type="text/javascript">
36540  */
36541  
36542
36543 /**
36544  * @class Roo.tree.TreeSorter
36545  * Provides sorting of nodes in a TreePanel
36546  * 
36547  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
36548  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
36549  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
36550  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
36551  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
36552  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
36553  * @constructor
36554  * @param {TreePanel} tree
36555  * @param {Object} config
36556  */
36557 Roo.tree.TreeSorter = function(tree, config){
36558     Roo.apply(this, config);
36559     tree.on("beforechildrenrendered", this.doSort, this);
36560     tree.on("append", this.updateSort, this);
36561     tree.on("insert", this.updateSort, this);
36562     
36563     var dsc = this.dir && this.dir.toLowerCase() == "desc";
36564     var p = this.property || "text";
36565     var sortType = this.sortType;
36566     var fs = this.folderSort;
36567     var cs = this.caseSensitive === true;
36568     var leafAttr = this.leafAttr || 'leaf';
36569
36570     this.sortFn = function(n1, n2){
36571         if(fs){
36572             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
36573                 return 1;
36574             }
36575             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
36576                 return -1;
36577             }
36578         }
36579         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
36580         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
36581         if(v1 < v2){
36582                         return dsc ? +1 : -1;
36583                 }else if(v1 > v2){
36584                         return dsc ? -1 : +1;
36585         }else{
36586                 return 0;
36587         }
36588     };
36589 };
36590
36591 Roo.tree.TreeSorter.prototype = {
36592     doSort : function(node){
36593         node.sort(this.sortFn);
36594     },
36595     
36596     compareNodes : function(n1, n2){
36597         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
36598     },
36599     
36600     updateSort : function(tree, node){
36601         if(node.childrenRendered){
36602             this.doSort.defer(1, this, [node]);
36603         }
36604     }
36605 };/*
36606  * Based on:
36607  * Ext JS Library 1.1.1
36608  * Copyright(c) 2006-2007, Ext JS, LLC.
36609  *
36610  * Originally Released Under LGPL - original licence link has changed is not relivant.
36611  *
36612  * Fork - LGPL
36613  * <script type="text/javascript">
36614  */
36615
36616 if(Roo.dd.DropZone){
36617     
36618 Roo.tree.TreeDropZone = function(tree, config){
36619     this.allowParentInsert = false;
36620     this.allowContainerDrop = false;
36621     this.appendOnly = false;
36622     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
36623     this.tree = tree;
36624     this.lastInsertClass = "x-tree-no-status";
36625     this.dragOverData = {};
36626 };
36627
36628 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
36629     ddGroup : "TreeDD",
36630     scroll:  true,
36631     
36632     expandDelay : 1000,
36633     
36634     expandNode : function(node){
36635         if(node.hasChildNodes() && !node.isExpanded()){
36636             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
36637         }
36638     },
36639     
36640     queueExpand : function(node){
36641         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
36642     },
36643     
36644     cancelExpand : function(){
36645         if(this.expandProcId){
36646             clearTimeout(this.expandProcId);
36647             this.expandProcId = false;
36648         }
36649     },
36650     
36651     isValidDropPoint : function(n, pt, dd, e, data){
36652         if(!n || !data){ return false; }
36653         var targetNode = n.node;
36654         var dropNode = data.node;
36655         // default drop rules
36656         if(!(targetNode && targetNode.isTarget && pt)){
36657             return false;
36658         }
36659         if(pt == "append" && targetNode.allowChildren === false){
36660             return false;
36661         }
36662         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
36663             return false;
36664         }
36665         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
36666             return false;
36667         }
36668         // reuse the object
36669         var overEvent = this.dragOverData;
36670         overEvent.tree = this.tree;
36671         overEvent.target = targetNode;
36672         overEvent.data = data;
36673         overEvent.point = pt;
36674         overEvent.source = dd;
36675         overEvent.rawEvent = e;
36676         overEvent.dropNode = dropNode;
36677         overEvent.cancel = false;  
36678         var result = this.tree.fireEvent("nodedragover", overEvent);
36679         return overEvent.cancel === false && result !== false;
36680     },
36681     
36682     getDropPoint : function(e, n, dd)
36683     {
36684         var tn = n.node;
36685         if(tn.isRoot){
36686             return tn.allowChildren !== false ? "append" : false; // always append for root
36687         }
36688         var dragEl = n.ddel;
36689         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
36690         var y = Roo.lib.Event.getPageY(e);
36691         //var noAppend = tn.allowChildren === false || tn.isLeaf();
36692         
36693         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
36694         var noAppend = tn.allowChildren === false;
36695         if(this.appendOnly || tn.parentNode.allowChildren === false){
36696             return noAppend ? false : "append";
36697         }
36698         var noBelow = false;
36699         if(!this.allowParentInsert){
36700             noBelow = tn.hasChildNodes() && tn.isExpanded();
36701         }
36702         var q = (b - t) / (noAppend ? 2 : 3);
36703         if(y >= t && y < (t + q)){
36704             return "above";
36705         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
36706             return "below";
36707         }else{
36708             return "append";
36709         }
36710     },
36711     
36712     onNodeEnter : function(n, dd, e, data)
36713     {
36714         this.cancelExpand();
36715     },
36716     
36717     onNodeOver : function(n, dd, e, data)
36718     {
36719        
36720         var pt = this.getDropPoint(e, n, dd);
36721         var node = n.node;
36722         
36723         // auto node expand check
36724         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
36725             this.queueExpand(node);
36726         }else if(pt != "append"){
36727             this.cancelExpand();
36728         }
36729         
36730         // set the insert point style on the target node
36731         var returnCls = this.dropNotAllowed;
36732         if(this.isValidDropPoint(n, pt, dd, e, data)){
36733            if(pt){
36734                var el = n.ddel;
36735                var cls;
36736                if(pt == "above"){
36737                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
36738                    cls = "x-tree-drag-insert-above";
36739                }else if(pt == "below"){
36740                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
36741                    cls = "x-tree-drag-insert-below";
36742                }else{
36743                    returnCls = "x-tree-drop-ok-append";
36744                    cls = "x-tree-drag-append";
36745                }
36746                if(this.lastInsertClass != cls){
36747                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
36748                    this.lastInsertClass = cls;
36749                }
36750            }
36751        }
36752        return returnCls;
36753     },
36754     
36755     onNodeOut : function(n, dd, e, data){
36756         
36757         this.cancelExpand();
36758         this.removeDropIndicators(n);
36759     },
36760     
36761     onNodeDrop : function(n, dd, e, data){
36762         var point = this.getDropPoint(e, n, dd);
36763         var targetNode = n.node;
36764         targetNode.ui.startDrop();
36765         if(!this.isValidDropPoint(n, point, dd, e, data)){
36766             targetNode.ui.endDrop();
36767             return false;
36768         }
36769         // first try to find the drop node
36770         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
36771         var dropEvent = {
36772             tree : this.tree,
36773             target: targetNode,
36774             data: data,
36775             point: point,
36776             source: dd,
36777             rawEvent: e,
36778             dropNode: dropNode,
36779             cancel: !dropNode   
36780         };
36781         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
36782         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
36783             targetNode.ui.endDrop();
36784             return false;
36785         }
36786         // allow target changing
36787         targetNode = dropEvent.target;
36788         if(point == "append" && !targetNode.isExpanded()){
36789             targetNode.expand(false, null, function(){
36790                 this.completeDrop(dropEvent);
36791             }.createDelegate(this));
36792         }else{
36793             this.completeDrop(dropEvent);
36794         }
36795         return true;
36796     },
36797     
36798     completeDrop : function(de){
36799         var ns = de.dropNode, p = de.point, t = de.target;
36800         if(!(ns instanceof Array)){
36801             ns = [ns];
36802         }
36803         var n;
36804         for(var i = 0, len = ns.length; i < len; i++){
36805             n = ns[i];
36806             if(p == "above"){
36807                 t.parentNode.insertBefore(n, t);
36808             }else if(p == "below"){
36809                 t.parentNode.insertBefore(n, t.nextSibling);
36810             }else{
36811                 t.appendChild(n);
36812             }
36813         }
36814         n.ui.focus();
36815         if(this.tree.hlDrop){
36816             n.ui.highlight();
36817         }
36818         t.ui.endDrop();
36819         this.tree.fireEvent("nodedrop", de);
36820     },
36821     
36822     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
36823         if(this.tree.hlDrop){
36824             dropNode.ui.focus();
36825             dropNode.ui.highlight();
36826         }
36827         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
36828     },
36829     
36830     getTree : function(){
36831         return this.tree;
36832     },
36833     
36834     removeDropIndicators : function(n){
36835         if(n && n.ddel){
36836             var el = n.ddel;
36837             Roo.fly(el).removeClass([
36838                     "x-tree-drag-insert-above",
36839                     "x-tree-drag-insert-below",
36840                     "x-tree-drag-append"]);
36841             this.lastInsertClass = "_noclass";
36842         }
36843     },
36844     
36845     beforeDragDrop : function(target, e, id){
36846         this.cancelExpand();
36847         return true;
36848     },
36849     
36850     afterRepair : function(data){
36851         if(data && Roo.enableFx){
36852             data.node.ui.highlight();
36853         }
36854         this.hideProxy();
36855     } 
36856     
36857 });
36858
36859 }
36860 /*
36861  * Based on:
36862  * Ext JS Library 1.1.1
36863  * Copyright(c) 2006-2007, Ext JS, LLC.
36864  *
36865  * Originally Released Under LGPL - original licence link has changed is not relivant.
36866  *
36867  * Fork - LGPL
36868  * <script type="text/javascript">
36869  */
36870  
36871
36872 if(Roo.dd.DragZone){
36873 Roo.tree.TreeDragZone = function(tree, config){
36874     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
36875     this.tree = tree;
36876 };
36877
36878 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
36879     ddGroup : "TreeDD",
36880    
36881     onBeforeDrag : function(data, e){
36882         var n = data.node;
36883         return n && n.draggable && !n.disabled;
36884     },
36885      
36886     
36887     onInitDrag : function(e){
36888         var data = this.dragData;
36889         this.tree.getSelectionModel().select(data.node);
36890         this.proxy.update("");
36891         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
36892         this.tree.fireEvent("startdrag", this.tree, data.node, e);
36893     },
36894     
36895     getRepairXY : function(e, data){
36896         return data.node.ui.getDDRepairXY();
36897     },
36898     
36899     onEndDrag : function(data, e){
36900         this.tree.fireEvent("enddrag", this.tree, data.node, e);
36901         
36902         
36903     },
36904     
36905     onValidDrop : function(dd, e, id){
36906         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
36907         this.hideProxy();
36908     },
36909     
36910     beforeInvalidDrop : function(e, id){
36911         // this scrolls the original position back into view
36912         var sm = this.tree.getSelectionModel();
36913         sm.clearSelections();
36914         sm.select(this.dragData.node);
36915     }
36916 });
36917 }/*
36918  * Based on:
36919  * Ext JS Library 1.1.1
36920  * Copyright(c) 2006-2007, Ext JS, LLC.
36921  *
36922  * Originally Released Under LGPL - original licence link has changed is not relivant.
36923  *
36924  * Fork - LGPL
36925  * <script type="text/javascript">
36926  */
36927 /**
36928  * @class Roo.tree.TreeEditor
36929  * @extends Roo.Editor
36930  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
36931  * as the editor field.
36932  * @constructor
36933  * @param {Object} config (used to be the tree panel.)
36934  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
36935  * 
36936  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
36937  * @cfg {Roo.form.TextField|Object} field The field configuration
36938  *
36939  * 
36940  */
36941 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
36942     var tree = config;
36943     var field;
36944     if (oldconfig) { // old style..
36945         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
36946     } else {
36947         // new style..
36948         tree = config.tree;
36949         config.field = config.field  || {};
36950         config.field.xtype = 'TextField';
36951         field = Roo.factory(config.field, Roo.form);
36952     }
36953     config = config || {};
36954     
36955     
36956     this.addEvents({
36957         /**
36958          * @event beforenodeedit
36959          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
36960          * false from the handler of this event.
36961          * @param {Editor} this
36962          * @param {Roo.tree.Node} node 
36963          */
36964         "beforenodeedit" : true
36965     });
36966     
36967     //Roo.log(config);
36968     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
36969
36970     this.tree = tree;
36971
36972     tree.on('beforeclick', this.beforeNodeClick, this);
36973     tree.getTreeEl().on('mousedown', this.hide, this);
36974     this.on('complete', this.updateNode, this);
36975     this.on('beforestartedit', this.fitToTree, this);
36976     this.on('startedit', this.bindScroll, this, {delay:10});
36977     this.on('specialkey', this.onSpecialKey, this);
36978 };
36979
36980 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
36981     /**
36982      * @cfg {String} alignment
36983      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
36984      */
36985     alignment: "l-l",
36986     // inherit
36987     autoSize: false,
36988     /**
36989      * @cfg {Boolean} hideEl
36990      * True to hide the bound element while the editor is displayed (defaults to false)
36991      */
36992     hideEl : false,
36993     /**
36994      * @cfg {String} cls
36995      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
36996      */
36997     cls: "x-small-editor x-tree-editor",
36998     /**
36999      * @cfg {Boolean} shim
37000      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
37001      */
37002     shim:false,
37003     // inherit
37004     shadow:"frame",
37005     /**
37006      * @cfg {Number} maxWidth
37007      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
37008      * the containing tree element's size, it will be automatically limited for you to the container width, taking
37009      * scroll and client offsets into account prior to each edit.
37010      */
37011     maxWidth: 250,
37012
37013     editDelay : 350,
37014
37015     // private
37016     fitToTree : function(ed, el){
37017         var td = this.tree.getTreeEl().dom, nd = el.dom;
37018         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
37019             td.scrollLeft = nd.offsetLeft;
37020         }
37021         var w = Math.min(
37022                 this.maxWidth,
37023                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
37024         this.setSize(w, '');
37025         
37026         return this.fireEvent('beforenodeedit', this, this.editNode);
37027         
37028     },
37029
37030     // private
37031     triggerEdit : function(node){
37032         this.completeEdit();
37033         this.editNode = node;
37034         this.startEdit(node.ui.textNode, node.text);
37035     },
37036
37037     // private
37038     bindScroll : function(){
37039         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
37040     },
37041
37042     // private
37043     beforeNodeClick : function(node, e){
37044         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
37045         this.lastClick = new Date();
37046         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
37047             e.stopEvent();
37048             this.triggerEdit(node);
37049             return false;
37050         }
37051         return true;
37052     },
37053
37054     // private
37055     updateNode : function(ed, value){
37056         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
37057         this.editNode.setText(value);
37058     },
37059
37060     // private
37061     onHide : function(){
37062         Roo.tree.TreeEditor.superclass.onHide.call(this);
37063         if(this.editNode){
37064             this.editNode.ui.focus();
37065         }
37066     },
37067
37068     // private
37069     onSpecialKey : function(field, e){
37070         var k = e.getKey();
37071         if(k == e.ESC){
37072             e.stopEvent();
37073             this.cancelEdit();
37074         }else if(k == e.ENTER && !e.hasModifier()){
37075             e.stopEvent();
37076             this.completeEdit();
37077         }
37078     }
37079 });//<Script type="text/javascript">
37080 /*
37081  * Based on:
37082  * Ext JS Library 1.1.1
37083  * Copyright(c) 2006-2007, Ext JS, LLC.
37084  *
37085  * Originally Released Under LGPL - original licence link has changed is not relivant.
37086  *
37087  * Fork - LGPL
37088  * <script type="text/javascript">
37089  */
37090  
37091 /**
37092  * Not documented??? - probably should be...
37093  */
37094
37095 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
37096     //focus: Roo.emptyFn, // prevent odd scrolling behavior
37097     
37098     renderElements : function(n, a, targetNode, bulkRender){
37099         //consel.log("renderElements?");
37100         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
37101
37102         var t = n.getOwnerTree();
37103         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
37104         
37105         var cols = t.columns;
37106         var bw = t.borderWidth;
37107         var c = cols[0];
37108         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
37109          var cb = typeof a.checked == "boolean";
37110         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
37111         var colcls = 'x-t-' + tid + '-c0';
37112         var buf = [
37113             '<li class="x-tree-node">',
37114             
37115                 
37116                 '<div class="x-tree-node-el ', a.cls,'">',
37117                     // extran...
37118                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
37119                 
37120                 
37121                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
37122                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
37123                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
37124                            (a.icon ? ' x-tree-node-inline-icon' : ''),
37125                            (a.iconCls ? ' '+a.iconCls : ''),
37126                            '" unselectable="on" />',
37127                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
37128                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
37129                              
37130                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
37131                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
37132                             '<span unselectable="on" qtip="' + tx + '">',
37133                              tx,
37134                              '</span></a>' ,
37135                     '</div>',
37136                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
37137                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
37138                  ];
37139         for(var i = 1, len = cols.length; i < len; i++){
37140             c = cols[i];
37141             colcls = 'x-t-' + tid + '-c' +i;
37142             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
37143             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
37144                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
37145                       "</div>");
37146          }
37147          
37148          buf.push(
37149             '</a>',
37150             '<div class="x-clear"></div></div>',
37151             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
37152             "</li>");
37153         
37154         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
37155             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
37156                                 n.nextSibling.ui.getEl(), buf.join(""));
37157         }else{
37158             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
37159         }
37160         var el = this.wrap.firstChild;
37161         this.elRow = el;
37162         this.elNode = el.firstChild;
37163         this.ranchor = el.childNodes[1];
37164         this.ctNode = this.wrap.childNodes[1];
37165         var cs = el.firstChild.childNodes;
37166         this.indentNode = cs[0];
37167         this.ecNode = cs[1];
37168         this.iconNode = cs[2];
37169         var index = 3;
37170         if(cb){
37171             this.checkbox = cs[3];
37172             index++;
37173         }
37174         this.anchor = cs[index];
37175         
37176         this.textNode = cs[index].firstChild;
37177         
37178         //el.on("click", this.onClick, this);
37179         //el.on("dblclick", this.onDblClick, this);
37180         
37181         
37182        // console.log(this);
37183     },
37184     initEvents : function(){
37185         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
37186         
37187             
37188         var a = this.ranchor;
37189
37190         var el = Roo.get(a);
37191
37192         if(Roo.isOpera){ // opera render bug ignores the CSS
37193             el.setStyle("text-decoration", "none");
37194         }
37195
37196         el.on("click", this.onClick, this);
37197         el.on("dblclick", this.onDblClick, this);
37198         el.on("contextmenu", this.onContextMenu, this);
37199         
37200     },
37201     
37202     /*onSelectedChange : function(state){
37203         if(state){
37204             this.focus();
37205             this.addClass("x-tree-selected");
37206         }else{
37207             //this.blur();
37208             this.removeClass("x-tree-selected");
37209         }
37210     },*/
37211     addClass : function(cls){
37212         if(this.elRow){
37213             Roo.fly(this.elRow).addClass(cls);
37214         }
37215         
37216     },
37217     
37218     
37219     removeClass : function(cls){
37220         if(this.elRow){
37221             Roo.fly(this.elRow).removeClass(cls);
37222         }
37223     }
37224
37225     
37226     
37227 });//<Script type="text/javascript">
37228
37229 /*
37230  * Based on:
37231  * Ext JS Library 1.1.1
37232  * Copyright(c) 2006-2007, Ext JS, LLC.
37233  *
37234  * Originally Released Under LGPL - original licence link has changed is not relivant.
37235  *
37236  * Fork - LGPL
37237  * <script type="text/javascript">
37238  */
37239  
37240
37241 /**
37242  * @class Roo.tree.ColumnTree
37243  * @extends Roo.data.TreePanel
37244  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
37245  * @cfg {int} borderWidth  compined right/left border allowance
37246  * @constructor
37247  * @param {String/HTMLElement/Element} el The container element
37248  * @param {Object} config
37249  */
37250 Roo.tree.ColumnTree =  function(el, config)
37251 {
37252    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
37253    this.addEvents({
37254         /**
37255         * @event resize
37256         * Fire this event on a container when it resizes
37257         * @param {int} w Width
37258         * @param {int} h Height
37259         */
37260        "resize" : true
37261     });
37262     this.on('resize', this.onResize, this);
37263 };
37264
37265 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
37266     //lines:false,
37267     
37268     
37269     borderWidth: Roo.isBorderBox ? 0 : 2, 
37270     headEls : false,
37271     
37272     render : function(){
37273         // add the header.....
37274        
37275         Roo.tree.ColumnTree.superclass.render.apply(this);
37276         
37277         this.el.addClass('x-column-tree');
37278         
37279         this.headers = this.el.createChild(
37280             {cls:'x-tree-headers'},this.innerCt.dom);
37281    
37282         var cols = this.columns, c;
37283         var totalWidth = 0;
37284         this.headEls = [];
37285         var  len = cols.length;
37286         for(var i = 0; i < len; i++){
37287              c = cols[i];
37288              totalWidth += c.width;
37289             this.headEls.push(this.headers.createChild({
37290                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
37291                  cn: {
37292                      cls:'x-tree-hd-text',
37293                      html: c.header
37294                  },
37295                  style:'width:'+(c.width-this.borderWidth)+'px;'
37296              }));
37297         }
37298         this.headers.createChild({cls:'x-clear'});
37299         // prevent floats from wrapping when clipped
37300         this.headers.setWidth(totalWidth);
37301         //this.innerCt.setWidth(totalWidth);
37302         this.innerCt.setStyle({ overflow: 'auto' });
37303         this.onResize(this.width, this.height);
37304              
37305         
37306     },
37307     onResize : function(w,h)
37308     {
37309         this.height = h;
37310         this.width = w;
37311         // resize cols..
37312         this.innerCt.setWidth(this.width);
37313         this.innerCt.setHeight(this.height-20);
37314         
37315         // headers...
37316         var cols = this.columns, c;
37317         var totalWidth = 0;
37318         var expEl = false;
37319         var len = cols.length;
37320         for(var i = 0; i < len; i++){
37321             c = cols[i];
37322             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
37323                 // it's the expander..
37324                 expEl  = this.headEls[i];
37325                 continue;
37326             }
37327             totalWidth += c.width;
37328             
37329         }
37330         if (expEl) {
37331             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
37332         }
37333         this.headers.setWidth(w-20);
37334
37335         
37336         
37337         
37338     }
37339 });
37340 /*
37341  * Based on:
37342  * Ext JS Library 1.1.1
37343  * Copyright(c) 2006-2007, Ext JS, LLC.
37344  *
37345  * Originally Released Under LGPL - original licence link has changed is not relivant.
37346  *
37347  * Fork - LGPL
37348  * <script type="text/javascript">
37349  */
37350  
37351 /**
37352  * @class Roo.menu.Menu
37353  * @extends Roo.util.Observable
37354  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
37355  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
37356  * @constructor
37357  * Creates a new Menu
37358  * @param {Object} config Configuration options
37359  */
37360 Roo.menu.Menu = function(config){
37361     
37362     Roo.menu.Menu.superclass.constructor.call(this, config);
37363     
37364     this.id = this.id || Roo.id();
37365     this.addEvents({
37366         /**
37367          * @event beforeshow
37368          * Fires before this menu is displayed
37369          * @param {Roo.menu.Menu} this
37370          */
37371         beforeshow : true,
37372         /**
37373          * @event beforehide
37374          * Fires before this menu is hidden
37375          * @param {Roo.menu.Menu} this
37376          */
37377         beforehide : true,
37378         /**
37379          * @event show
37380          * Fires after this menu is displayed
37381          * @param {Roo.menu.Menu} this
37382          */
37383         show : true,
37384         /**
37385          * @event hide
37386          * Fires after this menu is hidden
37387          * @param {Roo.menu.Menu} this
37388          */
37389         hide : true,
37390         /**
37391          * @event click
37392          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
37393          * @param {Roo.menu.Menu} this
37394          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37395          * @param {Roo.EventObject} e
37396          */
37397         click : true,
37398         /**
37399          * @event mouseover
37400          * Fires when the mouse is hovering over this menu
37401          * @param {Roo.menu.Menu} this
37402          * @param {Roo.EventObject} e
37403          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37404          */
37405         mouseover : true,
37406         /**
37407          * @event mouseout
37408          * Fires when the mouse exits this menu
37409          * @param {Roo.menu.Menu} this
37410          * @param {Roo.EventObject} e
37411          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37412          */
37413         mouseout : true,
37414         /**
37415          * @event itemclick
37416          * Fires when a menu item contained in this menu is clicked
37417          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
37418          * @param {Roo.EventObject} e
37419          */
37420         itemclick: true
37421     });
37422     if (this.registerMenu) {
37423         Roo.menu.MenuMgr.register(this);
37424     }
37425     
37426     var mis = this.items;
37427     this.items = new Roo.util.MixedCollection();
37428     if(mis){
37429         this.add.apply(this, mis);
37430     }
37431 };
37432
37433 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
37434     /**
37435      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
37436      */
37437     minWidth : 120,
37438     /**
37439      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
37440      * for bottom-right shadow (defaults to "sides")
37441      */
37442     shadow : "sides",
37443     /**
37444      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
37445      * this menu (defaults to "tl-tr?")
37446      */
37447     subMenuAlign : "tl-tr?",
37448     /**
37449      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
37450      * relative to its element of origin (defaults to "tl-bl?")
37451      */
37452     defaultAlign : "tl-bl?",
37453     /**
37454      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
37455      */
37456     allowOtherMenus : false,
37457     /**
37458      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
37459      */
37460     registerMenu : true,
37461
37462     hidden:true,
37463
37464     // private
37465     render : function(){
37466         if(this.el){
37467             return;
37468         }
37469         var el = this.el = new Roo.Layer({
37470             cls: "x-menu",
37471             shadow:this.shadow,
37472             constrain: false,
37473             parentEl: this.parentEl || document.body,
37474             zindex:15000
37475         });
37476
37477         this.keyNav = new Roo.menu.MenuNav(this);
37478
37479         if(this.plain){
37480             el.addClass("x-menu-plain");
37481         }
37482         if(this.cls){
37483             el.addClass(this.cls);
37484         }
37485         // generic focus element
37486         this.focusEl = el.createChild({
37487             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
37488         });
37489         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
37490         //disabling touch- as it's causing issues ..
37491         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
37492         ul.on('click'   , this.onClick, this);
37493         
37494         
37495         ul.on("mouseover", this.onMouseOver, this);
37496         ul.on("mouseout", this.onMouseOut, this);
37497         this.items.each(function(item){
37498             if (item.hidden) {
37499                 return;
37500             }
37501             
37502             var li = document.createElement("li");
37503             li.className = "x-menu-list-item";
37504             ul.dom.appendChild(li);
37505             item.render(li, this);
37506         }, this);
37507         this.ul = ul;
37508         this.autoWidth();
37509     },
37510
37511     // private
37512     autoWidth : function(){
37513         var el = this.el, ul = this.ul;
37514         if(!el){
37515             return;
37516         }
37517         var w = this.width;
37518         if(w){
37519             el.setWidth(w);
37520         }else if(Roo.isIE){
37521             el.setWidth(this.minWidth);
37522             var t = el.dom.offsetWidth; // force recalc
37523             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
37524         }
37525     },
37526
37527     // private
37528     delayAutoWidth : function(){
37529         if(this.rendered){
37530             if(!this.awTask){
37531                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
37532             }
37533             this.awTask.delay(20);
37534         }
37535     },
37536
37537     // private
37538     findTargetItem : function(e){
37539         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
37540         if(t && t.menuItemId){
37541             return this.items.get(t.menuItemId);
37542         }
37543     },
37544
37545     // private
37546     onClick : function(e){
37547         Roo.log("menu.onClick");
37548         var t = this.findTargetItem(e);
37549         if(!t){
37550             return;
37551         }
37552         Roo.log(e);
37553         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
37554             if(t == this.activeItem && t.shouldDeactivate(e)){
37555                 this.activeItem.deactivate();
37556                 delete this.activeItem;
37557                 return;
37558             }
37559             if(t.canActivate){
37560                 this.setActiveItem(t, true);
37561             }
37562             return;
37563             
37564             
37565         }
37566         
37567         t.onClick(e);
37568         this.fireEvent("click", this, t, e);
37569     },
37570
37571     // private
37572     setActiveItem : function(item, autoExpand){
37573         if(item != this.activeItem){
37574             if(this.activeItem){
37575                 this.activeItem.deactivate();
37576             }
37577             this.activeItem = item;
37578             item.activate(autoExpand);
37579         }else if(autoExpand){
37580             item.expandMenu();
37581         }
37582     },
37583
37584     // private
37585     tryActivate : function(start, step){
37586         var items = this.items;
37587         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
37588             var item = items.get(i);
37589             if(!item.disabled && item.canActivate){
37590                 this.setActiveItem(item, false);
37591                 return item;
37592             }
37593         }
37594         return false;
37595     },
37596
37597     // private
37598     onMouseOver : function(e){
37599         var t;
37600         if(t = this.findTargetItem(e)){
37601             if(t.canActivate && !t.disabled){
37602                 this.setActiveItem(t, true);
37603             }
37604         }
37605         this.fireEvent("mouseover", this, e, t);
37606     },
37607
37608     // private
37609     onMouseOut : function(e){
37610         var t;
37611         if(t = this.findTargetItem(e)){
37612             if(t == this.activeItem && t.shouldDeactivate(e)){
37613                 this.activeItem.deactivate();
37614                 delete this.activeItem;
37615             }
37616         }
37617         this.fireEvent("mouseout", this, e, t);
37618     },
37619
37620     /**
37621      * Read-only.  Returns true if the menu is currently displayed, else false.
37622      * @type Boolean
37623      */
37624     isVisible : function(){
37625         return this.el && !this.hidden;
37626     },
37627
37628     /**
37629      * Displays this menu relative to another element
37630      * @param {String/HTMLElement/Roo.Element} element The element to align to
37631      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
37632      * the element (defaults to this.defaultAlign)
37633      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37634      */
37635     show : function(el, pos, parentMenu){
37636         this.parentMenu = parentMenu;
37637         if(!this.el){
37638             this.render();
37639         }
37640         this.fireEvent("beforeshow", this);
37641         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
37642     },
37643
37644     /**
37645      * Displays this menu at a specific xy position
37646      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
37647      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37648      */
37649     showAt : function(xy, parentMenu, /* private: */_e){
37650         this.parentMenu = parentMenu;
37651         if(!this.el){
37652             this.render();
37653         }
37654         if(_e !== false){
37655             this.fireEvent("beforeshow", this);
37656             xy = this.el.adjustForConstraints(xy);
37657         }
37658         this.el.setXY(xy);
37659         this.el.show();
37660         this.hidden = false;
37661         this.focus();
37662         this.fireEvent("show", this);
37663     },
37664
37665     focus : function(){
37666         if(!this.hidden){
37667             this.doFocus.defer(50, this);
37668         }
37669     },
37670
37671     doFocus : function(){
37672         if(!this.hidden){
37673             this.focusEl.focus();
37674         }
37675     },
37676
37677     /**
37678      * Hides this menu and optionally all parent menus
37679      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
37680      */
37681     hide : function(deep){
37682         if(this.el && this.isVisible()){
37683             this.fireEvent("beforehide", this);
37684             if(this.activeItem){
37685                 this.activeItem.deactivate();
37686                 this.activeItem = null;
37687             }
37688             this.el.hide();
37689             this.hidden = true;
37690             this.fireEvent("hide", this);
37691         }
37692         if(deep === true && this.parentMenu){
37693             this.parentMenu.hide(true);
37694         }
37695     },
37696
37697     /**
37698      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
37699      * Any of the following are valid:
37700      * <ul>
37701      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
37702      * <li>An HTMLElement object which will be converted to a menu item</li>
37703      * <li>A menu item config object that will be created as a new menu item</li>
37704      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
37705      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
37706      * </ul>
37707      * Usage:
37708      * <pre><code>
37709 // Create the menu
37710 var menu = new Roo.menu.Menu();
37711
37712 // Create a menu item to add by reference
37713 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
37714
37715 // Add a bunch of items at once using different methods.
37716 // Only the last item added will be returned.
37717 var item = menu.add(
37718     menuItem,                // add existing item by ref
37719     'Dynamic Item',          // new TextItem
37720     '-',                     // new separator
37721     { text: 'Config Item' }  // new item by config
37722 );
37723 </code></pre>
37724      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
37725      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
37726      */
37727     add : function(){
37728         var a = arguments, l = a.length, item;
37729         for(var i = 0; i < l; i++){
37730             var el = a[i];
37731             if ((typeof(el) == "object") && el.xtype && el.xns) {
37732                 el = Roo.factory(el, Roo.menu);
37733             }
37734             
37735             if(el.render){ // some kind of Item
37736                 item = this.addItem(el);
37737             }else if(typeof el == "string"){ // string
37738                 if(el == "separator" || el == "-"){
37739                     item = this.addSeparator();
37740                 }else{
37741                     item = this.addText(el);
37742                 }
37743             }else if(el.tagName || el.el){ // element
37744                 item = this.addElement(el);
37745             }else if(typeof el == "object"){ // must be menu item config?
37746                 item = this.addMenuItem(el);
37747             }
37748         }
37749         return item;
37750     },
37751
37752     /**
37753      * Returns this menu's underlying {@link Roo.Element} object
37754      * @return {Roo.Element} The element
37755      */
37756     getEl : function(){
37757         if(!this.el){
37758             this.render();
37759         }
37760         return this.el;
37761     },
37762
37763     /**
37764      * Adds a separator bar to the menu
37765      * @return {Roo.menu.Item} The menu item that was added
37766      */
37767     addSeparator : function(){
37768         return this.addItem(new Roo.menu.Separator());
37769     },
37770
37771     /**
37772      * Adds an {@link Roo.Element} object to the menu
37773      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
37774      * @return {Roo.menu.Item} The menu item that was added
37775      */
37776     addElement : function(el){
37777         return this.addItem(new Roo.menu.BaseItem(el));
37778     },
37779
37780     /**
37781      * Adds an existing object based on {@link Roo.menu.Item} to the menu
37782      * @param {Roo.menu.Item} item The menu item to add
37783      * @return {Roo.menu.Item} The menu item that was added
37784      */
37785     addItem : function(item){
37786         this.items.add(item);
37787         if(this.ul){
37788             var li = document.createElement("li");
37789             li.className = "x-menu-list-item";
37790             this.ul.dom.appendChild(li);
37791             item.render(li, this);
37792             this.delayAutoWidth();
37793         }
37794         return item;
37795     },
37796
37797     /**
37798      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
37799      * @param {Object} config A MenuItem config object
37800      * @return {Roo.menu.Item} The menu item that was added
37801      */
37802     addMenuItem : function(config){
37803         if(!(config instanceof Roo.menu.Item)){
37804             if(typeof config.checked == "boolean"){ // must be check menu item config?
37805                 config = new Roo.menu.CheckItem(config);
37806             }else{
37807                 config = new Roo.menu.Item(config);
37808             }
37809         }
37810         return this.addItem(config);
37811     },
37812
37813     /**
37814      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
37815      * @param {String} text The text to display in the menu item
37816      * @return {Roo.menu.Item} The menu item that was added
37817      */
37818     addText : function(text){
37819         return this.addItem(new Roo.menu.TextItem({ text : text }));
37820     },
37821
37822     /**
37823      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
37824      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
37825      * @param {Roo.menu.Item} item The menu item to add
37826      * @return {Roo.menu.Item} The menu item that was added
37827      */
37828     insert : function(index, item){
37829         this.items.insert(index, item);
37830         if(this.ul){
37831             var li = document.createElement("li");
37832             li.className = "x-menu-list-item";
37833             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
37834             item.render(li, this);
37835             this.delayAutoWidth();
37836         }
37837         return item;
37838     },
37839
37840     /**
37841      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
37842      * @param {Roo.menu.Item} item The menu item to remove
37843      */
37844     remove : function(item){
37845         this.items.removeKey(item.id);
37846         item.destroy();
37847     },
37848
37849     /**
37850      * Removes and destroys all items in the menu
37851      */
37852     removeAll : function(){
37853         var f;
37854         while(f = this.items.first()){
37855             this.remove(f);
37856         }
37857     }
37858 });
37859
37860 // MenuNav is a private utility class used internally by the Menu
37861 Roo.menu.MenuNav = function(menu){
37862     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
37863     this.scope = this.menu = menu;
37864 };
37865
37866 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
37867     doRelay : function(e, h){
37868         var k = e.getKey();
37869         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
37870             this.menu.tryActivate(0, 1);
37871             return false;
37872         }
37873         return h.call(this.scope || this, e, this.menu);
37874     },
37875
37876     up : function(e, m){
37877         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
37878             m.tryActivate(m.items.length-1, -1);
37879         }
37880     },
37881
37882     down : function(e, m){
37883         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
37884             m.tryActivate(0, 1);
37885         }
37886     },
37887
37888     right : function(e, m){
37889         if(m.activeItem){
37890             m.activeItem.expandMenu(true);
37891         }
37892     },
37893
37894     left : function(e, m){
37895         m.hide();
37896         if(m.parentMenu && m.parentMenu.activeItem){
37897             m.parentMenu.activeItem.activate();
37898         }
37899     },
37900
37901     enter : function(e, m){
37902         if(m.activeItem){
37903             e.stopPropagation();
37904             m.activeItem.onClick(e);
37905             m.fireEvent("click", this, m.activeItem);
37906             return true;
37907         }
37908     }
37909 });/*
37910  * Based on:
37911  * Ext JS Library 1.1.1
37912  * Copyright(c) 2006-2007, Ext JS, LLC.
37913  *
37914  * Originally Released Under LGPL - original licence link has changed is not relivant.
37915  *
37916  * Fork - LGPL
37917  * <script type="text/javascript">
37918  */
37919  
37920 /**
37921  * @class Roo.menu.MenuMgr
37922  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
37923  * @singleton
37924  */
37925 Roo.menu.MenuMgr = function(){
37926    var menus, active, groups = {}, attached = false, lastShow = new Date();
37927
37928    // private - called when first menu is created
37929    function init(){
37930        menus = {};
37931        active = new Roo.util.MixedCollection();
37932        Roo.get(document).addKeyListener(27, function(){
37933            if(active.length > 0){
37934                hideAll();
37935            }
37936        });
37937    }
37938
37939    // private
37940    function hideAll(){
37941        if(active && active.length > 0){
37942            var c = active.clone();
37943            c.each(function(m){
37944                m.hide();
37945            });
37946        }
37947    }
37948
37949    // private
37950    function onHide(m){
37951        active.remove(m);
37952        if(active.length < 1){
37953            Roo.get(document).un("mousedown", onMouseDown);
37954            attached = false;
37955        }
37956    }
37957
37958    // private
37959    function onShow(m){
37960        var last = active.last();
37961        lastShow = new Date();
37962        active.add(m);
37963        if(!attached){
37964            Roo.get(document).on("mousedown", onMouseDown);
37965            attached = true;
37966        }
37967        if(m.parentMenu){
37968           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
37969           m.parentMenu.activeChild = m;
37970        }else if(last && last.isVisible()){
37971           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
37972        }
37973    }
37974
37975    // private
37976    function onBeforeHide(m){
37977        if(m.activeChild){
37978            m.activeChild.hide();
37979        }
37980        if(m.autoHideTimer){
37981            clearTimeout(m.autoHideTimer);
37982            delete m.autoHideTimer;
37983        }
37984    }
37985
37986    // private
37987    function onBeforeShow(m){
37988        var pm = m.parentMenu;
37989        if(!pm && !m.allowOtherMenus){
37990            hideAll();
37991        }else if(pm && pm.activeChild && active != m){
37992            pm.activeChild.hide();
37993        }
37994    }
37995
37996    // private
37997    function onMouseDown(e){
37998        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
37999            hideAll();
38000        }
38001    }
38002
38003    // private
38004    function onBeforeCheck(mi, state){
38005        if(state){
38006            var g = groups[mi.group];
38007            for(var i = 0, l = g.length; i < l; i++){
38008                if(g[i] != mi){
38009                    g[i].setChecked(false);
38010                }
38011            }
38012        }
38013    }
38014
38015    return {
38016
38017        /**
38018         * Hides all menus that are currently visible
38019         */
38020        hideAll : function(){
38021             hideAll();  
38022        },
38023
38024        // private
38025        register : function(menu){
38026            if(!menus){
38027                init();
38028            }
38029            menus[menu.id] = menu;
38030            menu.on("beforehide", onBeforeHide);
38031            menu.on("hide", onHide);
38032            menu.on("beforeshow", onBeforeShow);
38033            menu.on("show", onShow);
38034            var g = menu.group;
38035            if(g && menu.events["checkchange"]){
38036                if(!groups[g]){
38037                    groups[g] = [];
38038                }
38039                groups[g].push(menu);
38040                menu.on("checkchange", onCheck);
38041            }
38042        },
38043
38044         /**
38045          * Returns a {@link Roo.menu.Menu} object
38046          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
38047          * be used to generate and return a new Menu instance.
38048          */
38049        get : function(menu){
38050            if(typeof menu == "string"){ // menu id
38051                return menus[menu];
38052            }else if(menu.events){  // menu instance
38053                return menu;
38054            }else if(typeof menu.length == 'number'){ // array of menu items?
38055                return new Roo.menu.Menu({items:menu});
38056            }else{ // otherwise, must be a config
38057                return new Roo.menu.Menu(menu);
38058            }
38059        },
38060
38061        // private
38062        unregister : function(menu){
38063            delete menus[menu.id];
38064            menu.un("beforehide", onBeforeHide);
38065            menu.un("hide", onHide);
38066            menu.un("beforeshow", onBeforeShow);
38067            menu.un("show", onShow);
38068            var g = menu.group;
38069            if(g && menu.events["checkchange"]){
38070                groups[g].remove(menu);
38071                menu.un("checkchange", onCheck);
38072            }
38073        },
38074
38075        // private
38076        registerCheckable : function(menuItem){
38077            var g = menuItem.group;
38078            if(g){
38079                if(!groups[g]){
38080                    groups[g] = [];
38081                }
38082                groups[g].push(menuItem);
38083                menuItem.on("beforecheckchange", onBeforeCheck);
38084            }
38085        },
38086
38087        // private
38088        unregisterCheckable : function(menuItem){
38089            var g = menuItem.group;
38090            if(g){
38091                groups[g].remove(menuItem);
38092                menuItem.un("beforecheckchange", onBeforeCheck);
38093            }
38094        }
38095    };
38096 }();/*
38097  * Based on:
38098  * Ext JS Library 1.1.1
38099  * Copyright(c) 2006-2007, Ext JS, LLC.
38100  *
38101  * Originally Released Under LGPL - original licence link has changed is not relivant.
38102  *
38103  * Fork - LGPL
38104  * <script type="text/javascript">
38105  */
38106  
38107
38108 /**
38109  * @class Roo.menu.BaseItem
38110  * @extends Roo.Component
38111  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
38112  * management and base configuration options shared by all menu components.
38113  * @constructor
38114  * Creates a new BaseItem
38115  * @param {Object} config Configuration options
38116  */
38117 Roo.menu.BaseItem = function(config){
38118     Roo.menu.BaseItem.superclass.constructor.call(this, config);
38119
38120     this.addEvents({
38121         /**
38122          * @event click
38123          * Fires when this item is clicked
38124          * @param {Roo.menu.BaseItem} this
38125          * @param {Roo.EventObject} e
38126          */
38127         click: true,
38128         /**
38129          * @event activate
38130          * Fires when this item is activated
38131          * @param {Roo.menu.BaseItem} this
38132          */
38133         activate : true,
38134         /**
38135          * @event deactivate
38136          * Fires when this item is deactivated
38137          * @param {Roo.menu.BaseItem} this
38138          */
38139         deactivate : true
38140     });
38141
38142     if(this.handler){
38143         this.on("click", this.handler, this.scope, true);
38144     }
38145 };
38146
38147 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
38148     /**
38149      * @cfg {Function} handler
38150      * A function that will handle the click event of this menu item (defaults to undefined)
38151      */
38152     /**
38153      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
38154      */
38155     canActivate : false,
38156     
38157      /**
38158      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
38159      */
38160     hidden: false,
38161     
38162     /**
38163      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
38164      */
38165     activeClass : "x-menu-item-active",
38166     /**
38167      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
38168      */
38169     hideOnClick : true,
38170     /**
38171      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
38172      */
38173     hideDelay : 100,
38174
38175     // private
38176     ctype: "Roo.menu.BaseItem",
38177
38178     // private
38179     actionMode : "container",
38180
38181     // private
38182     render : function(container, parentMenu){
38183         this.parentMenu = parentMenu;
38184         Roo.menu.BaseItem.superclass.render.call(this, container);
38185         this.container.menuItemId = this.id;
38186     },
38187
38188     // private
38189     onRender : function(container, position){
38190         this.el = Roo.get(this.el);
38191         container.dom.appendChild(this.el.dom);
38192     },
38193
38194     // private
38195     onClick : function(e){
38196         if(!this.disabled && this.fireEvent("click", this, e) !== false
38197                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
38198             this.handleClick(e);
38199         }else{
38200             e.stopEvent();
38201         }
38202     },
38203
38204     // private
38205     activate : function(){
38206         if(this.disabled){
38207             return false;
38208         }
38209         var li = this.container;
38210         li.addClass(this.activeClass);
38211         this.region = li.getRegion().adjust(2, 2, -2, -2);
38212         this.fireEvent("activate", this);
38213         return true;
38214     },
38215
38216     // private
38217     deactivate : function(){
38218         this.container.removeClass(this.activeClass);
38219         this.fireEvent("deactivate", this);
38220     },
38221
38222     // private
38223     shouldDeactivate : function(e){
38224         return !this.region || !this.region.contains(e.getPoint());
38225     },
38226
38227     // private
38228     handleClick : function(e){
38229         if(this.hideOnClick){
38230             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
38231         }
38232     },
38233
38234     // private
38235     expandMenu : function(autoActivate){
38236         // do nothing
38237     },
38238
38239     // private
38240     hideMenu : function(){
38241         // do nothing
38242     }
38243 });/*
38244  * Based on:
38245  * Ext JS Library 1.1.1
38246  * Copyright(c) 2006-2007, Ext JS, LLC.
38247  *
38248  * Originally Released Under LGPL - original licence link has changed is not relivant.
38249  *
38250  * Fork - LGPL
38251  * <script type="text/javascript">
38252  */
38253  
38254 /**
38255  * @class Roo.menu.Adapter
38256  * @extends Roo.menu.BaseItem
38257  * 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.
38258  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
38259  * @constructor
38260  * Creates a new Adapter
38261  * @param {Object} config Configuration options
38262  */
38263 Roo.menu.Adapter = function(component, config){
38264     Roo.menu.Adapter.superclass.constructor.call(this, config);
38265     this.component = component;
38266 };
38267 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
38268     // private
38269     canActivate : true,
38270
38271     // private
38272     onRender : function(container, position){
38273         this.component.render(container);
38274         this.el = this.component.getEl();
38275     },
38276
38277     // private
38278     activate : function(){
38279         if(this.disabled){
38280             return false;
38281         }
38282         this.component.focus();
38283         this.fireEvent("activate", this);
38284         return true;
38285     },
38286
38287     // private
38288     deactivate : function(){
38289         this.fireEvent("deactivate", this);
38290     },
38291
38292     // private
38293     disable : function(){
38294         this.component.disable();
38295         Roo.menu.Adapter.superclass.disable.call(this);
38296     },
38297
38298     // private
38299     enable : function(){
38300         this.component.enable();
38301         Roo.menu.Adapter.superclass.enable.call(this);
38302     }
38303 });/*
38304  * Based on:
38305  * Ext JS Library 1.1.1
38306  * Copyright(c) 2006-2007, Ext JS, LLC.
38307  *
38308  * Originally Released Under LGPL - original licence link has changed is not relivant.
38309  *
38310  * Fork - LGPL
38311  * <script type="text/javascript">
38312  */
38313
38314 /**
38315  * @class Roo.menu.TextItem
38316  * @extends Roo.menu.BaseItem
38317  * Adds a static text string to a menu, usually used as either a heading or group separator.
38318  * Note: old style constructor with text is still supported.
38319  * 
38320  * @constructor
38321  * Creates a new TextItem
38322  * @param {Object} cfg Configuration
38323  */
38324 Roo.menu.TextItem = function(cfg){
38325     if (typeof(cfg) == 'string') {
38326         this.text = cfg;
38327     } else {
38328         Roo.apply(this,cfg);
38329     }
38330     
38331     Roo.menu.TextItem.superclass.constructor.call(this);
38332 };
38333
38334 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
38335     /**
38336      * @cfg {Boolean} text Text to show on item.
38337      */
38338     text : '',
38339     
38340     /**
38341      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38342      */
38343     hideOnClick : false,
38344     /**
38345      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
38346      */
38347     itemCls : "x-menu-text",
38348
38349     // private
38350     onRender : function(){
38351         var s = document.createElement("span");
38352         s.className = this.itemCls;
38353         s.innerHTML = this.text;
38354         this.el = s;
38355         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
38356     }
38357 });/*
38358  * Based on:
38359  * Ext JS Library 1.1.1
38360  * Copyright(c) 2006-2007, Ext JS, LLC.
38361  *
38362  * Originally Released Under LGPL - original licence link has changed is not relivant.
38363  *
38364  * Fork - LGPL
38365  * <script type="text/javascript">
38366  */
38367
38368 /**
38369  * @class Roo.menu.Separator
38370  * @extends Roo.menu.BaseItem
38371  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
38372  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
38373  * @constructor
38374  * @param {Object} config Configuration options
38375  */
38376 Roo.menu.Separator = function(config){
38377     Roo.menu.Separator.superclass.constructor.call(this, config);
38378 };
38379
38380 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
38381     /**
38382      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
38383      */
38384     itemCls : "x-menu-sep",
38385     /**
38386      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38387      */
38388     hideOnClick : false,
38389
38390     // private
38391     onRender : function(li){
38392         var s = document.createElement("span");
38393         s.className = this.itemCls;
38394         s.innerHTML = "&#160;";
38395         this.el = s;
38396         li.addClass("x-menu-sep-li");
38397         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
38398     }
38399 });/*
38400  * Based on:
38401  * Ext JS Library 1.1.1
38402  * Copyright(c) 2006-2007, Ext JS, LLC.
38403  *
38404  * Originally Released Under LGPL - original licence link has changed is not relivant.
38405  *
38406  * Fork - LGPL
38407  * <script type="text/javascript">
38408  */
38409 /**
38410  * @class Roo.menu.Item
38411  * @extends Roo.menu.BaseItem
38412  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
38413  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
38414  * activation and click handling.
38415  * @constructor
38416  * Creates a new Item
38417  * @param {Object} config Configuration options
38418  */
38419 Roo.menu.Item = function(config){
38420     Roo.menu.Item.superclass.constructor.call(this, config);
38421     if(this.menu){
38422         this.menu = Roo.menu.MenuMgr.get(this.menu);
38423     }
38424 };
38425 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
38426     
38427     /**
38428      * @cfg {String} text
38429      * The text to show on the menu item.
38430      */
38431     text: '',
38432      /**
38433      * @cfg {String} HTML to render in menu
38434      * The text to show on the menu item (HTML version).
38435      */
38436     html: '',
38437     /**
38438      * @cfg {String} icon
38439      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
38440      */
38441     icon: undefined,
38442     /**
38443      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
38444      */
38445     itemCls : "x-menu-item",
38446     /**
38447      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
38448      */
38449     canActivate : true,
38450     /**
38451      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
38452      */
38453     showDelay: 200,
38454     // doc'd in BaseItem
38455     hideDelay: 200,
38456
38457     // private
38458     ctype: "Roo.menu.Item",
38459     
38460     // private
38461     onRender : function(container, position){
38462         var el = document.createElement("a");
38463         el.hideFocus = true;
38464         el.unselectable = "on";
38465         el.href = this.href || "#";
38466         if(this.hrefTarget){
38467             el.target = this.hrefTarget;
38468         }
38469         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
38470         
38471         var html = this.html.length ? this.html  : String.format('{0}',this.text);
38472         
38473         el.innerHTML = String.format(
38474                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
38475                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
38476         this.el = el;
38477         Roo.menu.Item.superclass.onRender.call(this, container, position);
38478     },
38479
38480     /**
38481      * Sets the text to display in this menu item
38482      * @param {String} text The text to display
38483      * @param {Boolean} isHTML true to indicate text is pure html.
38484      */
38485     setText : function(text, isHTML){
38486         if (isHTML) {
38487             this.html = text;
38488         } else {
38489             this.text = text;
38490             this.html = '';
38491         }
38492         if(this.rendered){
38493             var html = this.html.length ? this.html  : String.format('{0}',this.text);
38494      
38495             this.el.update(String.format(
38496                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
38497                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
38498             this.parentMenu.autoWidth();
38499         }
38500     },
38501
38502     // private
38503     handleClick : function(e){
38504         if(!this.href){ // if no link defined, stop the event automatically
38505             e.stopEvent();
38506         }
38507         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
38508     },
38509
38510     // private
38511     activate : function(autoExpand){
38512         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
38513             this.focus();
38514             if(autoExpand){
38515                 this.expandMenu();
38516             }
38517         }
38518         return true;
38519     },
38520
38521     // private
38522     shouldDeactivate : function(e){
38523         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
38524             if(this.menu && this.menu.isVisible()){
38525                 return !this.menu.getEl().getRegion().contains(e.getPoint());
38526             }
38527             return true;
38528         }
38529         return false;
38530     },
38531
38532     // private
38533     deactivate : function(){
38534         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
38535         this.hideMenu();
38536     },
38537
38538     // private
38539     expandMenu : function(autoActivate){
38540         if(!this.disabled && this.menu){
38541             clearTimeout(this.hideTimer);
38542             delete this.hideTimer;
38543             if(!this.menu.isVisible() && !this.showTimer){
38544                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
38545             }else if (this.menu.isVisible() && autoActivate){
38546                 this.menu.tryActivate(0, 1);
38547             }
38548         }
38549     },
38550
38551     // private
38552     deferExpand : function(autoActivate){
38553         delete this.showTimer;
38554         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
38555         if(autoActivate){
38556             this.menu.tryActivate(0, 1);
38557         }
38558     },
38559
38560     // private
38561     hideMenu : function(){
38562         clearTimeout(this.showTimer);
38563         delete this.showTimer;
38564         if(!this.hideTimer && this.menu && this.menu.isVisible()){
38565             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
38566         }
38567     },
38568
38569     // private
38570     deferHide : function(){
38571         delete this.hideTimer;
38572         this.menu.hide();
38573     }
38574 });/*
38575  * Based on:
38576  * Ext JS Library 1.1.1
38577  * Copyright(c) 2006-2007, Ext JS, LLC.
38578  *
38579  * Originally Released Under LGPL - original licence link has changed is not relivant.
38580  *
38581  * Fork - LGPL
38582  * <script type="text/javascript">
38583  */
38584  
38585 /**
38586  * @class Roo.menu.CheckItem
38587  * @extends Roo.menu.Item
38588  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
38589  * @constructor
38590  * Creates a new CheckItem
38591  * @param {Object} config Configuration options
38592  */
38593 Roo.menu.CheckItem = function(config){
38594     Roo.menu.CheckItem.superclass.constructor.call(this, config);
38595     this.addEvents({
38596         /**
38597          * @event beforecheckchange
38598          * Fires before the checked value is set, providing an opportunity to cancel if needed
38599          * @param {Roo.menu.CheckItem} this
38600          * @param {Boolean} checked The new checked value that will be set
38601          */
38602         "beforecheckchange" : true,
38603         /**
38604          * @event checkchange
38605          * Fires after the checked value has been set
38606          * @param {Roo.menu.CheckItem} this
38607          * @param {Boolean} checked The checked value that was set
38608          */
38609         "checkchange" : true
38610     });
38611     if(this.checkHandler){
38612         this.on('checkchange', this.checkHandler, this.scope);
38613     }
38614 };
38615 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
38616     /**
38617      * @cfg {String} group
38618      * All check items with the same group name will automatically be grouped into a single-select
38619      * radio button group (defaults to '')
38620      */
38621     /**
38622      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
38623      */
38624     itemCls : "x-menu-item x-menu-check-item",
38625     /**
38626      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
38627      */
38628     groupClass : "x-menu-group-item",
38629
38630     /**
38631      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
38632      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
38633      * initialized with checked = true will be rendered as checked.
38634      */
38635     checked: false,
38636
38637     // private
38638     ctype: "Roo.menu.CheckItem",
38639
38640     // private
38641     onRender : function(c){
38642         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
38643         if(this.group){
38644             this.el.addClass(this.groupClass);
38645         }
38646         Roo.menu.MenuMgr.registerCheckable(this);
38647         if(this.checked){
38648             this.checked = false;
38649             this.setChecked(true, true);
38650         }
38651     },
38652
38653     // private
38654     destroy : function(){
38655         if(this.rendered){
38656             Roo.menu.MenuMgr.unregisterCheckable(this);
38657         }
38658         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
38659     },
38660
38661     /**
38662      * Set the checked state of this item
38663      * @param {Boolean} checked The new checked value
38664      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
38665      */
38666     setChecked : function(state, suppressEvent){
38667         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
38668             if(this.container){
38669                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
38670             }
38671             this.checked = state;
38672             if(suppressEvent !== true){
38673                 this.fireEvent("checkchange", this, state);
38674             }
38675         }
38676     },
38677
38678     // private
38679     handleClick : function(e){
38680        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
38681            this.setChecked(!this.checked);
38682        }
38683        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
38684     }
38685 });/*
38686  * Based on:
38687  * Ext JS Library 1.1.1
38688  * Copyright(c) 2006-2007, Ext JS, LLC.
38689  *
38690  * Originally Released Under LGPL - original licence link has changed is not relivant.
38691  *
38692  * Fork - LGPL
38693  * <script type="text/javascript">
38694  */
38695  
38696 /**
38697  * @class Roo.menu.DateItem
38698  * @extends Roo.menu.Adapter
38699  * A menu item that wraps the {@link Roo.DatPicker} component.
38700  * @constructor
38701  * Creates a new DateItem
38702  * @param {Object} config Configuration options
38703  */
38704 Roo.menu.DateItem = function(config){
38705     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
38706     /** The Roo.DatePicker object @type Roo.DatePicker */
38707     this.picker = this.component;
38708     this.addEvents({select: true});
38709     
38710     this.picker.on("render", function(picker){
38711         picker.getEl().swallowEvent("click");
38712         picker.container.addClass("x-menu-date-item");
38713     });
38714
38715     this.picker.on("select", this.onSelect, this);
38716 };
38717
38718 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
38719     // private
38720     onSelect : function(picker, date){
38721         this.fireEvent("select", this, date, picker);
38722         Roo.menu.DateItem.superclass.handleClick.call(this);
38723     }
38724 });/*
38725  * Based on:
38726  * Ext JS Library 1.1.1
38727  * Copyright(c) 2006-2007, Ext JS, LLC.
38728  *
38729  * Originally Released Under LGPL - original licence link has changed is not relivant.
38730  *
38731  * Fork - LGPL
38732  * <script type="text/javascript">
38733  */
38734  
38735 /**
38736  * @class Roo.menu.ColorItem
38737  * @extends Roo.menu.Adapter
38738  * A menu item that wraps the {@link Roo.ColorPalette} component.
38739  * @constructor
38740  * Creates a new ColorItem
38741  * @param {Object} config Configuration options
38742  */
38743 Roo.menu.ColorItem = function(config){
38744     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
38745     /** The Roo.ColorPalette object @type Roo.ColorPalette */
38746     this.palette = this.component;
38747     this.relayEvents(this.palette, ["select"]);
38748     if(this.selectHandler){
38749         this.on('select', this.selectHandler, this.scope);
38750     }
38751 };
38752 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
38753  * Based on:
38754  * Ext JS Library 1.1.1
38755  * Copyright(c) 2006-2007, Ext JS, LLC.
38756  *
38757  * Originally Released Under LGPL - original licence link has changed is not relivant.
38758  *
38759  * Fork - LGPL
38760  * <script type="text/javascript">
38761  */
38762  
38763
38764 /**
38765  * @class Roo.menu.DateMenu
38766  * @extends Roo.menu.Menu
38767  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
38768  * @constructor
38769  * Creates a new DateMenu
38770  * @param {Object} config Configuration options
38771  */
38772 Roo.menu.DateMenu = function(config){
38773     Roo.menu.DateMenu.superclass.constructor.call(this, config);
38774     this.plain = true;
38775     var di = new Roo.menu.DateItem(config);
38776     this.add(di);
38777     /**
38778      * The {@link Roo.DatePicker} instance for this DateMenu
38779      * @type DatePicker
38780      */
38781     this.picker = di.picker;
38782     /**
38783      * @event select
38784      * @param {DatePicker} picker
38785      * @param {Date} date
38786      */
38787     this.relayEvents(di, ["select"]);
38788     this.on('beforeshow', function(){
38789         if(this.picker){
38790             this.picker.hideMonthPicker(false);
38791         }
38792     }, this);
38793 };
38794 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
38795     cls:'x-date-menu'
38796 });/*
38797  * Based on:
38798  * Ext JS Library 1.1.1
38799  * Copyright(c) 2006-2007, Ext JS, LLC.
38800  *
38801  * Originally Released Under LGPL - original licence link has changed is not relivant.
38802  *
38803  * Fork - LGPL
38804  * <script type="text/javascript">
38805  */
38806  
38807
38808 /**
38809  * @class Roo.menu.ColorMenu
38810  * @extends Roo.menu.Menu
38811  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
38812  * @constructor
38813  * Creates a new ColorMenu
38814  * @param {Object} config Configuration options
38815  */
38816 Roo.menu.ColorMenu = function(config){
38817     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
38818     this.plain = true;
38819     var ci = new Roo.menu.ColorItem(config);
38820     this.add(ci);
38821     /**
38822      * The {@link Roo.ColorPalette} instance for this ColorMenu
38823      * @type ColorPalette
38824      */
38825     this.palette = ci.palette;
38826     /**
38827      * @event select
38828      * @param {ColorPalette} palette
38829      * @param {String} color
38830      */
38831     this.relayEvents(ci, ["select"]);
38832 };
38833 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
38834  * Based on:
38835  * Ext JS Library 1.1.1
38836  * Copyright(c) 2006-2007, Ext JS, LLC.
38837  *
38838  * Originally Released Under LGPL - original licence link has changed is not relivant.
38839  *
38840  * Fork - LGPL
38841  * <script type="text/javascript">
38842  */
38843  
38844 /**
38845  * @class Roo.form.TextItem
38846  * @extends Roo.BoxComponent
38847  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
38848  * @constructor
38849  * Creates a new TextItem
38850  * @param {Object} config Configuration options
38851  */
38852 Roo.form.TextItem = function(config){
38853     Roo.form.TextItem.superclass.constructor.call(this, config);
38854 };
38855
38856 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
38857     
38858     /**
38859      * @cfg {String} tag the tag for this item (default div)
38860      */
38861     tag : 'div',
38862     /**
38863      * @cfg {String} html the content for this item
38864      */
38865     html : '',
38866     
38867     getAutoCreate : function()
38868     {
38869         var cfg = {
38870             id: this.id,
38871             tag: this.tag,
38872             html: this.html,
38873             cls: 'x-form-item'
38874         };
38875         
38876         return cfg;
38877         
38878     },
38879     
38880     onRender : function(ct, position)
38881     {
38882         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
38883         
38884         if(!this.el){
38885             var cfg = this.getAutoCreate();
38886             if(!cfg.name){
38887                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
38888             }
38889             if (!cfg.name.length) {
38890                 delete cfg.name;
38891             }
38892             this.el = ct.createChild(cfg, position);
38893         }
38894     },
38895     /*
38896      * setHTML
38897      * @param {String} html update the Contents of the element.
38898      */
38899     setHTML : function(html)
38900     {
38901         this.fieldEl.dom.innerHTML = html;
38902     }
38903     
38904 });/*
38905  * Based on:
38906  * Ext JS Library 1.1.1
38907  * Copyright(c) 2006-2007, Ext JS, LLC.
38908  *
38909  * Originally Released Under LGPL - original licence link has changed is not relivant.
38910  *
38911  * Fork - LGPL
38912  * <script type="text/javascript">
38913  */
38914  
38915 /**
38916  * @class Roo.form.Field
38917  * @extends Roo.BoxComponent
38918  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
38919  * @constructor
38920  * Creates a new Field
38921  * @param {Object} config Configuration options
38922  */
38923 Roo.form.Field = function(config){
38924     Roo.form.Field.superclass.constructor.call(this, config);
38925 };
38926
38927 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
38928     /**
38929      * @cfg {String} fieldLabel Label to use when rendering a form.
38930      */
38931        /**
38932      * @cfg {String} qtip Mouse over tip
38933      */
38934      
38935     /**
38936      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
38937      */
38938     invalidClass : "x-form-invalid",
38939     /**
38940      * @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")
38941      */
38942     invalidText : "The value in this field is invalid",
38943     /**
38944      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
38945      */
38946     focusClass : "x-form-focus",
38947     /**
38948      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
38949       automatic validation (defaults to "keyup").
38950      */
38951     validationEvent : "keyup",
38952     /**
38953      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
38954      */
38955     validateOnBlur : true,
38956     /**
38957      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
38958      */
38959     validationDelay : 250,
38960     /**
38961      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38962      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
38963      */
38964     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
38965     /**
38966      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
38967      */
38968     fieldClass : "x-form-field",
38969     /**
38970      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
38971      *<pre>
38972 Value         Description
38973 -----------   ----------------------------------------------------------------------
38974 qtip          Display a quick tip when the user hovers over the field
38975 title         Display a default browser title attribute popup
38976 under         Add a block div beneath the field containing the error text
38977 side          Add an error icon to the right of the field with a popup on hover
38978 [element id]  Add the error text directly to the innerHTML of the specified element
38979 </pre>
38980      */
38981     msgTarget : 'qtip',
38982     /**
38983      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
38984      */
38985     msgFx : 'normal',
38986
38987     /**
38988      * @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.
38989      */
38990     readOnly : false,
38991
38992     /**
38993      * @cfg {Boolean} disabled True to disable the field (defaults to false).
38994      */
38995     disabled : false,
38996
38997     /**
38998      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
38999      */
39000     inputType : undefined,
39001     
39002     /**
39003      * @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).
39004          */
39005         tabIndex : undefined,
39006         
39007     // private
39008     isFormField : true,
39009
39010     // private
39011     hasFocus : false,
39012     /**
39013      * @property {Roo.Element} fieldEl
39014      * Element Containing the rendered Field (with label etc.)
39015      */
39016     /**
39017      * @cfg {Mixed} value A value to initialize this field with.
39018      */
39019     value : undefined,
39020
39021     /**
39022      * @cfg {String} name The field's HTML name attribute.
39023      */
39024     /**
39025      * @cfg {String} cls A CSS class to apply to the field's underlying element.
39026      */
39027     // private
39028     loadedValue : false,
39029      
39030      
39031         // private ??
39032         initComponent : function(){
39033         Roo.form.Field.superclass.initComponent.call(this);
39034         this.addEvents({
39035             /**
39036              * @event focus
39037              * Fires when this field receives input focus.
39038              * @param {Roo.form.Field} this
39039              */
39040             focus : true,
39041             /**
39042              * @event blur
39043              * Fires when this field loses input focus.
39044              * @param {Roo.form.Field} this
39045              */
39046             blur : true,
39047             /**
39048              * @event specialkey
39049              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
39050              * {@link Roo.EventObject#getKey} to determine which key was pressed.
39051              * @param {Roo.form.Field} this
39052              * @param {Roo.EventObject} e The event object
39053              */
39054             specialkey : true,
39055             /**
39056              * @event change
39057              * Fires just before the field blurs if the field value has changed.
39058              * @param {Roo.form.Field} this
39059              * @param {Mixed} newValue The new value
39060              * @param {Mixed} oldValue The original value
39061              */
39062             change : true,
39063             /**
39064              * @event invalid
39065              * Fires after the field has been marked as invalid.
39066              * @param {Roo.form.Field} this
39067              * @param {String} msg The validation message
39068              */
39069             invalid : true,
39070             /**
39071              * @event valid
39072              * Fires after the field has been validated with no errors.
39073              * @param {Roo.form.Field} this
39074              */
39075             valid : true,
39076              /**
39077              * @event keyup
39078              * Fires after the key up
39079              * @param {Roo.form.Field} this
39080              * @param {Roo.EventObject}  e The event Object
39081              */
39082             keyup : true
39083         });
39084     },
39085
39086     /**
39087      * Returns the name attribute of the field if available
39088      * @return {String} name The field name
39089      */
39090     getName: function(){
39091          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
39092     },
39093
39094     // private
39095     onRender : function(ct, position){
39096         Roo.form.Field.superclass.onRender.call(this, ct, position);
39097         if(!this.el){
39098             var cfg = this.getAutoCreate();
39099             if(!cfg.name){
39100                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
39101             }
39102             if (!cfg.name.length) {
39103                 delete cfg.name;
39104             }
39105             if(this.inputType){
39106                 cfg.type = this.inputType;
39107             }
39108             this.el = ct.createChild(cfg, position);
39109         }
39110         var type = this.el.dom.type;
39111         if(type){
39112             if(type == 'password'){
39113                 type = 'text';
39114             }
39115             this.el.addClass('x-form-'+type);
39116         }
39117         if(this.readOnly){
39118             this.el.dom.readOnly = true;
39119         }
39120         if(this.tabIndex !== undefined){
39121             this.el.dom.setAttribute('tabIndex', this.tabIndex);
39122         }
39123
39124         this.el.addClass([this.fieldClass, this.cls]);
39125         this.initValue();
39126     },
39127
39128     /**
39129      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
39130      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
39131      * @return {Roo.form.Field} this
39132      */
39133     applyTo : function(target){
39134         this.allowDomMove = false;
39135         this.el = Roo.get(target);
39136         this.render(this.el.dom.parentNode);
39137         return this;
39138     },
39139
39140     // private
39141     initValue : function(){
39142         if(this.value !== undefined){
39143             this.setValue(this.value);
39144         }else if(this.el.dom.value.length > 0){
39145             this.setValue(this.el.dom.value);
39146         }
39147     },
39148
39149     /**
39150      * Returns true if this field has been changed since it was originally loaded and is not disabled.
39151      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
39152      */
39153     isDirty : function() {
39154         if(this.disabled) {
39155             return false;
39156         }
39157         return String(this.getValue()) !== String(this.originalValue);
39158     },
39159
39160     /**
39161      * stores the current value in loadedValue
39162      */
39163     resetHasChanged : function()
39164     {
39165         this.loadedValue = String(this.getValue());
39166     },
39167     /**
39168      * checks the current value against the 'loaded' value.
39169      * Note - will return false if 'resetHasChanged' has not been called first.
39170      */
39171     hasChanged : function()
39172     {
39173         if(this.disabled || this.readOnly) {
39174             return false;
39175         }
39176         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
39177     },
39178     
39179     
39180     
39181     // private
39182     afterRender : function(){
39183         Roo.form.Field.superclass.afterRender.call(this);
39184         this.initEvents();
39185     },
39186
39187     // private
39188     fireKey : function(e){
39189         //Roo.log('field ' + e.getKey());
39190         if(e.isNavKeyPress()){
39191             this.fireEvent("specialkey", this, e);
39192         }
39193     },
39194
39195     /**
39196      * Resets the current field value to the originally loaded value and clears any validation messages
39197      */
39198     reset : function(){
39199         this.setValue(this.resetValue);
39200         this.originalValue = this.getValue();
39201         this.clearInvalid();
39202     },
39203
39204     // private
39205     initEvents : function(){
39206         // safari killled keypress - so keydown is now used..
39207         this.el.on("keydown" , this.fireKey,  this);
39208         this.el.on("focus", this.onFocus,  this);
39209         this.el.on("blur", this.onBlur,  this);
39210         this.el.relayEvent('keyup', this);
39211
39212         // reference to original value for reset
39213         this.originalValue = this.getValue();
39214         this.resetValue =  this.getValue();
39215     },
39216
39217     // private
39218     onFocus : function(){
39219         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
39220             this.el.addClass(this.focusClass);
39221         }
39222         if(!this.hasFocus){
39223             this.hasFocus = true;
39224             this.startValue = this.getValue();
39225             this.fireEvent("focus", this);
39226         }
39227     },
39228
39229     beforeBlur : Roo.emptyFn,
39230
39231     // private
39232     onBlur : function(){
39233         this.beforeBlur();
39234         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
39235             this.el.removeClass(this.focusClass);
39236         }
39237         this.hasFocus = false;
39238         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
39239             this.validate();
39240         }
39241         var v = this.getValue();
39242         if(String(v) !== String(this.startValue)){
39243             this.fireEvent('change', this, v, this.startValue);
39244         }
39245         this.fireEvent("blur", this);
39246     },
39247
39248     /**
39249      * Returns whether or not the field value is currently valid
39250      * @param {Boolean} preventMark True to disable marking the field invalid
39251      * @return {Boolean} True if the value is valid, else false
39252      */
39253     isValid : function(preventMark){
39254         if(this.disabled){
39255             return true;
39256         }
39257         var restore = this.preventMark;
39258         this.preventMark = preventMark === true;
39259         var v = this.validateValue(this.processValue(this.getRawValue()));
39260         this.preventMark = restore;
39261         return v;
39262     },
39263
39264     /**
39265      * Validates the field value
39266      * @return {Boolean} True if the value is valid, else false
39267      */
39268     validate : function(){
39269         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
39270             this.clearInvalid();
39271             return true;
39272         }
39273         return false;
39274     },
39275
39276     processValue : function(value){
39277         return value;
39278     },
39279
39280     // private
39281     // Subclasses should provide the validation implementation by overriding this
39282     validateValue : function(value){
39283         return true;
39284     },
39285
39286     /**
39287      * Mark this field as invalid
39288      * @param {String} msg The validation message
39289      */
39290     markInvalid : function(msg){
39291         if(!this.rendered || this.preventMark){ // not rendered
39292             return;
39293         }
39294         
39295         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39296         
39297         obj.el.addClass(this.invalidClass);
39298         msg = msg || this.invalidText;
39299         switch(this.msgTarget){
39300             case 'qtip':
39301                 obj.el.dom.qtip = msg;
39302                 obj.el.dom.qclass = 'x-form-invalid-tip';
39303                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
39304                     Roo.QuickTips.enable();
39305                 }
39306                 break;
39307             case 'title':
39308                 this.el.dom.title = msg;
39309                 break;
39310             case 'under':
39311                 if(!this.errorEl){
39312                     var elp = this.el.findParent('.x-form-element', 5, true);
39313                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
39314                     this.errorEl.setWidth(elp.getWidth(true)-20);
39315                 }
39316                 this.errorEl.update(msg);
39317                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
39318                 break;
39319             case 'side':
39320                 if(!this.errorIcon){
39321                     var elp = this.el.findParent('.x-form-element', 5, true);
39322                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
39323                 }
39324                 this.alignErrorIcon();
39325                 this.errorIcon.dom.qtip = msg;
39326                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
39327                 this.errorIcon.show();
39328                 this.on('resize', this.alignErrorIcon, this);
39329                 break;
39330             default:
39331                 var t = Roo.getDom(this.msgTarget);
39332                 t.innerHTML = msg;
39333                 t.style.display = this.msgDisplay;
39334                 break;
39335         }
39336         this.fireEvent('invalid', this, msg);
39337     },
39338
39339     // private
39340     alignErrorIcon : function(){
39341         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
39342     },
39343
39344     /**
39345      * Clear any invalid styles/messages for this field
39346      */
39347     clearInvalid : function(){
39348         if(!this.rendered || this.preventMark){ // not rendered
39349             return;
39350         }
39351         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39352         
39353         obj.el.removeClass(this.invalidClass);
39354         switch(this.msgTarget){
39355             case 'qtip':
39356                 obj.el.dom.qtip = '';
39357                 break;
39358             case 'title':
39359                 this.el.dom.title = '';
39360                 break;
39361             case 'under':
39362                 if(this.errorEl){
39363                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
39364                 }
39365                 break;
39366             case 'side':
39367                 if(this.errorIcon){
39368                     this.errorIcon.dom.qtip = '';
39369                     this.errorIcon.hide();
39370                     this.un('resize', this.alignErrorIcon, this);
39371                 }
39372                 break;
39373             default:
39374                 var t = Roo.getDom(this.msgTarget);
39375                 t.innerHTML = '';
39376                 t.style.display = 'none';
39377                 break;
39378         }
39379         this.fireEvent('valid', this);
39380     },
39381
39382     /**
39383      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
39384      * @return {Mixed} value The field value
39385      */
39386     getRawValue : function(){
39387         var v = this.el.getValue();
39388         
39389         return v;
39390     },
39391
39392     /**
39393      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
39394      * @return {Mixed} value The field value
39395      */
39396     getValue : function(){
39397         var v = this.el.getValue();
39398          
39399         return v;
39400     },
39401
39402     /**
39403      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
39404      * @param {Mixed} value The value to set
39405      */
39406     setRawValue : function(v){
39407         return this.el.dom.value = (v === null || v === undefined ? '' : v);
39408     },
39409
39410     /**
39411      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
39412      * @param {Mixed} value The value to set
39413      */
39414     setValue : function(v){
39415         this.value = v;
39416         if(this.rendered){
39417             this.el.dom.value = (v === null || v === undefined ? '' : v);
39418              this.validate();
39419         }
39420     },
39421
39422     adjustSize : function(w, h){
39423         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
39424         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
39425         return s;
39426     },
39427
39428     adjustWidth : function(tag, w){
39429         tag = tag.toLowerCase();
39430         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
39431             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
39432                 if(tag == 'input'){
39433                     return w + 2;
39434                 }
39435                 if(tag == 'textarea'){
39436                     return w-2;
39437                 }
39438             }else if(Roo.isOpera){
39439                 if(tag == 'input'){
39440                     return w + 2;
39441                 }
39442                 if(tag == 'textarea'){
39443                     return w-2;
39444                 }
39445             }
39446         }
39447         return w;
39448     }
39449 });
39450
39451
39452 // anything other than normal should be considered experimental
39453 Roo.form.Field.msgFx = {
39454     normal : {
39455         show: function(msgEl, f){
39456             msgEl.setDisplayed('block');
39457         },
39458
39459         hide : function(msgEl, f){
39460             msgEl.setDisplayed(false).update('');
39461         }
39462     },
39463
39464     slide : {
39465         show: function(msgEl, f){
39466             msgEl.slideIn('t', {stopFx:true});
39467         },
39468
39469         hide : function(msgEl, f){
39470             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
39471         }
39472     },
39473
39474     slideRight : {
39475         show: function(msgEl, f){
39476             msgEl.fixDisplay();
39477             msgEl.alignTo(f.el, 'tl-tr');
39478             msgEl.slideIn('l', {stopFx:true});
39479         },
39480
39481         hide : function(msgEl, f){
39482             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
39483         }
39484     }
39485 };/*
39486  * Based on:
39487  * Ext JS Library 1.1.1
39488  * Copyright(c) 2006-2007, Ext JS, LLC.
39489  *
39490  * Originally Released Under LGPL - original licence link has changed is not relivant.
39491  *
39492  * Fork - LGPL
39493  * <script type="text/javascript">
39494  */
39495  
39496
39497 /**
39498  * @class Roo.form.TextField
39499  * @extends Roo.form.Field
39500  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
39501  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
39502  * @constructor
39503  * Creates a new TextField
39504  * @param {Object} config Configuration options
39505  */
39506 Roo.form.TextField = function(config){
39507     Roo.form.TextField.superclass.constructor.call(this, config);
39508     this.addEvents({
39509         /**
39510          * @event autosize
39511          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
39512          * according to the default logic, but this event provides a hook for the developer to apply additional
39513          * logic at runtime to resize the field if needed.
39514              * @param {Roo.form.Field} this This text field
39515              * @param {Number} width The new field width
39516              */
39517         autosize : true
39518     });
39519 };
39520
39521 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
39522     /**
39523      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
39524      */
39525     grow : false,
39526     /**
39527      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
39528      */
39529     growMin : 30,
39530     /**
39531      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
39532      */
39533     growMax : 800,
39534     /**
39535      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
39536      */
39537     vtype : null,
39538     /**
39539      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
39540      */
39541     maskRe : null,
39542     /**
39543      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
39544      */
39545     disableKeyFilter : false,
39546     /**
39547      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
39548      */
39549     allowBlank : true,
39550     /**
39551      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
39552      */
39553     minLength : 0,
39554     /**
39555      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
39556      */
39557     maxLength : Number.MAX_VALUE,
39558     /**
39559      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
39560      */
39561     minLengthText : "The minimum length for this field is {0}",
39562     /**
39563      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
39564      */
39565     maxLengthText : "The maximum length for this field is {0}",
39566     /**
39567      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
39568      */
39569     selectOnFocus : false,
39570     /**
39571      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
39572      */    
39573     allowLeadingSpace : false,
39574     /**
39575      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
39576      */
39577     blankText : "This field is required",
39578     /**
39579      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
39580      * If available, this function will be called only after the basic validators all return true, and will be passed the
39581      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
39582      */
39583     validator : null,
39584     /**
39585      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
39586      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
39587      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
39588      */
39589     regex : null,
39590     /**
39591      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
39592      */
39593     regexText : "",
39594     /**
39595      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
39596      */
39597     emptyText : null,
39598    
39599
39600     // private
39601     initEvents : function()
39602     {
39603         if (this.emptyText) {
39604             this.el.attr('placeholder', this.emptyText);
39605         }
39606         
39607         Roo.form.TextField.superclass.initEvents.call(this);
39608         if(this.validationEvent == 'keyup'){
39609             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
39610             this.el.on('keyup', this.filterValidation, this);
39611         }
39612         else if(this.validationEvent !== false){
39613             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
39614         }
39615         
39616         if(this.selectOnFocus){
39617             this.on("focus", this.preFocus, this);
39618         }
39619         if (!this.allowLeadingSpace) {
39620             this.on('blur', this.cleanLeadingSpace, this);
39621         }
39622         
39623         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
39624             this.el.on("keypress", this.filterKeys, this);
39625         }
39626         if(this.grow){
39627             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
39628             this.el.on("click", this.autoSize,  this);
39629         }
39630         if(this.el.is('input[type=password]') && Roo.isSafari){
39631             this.el.on('keydown', this.SafariOnKeyDown, this);
39632         }
39633     },
39634
39635     processValue : function(value){
39636         if(this.stripCharsRe){
39637             var newValue = value.replace(this.stripCharsRe, '');
39638             if(newValue !== value){
39639                 this.setRawValue(newValue);
39640                 return newValue;
39641             }
39642         }
39643         return value;
39644     },
39645
39646     filterValidation : function(e){
39647         if(!e.isNavKeyPress()){
39648             this.validationTask.delay(this.validationDelay);
39649         }
39650     },
39651
39652     // private
39653     onKeyUp : function(e){
39654         if(!e.isNavKeyPress()){
39655             this.autoSize();
39656         }
39657     },
39658     // private - clean the leading white space
39659     cleanLeadingSpace : function(e)
39660     {
39661         if ( this.inputType == 'file') {
39662             return;
39663         }
39664         
39665         this.setValue((this.getValue() + '').replace(/^\s+/,''));
39666     },
39667     /**
39668      * Resets the current field value to the originally-loaded value and clears any validation messages.
39669      *  
39670      */
39671     reset : function(){
39672         Roo.form.TextField.superclass.reset.call(this);
39673        
39674     }, 
39675     // private
39676     preFocus : function(){
39677         
39678         if(this.selectOnFocus){
39679             this.el.dom.select();
39680         }
39681     },
39682
39683     
39684     // private
39685     filterKeys : function(e){
39686         var k = e.getKey();
39687         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
39688             return;
39689         }
39690         var c = e.getCharCode(), cc = String.fromCharCode(c);
39691         if(Roo.isIE && (e.isSpecialKey() || !cc)){
39692             return;
39693         }
39694         if(!this.maskRe.test(cc)){
39695             e.stopEvent();
39696         }
39697     },
39698
39699     setValue : function(v){
39700         
39701         Roo.form.TextField.superclass.setValue.apply(this, arguments);
39702         
39703         this.autoSize();
39704     },
39705
39706     /**
39707      * Validates a value according to the field's validation rules and marks the field as invalid
39708      * if the validation fails
39709      * @param {Mixed} value The value to validate
39710      * @return {Boolean} True if the value is valid, else false
39711      */
39712     validateValue : function(value){
39713         if(value.length < 1)  { // if it's blank
39714              if(this.allowBlank){
39715                 this.clearInvalid();
39716                 return true;
39717              }else{
39718                 this.markInvalid(this.blankText);
39719                 return false;
39720              }
39721         }
39722         if(value.length < this.minLength){
39723             this.markInvalid(String.format(this.minLengthText, this.minLength));
39724             return false;
39725         }
39726         if(value.length > this.maxLength){
39727             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
39728             return false;
39729         }
39730         if(this.vtype){
39731             var vt = Roo.form.VTypes;
39732             if(!vt[this.vtype](value, this)){
39733                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
39734                 return false;
39735             }
39736         }
39737         if(typeof this.validator == "function"){
39738             var msg = this.validator(value);
39739             if(msg !== true){
39740                 this.markInvalid(msg);
39741                 return false;
39742             }
39743         }
39744         if(this.regex && !this.regex.test(value)){
39745             this.markInvalid(this.regexText);
39746             return false;
39747         }
39748         return true;
39749     },
39750
39751     /**
39752      * Selects text in this field
39753      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
39754      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
39755      */
39756     selectText : function(start, end){
39757         var v = this.getRawValue();
39758         if(v.length > 0){
39759             start = start === undefined ? 0 : start;
39760             end = end === undefined ? v.length : end;
39761             var d = this.el.dom;
39762             if(d.setSelectionRange){
39763                 d.setSelectionRange(start, end);
39764             }else if(d.createTextRange){
39765                 var range = d.createTextRange();
39766                 range.moveStart("character", start);
39767                 range.moveEnd("character", v.length-end);
39768                 range.select();
39769             }
39770         }
39771     },
39772
39773     /**
39774      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
39775      * This only takes effect if grow = true, and fires the autosize event.
39776      */
39777     autoSize : function(){
39778         if(!this.grow || !this.rendered){
39779             return;
39780         }
39781         if(!this.metrics){
39782             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
39783         }
39784         var el = this.el;
39785         var v = el.dom.value;
39786         var d = document.createElement('div');
39787         d.appendChild(document.createTextNode(v));
39788         v = d.innerHTML;
39789         d = null;
39790         v += "&#160;";
39791         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
39792         this.el.setWidth(w);
39793         this.fireEvent("autosize", this, w);
39794     },
39795     
39796     // private
39797     SafariOnKeyDown : function(event)
39798     {
39799         // this is a workaround for a password hang bug on chrome/ webkit.
39800         
39801         var isSelectAll = false;
39802         
39803         if(this.el.dom.selectionEnd > 0){
39804             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
39805         }
39806         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
39807             event.preventDefault();
39808             this.setValue('');
39809             return;
39810         }
39811         
39812         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
39813             
39814             event.preventDefault();
39815             // this is very hacky as keydown always get's upper case.
39816             
39817             var cc = String.fromCharCode(event.getCharCode());
39818             
39819             
39820             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
39821             
39822         }
39823         
39824         
39825     }
39826 });/*
39827  * Based on:
39828  * Ext JS Library 1.1.1
39829  * Copyright(c) 2006-2007, Ext JS, LLC.
39830  *
39831  * Originally Released Under LGPL - original licence link has changed is not relivant.
39832  *
39833  * Fork - LGPL
39834  * <script type="text/javascript">
39835  */
39836  
39837 /**
39838  * @class Roo.form.Hidden
39839  * @extends Roo.form.TextField
39840  * Simple Hidden element used on forms 
39841  * 
39842  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
39843  * 
39844  * @constructor
39845  * Creates a new Hidden form element.
39846  * @param {Object} config Configuration options
39847  */
39848
39849
39850
39851 // easy hidden field...
39852 Roo.form.Hidden = function(config){
39853     Roo.form.Hidden.superclass.constructor.call(this, config);
39854 };
39855   
39856 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
39857     fieldLabel:      '',
39858     inputType:      'hidden',
39859     width:          50,
39860     allowBlank:     true,
39861     labelSeparator: '',
39862     hidden:         true,
39863     itemCls :       'x-form-item-display-none'
39864
39865
39866 });
39867
39868
39869 /*
39870  * Based on:
39871  * Ext JS Library 1.1.1
39872  * Copyright(c) 2006-2007, Ext JS, LLC.
39873  *
39874  * Originally Released Under LGPL - original licence link has changed is not relivant.
39875  *
39876  * Fork - LGPL
39877  * <script type="text/javascript">
39878  */
39879  
39880 /**
39881  * @class Roo.form.TriggerField
39882  * @extends Roo.form.TextField
39883  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
39884  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
39885  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
39886  * for which you can provide a custom implementation.  For example:
39887  * <pre><code>
39888 var trigger = new Roo.form.TriggerField();
39889 trigger.onTriggerClick = myTriggerFn;
39890 trigger.applyTo('my-field');
39891 </code></pre>
39892  *
39893  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
39894  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
39895  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39896  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
39897  * @constructor
39898  * Create a new TriggerField.
39899  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
39900  * to the base TextField)
39901  */
39902 Roo.form.TriggerField = function(config){
39903     this.mimicing = false;
39904     Roo.form.TriggerField.superclass.constructor.call(this, config);
39905 };
39906
39907 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
39908     /**
39909      * @cfg {String} triggerClass A CSS class to apply to the trigger
39910      */
39911     /**
39912      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39913      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
39914      */
39915     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
39916     /**
39917      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
39918      */
39919     hideTrigger:false,
39920
39921     /** @cfg {Boolean} grow @hide */
39922     /** @cfg {Number} growMin @hide */
39923     /** @cfg {Number} growMax @hide */
39924
39925     /**
39926      * @hide 
39927      * @method
39928      */
39929     autoSize: Roo.emptyFn,
39930     // private
39931     monitorTab : true,
39932     // private
39933     deferHeight : true,
39934
39935     
39936     actionMode : 'wrap',
39937     // private
39938     onResize : function(w, h){
39939         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
39940         if(typeof w == 'number'){
39941             var x = w - this.trigger.getWidth();
39942             this.el.setWidth(this.adjustWidth('input', x));
39943             this.trigger.setStyle('left', x+'px');
39944         }
39945     },
39946
39947     // private
39948     adjustSize : Roo.BoxComponent.prototype.adjustSize,
39949
39950     // private
39951     getResizeEl : function(){
39952         return this.wrap;
39953     },
39954
39955     // private
39956     getPositionEl : function(){
39957         return this.wrap;
39958     },
39959
39960     // private
39961     alignErrorIcon : function(){
39962         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
39963     },
39964
39965     // private
39966     onRender : function(ct, position){
39967         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
39968         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
39969         this.trigger = this.wrap.createChild(this.triggerConfig ||
39970                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
39971         if(this.hideTrigger){
39972             this.trigger.setDisplayed(false);
39973         }
39974         this.initTrigger();
39975         if(!this.width){
39976             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
39977         }
39978     },
39979
39980     // private
39981     initTrigger : function(){
39982         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39983         this.trigger.addClassOnOver('x-form-trigger-over');
39984         this.trigger.addClassOnClick('x-form-trigger-click');
39985     },
39986
39987     // private
39988     onDestroy : function(){
39989         if(this.trigger){
39990             this.trigger.removeAllListeners();
39991             this.trigger.remove();
39992         }
39993         if(this.wrap){
39994             this.wrap.remove();
39995         }
39996         Roo.form.TriggerField.superclass.onDestroy.call(this);
39997     },
39998
39999     // private
40000     onFocus : function(){
40001         Roo.form.TriggerField.superclass.onFocus.call(this);
40002         if(!this.mimicing){
40003             this.wrap.addClass('x-trigger-wrap-focus');
40004             this.mimicing = true;
40005             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
40006             if(this.monitorTab){
40007                 this.el.on("keydown", this.checkTab, this);
40008             }
40009         }
40010     },
40011
40012     // private
40013     checkTab : function(e){
40014         if(e.getKey() == e.TAB){
40015             this.triggerBlur();
40016         }
40017     },
40018
40019     // private
40020     onBlur : function(){
40021         // do nothing
40022     },
40023
40024     // private
40025     mimicBlur : function(e, t){
40026         if(!this.wrap.contains(t) && this.validateBlur()){
40027             this.triggerBlur();
40028         }
40029     },
40030
40031     // private
40032     triggerBlur : function(){
40033         this.mimicing = false;
40034         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
40035         if(this.monitorTab){
40036             this.el.un("keydown", this.checkTab, this);
40037         }
40038         this.wrap.removeClass('x-trigger-wrap-focus');
40039         Roo.form.TriggerField.superclass.onBlur.call(this);
40040     },
40041
40042     // private
40043     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
40044     validateBlur : function(e, t){
40045         return true;
40046     },
40047
40048     // private
40049     onDisable : function(){
40050         Roo.form.TriggerField.superclass.onDisable.call(this);
40051         if(this.wrap){
40052             this.wrap.addClass('x-item-disabled');
40053         }
40054     },
40055
40056     // private
40057     onEnable : function(){
40058         Roo.form.TriggerField.superclass.onEnable.call(this);
40059         if(this.wrap){
40060             this.wrap.removeClass('x-item-disabled');
40061         }
40062     },
40063
40064     // private
40065     onShow : function(){
40066         var ae = this.getActionEl();
40067         
40068         if(ae){
40069             ae.dom.style.display = '';
40070             ae.dom.style.visibility = 'visible';
40071         }
40072     },
40073
40074     // private
40075     
40076     onHide : function(){
40077         var ae = this.getActionEl();
40078         ae.dom.style.display = 'none';
40079     },
40080
40081     /**
40082      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
40083      * by an implementing function.
40084      * @method
40085      * @param {EventObject} e
40086      */
40087     onTriggerClick : Roo.emptyFn
40088 });
40089
40090 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
40091 // to be extended by an implementing class.  For an example of implementing this class, see the custom
40092 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
40093 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
40094     initComponent : function(){
40095         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
40096
40097         this.triggerConfig = {
40098             tag:'span', cls:'x-form-twin-triggers', cn:[
40099             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
40100             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
40101         ]};
40102     },
40103
40104     getTrigger : function(index){
40105         return this.triggers[index];
40106     },
40107
40108     initTrigger : function(){
40109         var ts = this.trigger.select('.x-form-trigger', true);
40110         this.wrap.setStyle('overflow', 'hidden');
40111         var triggerField = this;
40112         ts.each(function(t, all, index){
40113             t.hide = function(){
40114                 var w = triggerField.wrap.getWidth();
40115                 this.dom.style.display = 'none';
40116                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
40117             };
40118             t.show = function(){
40119                 var w = triggerField.wrap.getWidth();
40120                 this.dom.style.display = '';
40121                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
40122             };
40123             var triggerIndex = 'Trigger'+(index+1);
40124
40125             if(this['hide'+triggerIndex]){
40126                 t.dom.style.display = 'none';
40127             }
40128             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
40129             t.addClassOnOver('x-form-trigger-over');
40130             t.addClassOnClick('x-form-trigger-click');
40131         }, this);
40132         this.triggers = ts.elements;
40133     },
40134
40135     onTrigger1Click : Roo.emptyFn,
40136     onTrigger2Click : Roo.emptyFn
40137 });/*
40138  * Based on:
40139  * Ext JS Library 1.1.1
40140  * Copyright(c) 2006-2007, Ext JS, LLC.
40141  *
40142  * Originally Released Under LGPL - original licence link has changed is not relivant.
40143  *
40144  * Fork - LGPL
40145  * <script type="text/javascript">
40146  */
40147  
40148 /**
40149  * @class Roo.form.TextArea
40150  * @extends Roo.form.TextField
40151  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
40152  * support for auto-sizing.
40153  * @constructor
40154  * Creates a new TextArea
40155  * @param {Object} config Configuration options
40156  */
40157 Roo.form.TextArea = function(config){
40158     Roo.form.TextArea.superclass.constructor.call(this, config);
40159     // these are provided exchanges for backwards compat
40160     // minHeight/maxHeight were replaced by growMin/growMax to be
40161     // compatible with TextField growing config values
40162     if(this.minHeight !== undefined){
40163         this.growMin = this.minHeight;
40164     }
40165     if(this.maxHeight !== undefined){
40166         this.growMax = this.maxHeight;
40167     }
40168 };
40169
40170 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
40171     /**
40172      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
40173      */
40174     growMin : 60,
40175     /**
40176      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
40177      */
40178     growMax: 1000,
40179     /**
40180      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
40181      * in the field (equivalent to setting overflow: hidden, defaults to false)
40182      */
40183     preventScrollbars: false,
40184     /**
40185      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40186      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
40187      */
40188
40189     // private
40190     onRender : function(ct, position){
40191         if(!this.el){
40192             this.defaultAutoCreate = {
40193                 tag: "textarea",
40194                 style:"width:300px;height:60px;",
40195                 autocomplete: "new-password"
40196             };
40197         }
40198         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
40199         if(this.grow){
40200             this.textSizeEl = Roo.DomHelper.append(document.body, {
40201                 tag: "pre", cls: "x-form-grow-sizer"
40202             });
40203             if(this.preventScrollbars){
40204                 this.el.setStyle("overflow", "hidden");
40205             }
40206             this.el.setHeight(this.growMin);
40207         }
40208     },
40209
40210     onDestroy : function(){
40211         if(this.textSizeEl){
40212             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
40213         }
40214         Roo.form.TextArea.superclass.onDestroy.call(this);
40215     },
40216
40217     // private
40218     onKeyUp : function(e){
40219         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
40220             this.autoSize();
40221         }
40222     },
40223
40224     /**
40225      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
40226      * This only takes effect if grow = true, and fires the autosize event if the height changes.
40227      */
40228     autoSize : function(){
40229         if(!this.grow || !this.textSizeEl){
40230             return;
40231         }
40232         var el = this.el;
40233         var v = el.dom.value;
40234         var ts = this.textSizeEl;
40235
40236         ts.innerHTML = '';
40237         ts.appendChild(document.createTextNode(v));
40238         v = ts.innerHTML;
40239
40240         Roo.fly(ts).setWidth(this.el.getWidth());
40241         if(v.length < 1){
40242             v = "&#160;&#160;";
40243         }else{
40244             if(Roo.isIE){
40245                 v = v.replace(/\n/g, '<p>&#160;</p>');
40246             }
40247             v += "&#160;\n&#160;";
40248         }
40249         ts.innerHTML = v;
40250         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
40251         if(h != this.lastHeight){
40252             this.lastHeight = h;
40253             this.el.setHeight(h);
40254             this.fireEvent("autosize", this, h);
40255         }
40256     }
40257 });/*
40258  * Based on:
40259  * Ext JS Library 1.1.1
40260  * Copyright(c) 2006-2007, Ext JS, LLC.
40261  *
40262  * Originally Released Under LGPL - original licence link has changed is not relivant.
40263  *
40264  * Fork - LGPL
40265  * <script type="text/javascript">
40266  */
40267  
40268
40269 /**
40270  * @class Roo.form.NumberField
40271  * @extends Roo.form.TextField
40272  * Numeric text field that provides automatic keystroke filtering and numeric validation.
40273  * @constructor
40274  * Creates a new NumberField
40275  * @param {Object} config Configuration options
40276  */
40277 Roo.form.NumberField = function(config){
40278     Roo.form.NumberField.superclass.constructor.call(this, config);
40279 };
40280
40281 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
40282     /**
40283      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
40284      */
40285     fieldClass: "x-form-field x-form-num-field",
40286     /**
40287      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40288      */
40289     allowDecimals : true,
40290     /**
40291      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40292      */
40293     decimalSeparator : ".",
40294     /**
40295      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40296      */
40297     decimalPrecision : 2,
40298     /**
40299      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40300      */
40301     allowNegative : true,
40302     /**
40303      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40304      */
40305     minValue : Number.NEGATIVE_INFINITY,
40306     /**
40307      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40308      */
40309     maxValue : Number.MAX_VALUE,
40310     /**
40311      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40312      */
40313     minText : "The minimum value for this field is {0}",
40314     /**
40315      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40316      */
40317     maxText : "The maximum value for this field is {0}",
40318     /**
40319      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40320      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40321      */
40322     nanText : "{0} is not a valid number",
40323
40324     // private
40325     initEvents : function(){
40326         Roo.form.NumberField.superclass.initEvents.call(this);
40327         var allowed = "0123456789";
40328         if(this.allowDecimals){
40329             allowed += this.decimalSeparator;
40330         }
40331         if(this.allowNegative){
40332             allowed += "-";
40333         }
40334         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40335         var keyPress = function(e){
40336             var k = e.getKey();
40337             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40338                 return;
40339             }
40340             var c = e.getCharCode();
40341             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40342                 e.stopEvent();
40343             }
40344         };
40345         this.el.on("keypress", keyPress, this);
40346     },
40347
40348     // private
40349     validateValue : function(value){
40350         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
40351             return false;
40352         }
40353         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40354              return true;
40355         }
40356         var num = this.parseValue(value);
40357         if(isNaN(num)){
40358             this.markInvalid(String.format(this.nanText, value));
40359             return false;
40360         }
40361         if(num < this.minValue){
40362             this.markInvalid(String.format(this.minText, this.minValue));
40363             return false;
40364         }
40365         if(num > this.maxValue){
40366             this.markInvalid(String.format(this.maxText, this.maxValue));
40367             return false;
40368         }
40369         return true;
40370     },
40371
40372     getValue : function(){
40373         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
40374     },
40375
40376     // private
40377     parseValue : function(value){
40378         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40379         return isNaN(value) ? '' : value;
40380     },
40381
40382     // private
40383     fixPrecision : function(value){
40384         var nan = isNaN(value);
40385         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40386             return nan ? '' : value;
40387         }
40388         return parseFloat(value).toFixed(this.decimalPrecision);
40389     },
40390
40391     setValue : function(v){
40392         v = this.fixPrecision(v);
40393         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
40394     },
40395
40396     // private
40397     decimalPrecisionFcn : function(v){
40398         return Math.floor(v);
40399     },
40400
40401     beforeBlur : function(){
40402         var v = this.parseValue(this.getRawValue());
40403         if(v){
40404             this.setValue(v);
40405         }
40406     }
40407 });/*
40408  * Based on:
40409  * Ext JS Library 1.1.1
40410  * Copyright(c) 2006-2007, Ext JS, LLC.
40411  *
40412  * Originally Released Under LGPL - original licence link has changed is not relivant.
40413  *
40414  * Fork - LGPL
40415  * <script type="text/javascript">
40416  */
40417  
40418 /**
40419  * @class Roo.form.DateField
40420  * @extends Roo.form.TriggerField
40421  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40422 * @constructor
40423 * Create a new DateField
40424 * @param {Object} config
40425  */
40426 Roo.form.DateField = function(config)
40427 {
40428     Roo.form.DateField.superclass.constructor.call(this, config);
40429     
40430       this.addEvents({
40431          
40432         /**
40433          * @event select
40434          * Fires when a date is selected
40435              * @param {Roo.form.DateField} combo This combo box
40436              * @param {Date} date The date selected
40437              */
40438         'select' : true
40439          
40440     });
40441     
40442     
40443     if(typeof this.minValue == "string") {
40444         this.minValue = this.parseDate(this.minValue);
40445     }
40446     if(typeof this.maxValue == "string") {
40447         this.maxValue = this.parseDate(this.maxValue);
40448     }
40449     this.ddMatch = null;
40450     if(this.disabledDates){
40451         var dd = this.disabledDates;
40452         var re = "(?:";
40453         for(var i = 0; i < dd.length; i++){
40454             re += dd[i];
40455             if(i != dd.length-1) {
40456                 re += "|";
40457             }
40458         }
40459         this.ddMatch = new RegExp(re + ")");
40460     }
40461 };
40462
40463 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
40464     /**
40465      * @cfg {String} format
40466      * The default date format string which can be overriden for localization support.  The format must be
40467      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40468      */
40469     format : "m/d/y",
40470     /**
40471      * @cfg {String} altFormats
40472      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40473      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40474      */
40475     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
40476     /**
40477      * @cfg {Array} disabledDays
40478      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40479      */
40480     disabledDays : null,
40481     /**
40482      * @cfg {String} disabledDaysText
40483      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40484      */
40485     disabledDaysText : "Disabled",
40486     /**
40487      * @cfg {Array} disabledDates
40488      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40489      * expression so they are very powerful. Some examples:
40490      * <ul>
40491      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40492      * <li>["03/08", "09/16"] would disable those days for every year</li>
40493      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40494      * <li>["03/../2006"] would disable every day in March 2006</li>
40495      * <li>["^03"] would disable every day in every March</li>
40496      * </ul>
40497      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40498      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40499      */
40500     disabledDates : null,
40501     /**
40502      * @cfg {String} disabledDatesText
40503      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40504      */
40505     disabledDatesText : "Disabled",
40506     /**
40507      * @cfg {Date/String} minValue
40508      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40509      * valid format (defaults to null).
40510      */
40511     minValue : null,
40512     /**
40513      * @cfg {Date/String} maxValue
40514      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40515      * valid format (defaults to null).
40516      */
40517     maxValue : null,
40518     /**
40519      * @cfg {String} minText
40520      * The error text to display when the date in the cell is before minValue (defaults to
40521      * 'The date in this field must be after {minValue}').
40522      */
40523     minText : "The date in this field must be equal to or after {0}",
40524     /**
40525      * @cfg {String} maxText
40526      * The error text to display when the date in the cell is after maxValue (defaults to
40527      * 'The date in this field must be before {maxValue}').
40528      */
40529     maxText : "The date in this field must be equal to or before {0}",
40530     /**
40531      * @cfg {String} invalidText
40532      * The error text to display when the date in the field is invalid (defaults to
40533      * '{value} is not a valid date - it must be in the format {format}').
40534      */
40535     invalidText : "{0} is not a valid date - it must be in the format {1}",
40536     /**
40537      * @cfg {String} triggerClass
40538      * An additional CSS class used to style the trigger button.  The trigger will always get the
40539      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40540      * which displays a calendar icon).
40541      */
40542     triggerClass : 'x-form-date-trigger',
40543     
40544
40545     /**
40546      * @cfg {Boolean} useIso
40547      * if enabled, then the date field will use a hidden field to store the 
40548      * real value as iso formated date. default (false)
40549      */ 
40550     useIso : false,
40551     /**
40552      * @cfg {String/Object} autoCreate
40553      * A DomHelper element spec, or true for a default element spec (defaults to
40554      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40555      */ 
40556     // private
40557     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
40558     
40559     // private
40560     hiddenField: false,
40561     
40562     onRender : function(ct, position)
40563     {
40564         Roo.form.DateField.superclass.onRender.call(this, ct, position);
40565         if (this.useIso) {
40566             //this.el.dom.removeAttribute('name'); 
40567             Roo.log("Changing name?");
40568             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
40569             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40570                     'before', true);
40571             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40572             // prevent input submission
40573             this.hiddenName = this.name;
40574         }
40575             
40576             
40577     },
40578     
40579     // private
40580     validateValue : function(value)
40581     {
40582         value = this.formatDate(value);
40583         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
40584             Roo.log('super failed');
40585             return false;
40586         }
40587         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40588              return true;
40589         }
40590         var svalue = value;
40591         value = this.parseDate(value);
40592         if(!value){
40593             Roo.log('parse date failed' + svalue);
40594             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40595             return false;
40596         }
40597         var time = value.getTime();
40598         if(this.minValue && time < this.minValue.getTime()){
40599             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40600             return false;
40601         }
40602         if(this.maxValue && time > this.maxValue.getTime()){
40603             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40604             return false;
40605         }
40606         if(this.disabledDays){
40607             var day = value.getDay();
40608             for(var i = 0; i < this.disabledDays.length; i++) {
40609                 if(day === this.disabledDays[i]){
40610                     this.markInvalid(this.disabledDaysText);
40611                     return false;
40612                 }
40613             }
40614         }
40615         var fvalue = this.formatDate(value);
40616         if(this.ddMatch && this.ddMatch.test(fvalue)){
40617             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40618             return false;
40619         }
40620         return true;
40621     },
40622
40623     // private
40624     // Provides logic to override the default TriggerField.validateBlur which just returns true
40625     validateBlur : function(){
40626         return !this.menu || !this.menu.isVisible();
40627     },
40628     
40629     getName: function()
40630     {
40631         // returns hidden if it's set..
40632         if (!this.rendered) {return ''};
40633         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40634         
40635     },
40636
40637     /**
40638      * Returns the current date value of the date field.
40639      * @return {Date} The date value
40640      */
40641     getValue : function(){
40642         
40643         return  this.hiddenField ?
40644                 this.hiddenField.value :
40645                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
40646     },
40647
40648     /**
40649      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40650      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
40651      * (the default format used is "m/d/y").
40652      * <br />Usage:
40653      * <pre><code>
40654 //All of these calls set the same date value (May 4, 2006)
40655
40656 //Pass a date object:
40657 var dt = new Date('5/4/06');
40658 dateField.setValue(dt);
40659
40660 //Pass a date string (default format):
40661 dateField.setValue('5/4/06');
40662
40663 //Pass a date string (custom format):
40664 dateField.format = 'Y-m-d';
40665 dateField.setValue('2006-5-4');
40666 </code></pre>
40667      * @param {String/Date} date The date or valid date string
40668      */
40669     setValue : function(date){
40670         if (this.hiddenField) {
40671             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40672         }
40673         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40674         // make sure the value field is always stored as a date..
40675         this.value = this.parseDate(date);
40676         
40677         
40678     },
40679
40680     // private
40681     parseDate : function(value){
40682         if(!value || value instanceof Date){
40683             return value;
40684         }
40685         var v = Date.parseDate(value, this.format);
40686          if (!v && this.useIso) {
40687             v = Date.parseDate(value, 'Y-m-d');
40688         }
40689         if(!v && this.altFormats){
40690             if(!this.altFormatsArray){
40691                 this.altFormatsArray = this.altFormats.split("|");
40692             }
40693             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40694                 v = Date.parseDate(value, this.altFormatsArray[i]);
40695             }
40696         }
40697         return v;
40698     },
40699
40700     // private
40701     formatDate : function(date, fmt){
40702         return (!date || !(date instanceof Date)) ?
40703                date : date.dateFormat(fmt || this.format);
40704     },
40705
40706     // private
40707     menuListeners : {
40708         select: function(m, d){
40709             
40710             this.setValue(d);
40711             this.fireEvent('select', this, d);
40712         },
40713         show : function(){ // retain focus styling
40714             this.onFocus();
40715         },
40716         hide : function(){
40717             this.focus.defer(10, this);
40718             var ml = this.menuListeners;
40719             this.menu.un("select", ml.select,  this);
40720             this.menu.un("show", ml.show,  this);
40721             this.menu.un("hide", ml.hide,  this);
40722         }
40723     },
40724
40725     // private
40726     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40727     onTriggerClick : function(){
40728         if(this.disabled){
40729             return;
40730         }
40731         if(this.menu == null){
40732             this.menu = new Roo.menu.DateMenu();
40733         }
40734         Roo.apply(this.menu.picker,  {
40735             showClear: this.allowBlank,
40736             minDate : this.minValue,
40737             maxDate : this.maxValue,
40738             disabledDatesRE : this.ddMatch,
40739             disabledDatesText : this.disabledDatesText,
40740             disabledDays : this.disabledDays,
40741             disabledDaysText : this.disabledDaysText,
40742             format : this.useIso ? 'Y-m-d' : this.format,
40743             minText : String.format(this.minText, this.formatDate(this.minValue)),
40744             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40745         });
40746         this.menu.on(Roo.apply({}, this.menuListeners, {
40747             scope:this
40748         }));
40749         this.menu.picker.setValue(this.getValue() || new Date());
40750         this.menu.show(this.el, "tl-bl?");
40751     },
40752
40753     beforeBlur : function(){
40754         var v = this.parseDate(this.getRawValue());
40755         if(v){
40756             this.setValue(v);
40757         }
40758     },
40759
40760     /*@
40761      * overide
40762      * 
40763      */
40764     isDirty : function() {
40765         if(this.disabled) {
40766             return false;
40767         }
40768         
40769         if(typeof(this.startValue) === 'undefined'){
40770             return false;
40771         }
40772         
40773         return String(this.getValue()) !== String(this.startValue);
40774         
40775     },
40776     // @overide
40777     cleanLeadingSpace : function(e)
40778     {
40779        return;
40780     }
40781     
40782 });/*
40783  * Based on:
40784  * Ext JS Library 1.1.1
40785  * Copyright(c) 2006-2007, Ext JS, LLC.
40786  *
40787  * Originally Released Under LGPL - original licence link has changed is not relivant.
40788  *
40789  * Fork - LGPL
40790  * <script type="text/javascript">
40791  */
40792  
40793 /**
40794  * @class Roo.form.MonthField
40795  * @extends Roo.form.TriggerField
40796  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40797 * @constructor
40798 * Create a new MonthField
40799 * @param {Object} config
40800  */
40801 Roo.form.MonthField = function(config){
40802     
40803     Roo.form.MonthField.superclass.constructor.call(this, config);
40804     
40805       this.addEvents({
40806          
40807         /**
40808          * @event select
40809          * Fires when a date is selected
40810              * @param {Roo.form.MonthFieeld} combo This combo box
40811              * @param {Date} date The date selected
40812              */
40813         'select' : true
40814          
40815     });
40816     
40817     
40818     if(typeof this.minValue == "string") {
40819         this.minValue = this.parseDate(this.minValue);
40820     }
40821     if(typeof this.maxValue == "string") {
40822         this.maxValue = this.parseDate(this.maxValue);
40823     }
40824     this.ddMatch = null;
40825     if(this.disabledDates){
40826         var dd = this.disabledDates;
40827         var re = "(?:";
40828         for(var i = 0; i < dd.length; i++){
40829             re += dd[i];
40830             if(i != dd.length-1) {
40831                 re += "|";
40832             }
40833         }
40834         this.ddMatch = new RegExp(re + ")");
40835     }
40836 };
40837
40838 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
40839     /**
40840      * @cfg {String} format
40841      * The default date format string which can be overriden for localization support.  The format must be
40842      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40843      */
40844     format : "M Y",
40845     /**
40846      * @cfg {String} altFormats
40847      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40848      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40849      */
40850     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
40851     /**
40852      * @cfg {Array} disabledDays
40853      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40854      */
40855     disabledDays : [0,1,2,3,4,5,6],
40856     /**
40857      * @cfg {String} disabledDaysText
40858      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40859      */
40860     disabledDaysText : "Disabled",
40861     /**
40862      * @cfg {Array} disabledDates
40863      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40864      * expression so they are very powerful. Some examples:
40865      * <ul>
40866      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40867      * <li>["03/08", "09/16"] would disable those days for every year</li>
40868      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40869      * <li>["03/../2006"] would disable every day in March 2006</li>
40870      * <li>["^03"] would disable every day in every March</li>
40871      * </ul>
40872      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40873      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40874      */
40875     disabledDates : null,
40876     /**
40877      * @cfg {String} disabledDatesText
40878      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40879      */
40880     disabledDatesText : "Disabled",
40881     /**
40882      * @cfg {Date/String} minValue
40883      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40884      * valid format (defaults to null).
40885      */
40886     minValue : null,
40887     /**
40888      * @cfg {Date/String} maxValue
40889      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40890      * valid format (defaults to null).
40891      */
40892     maxValue : null,
40893     /**
40894      * @cfg {String} minText
40895      * The error text to display when the date in the cell is before minValue (defaults to
40896      * 'The date in this field must be after {minValue}').
40897      */
40898     minText : "The date in this field must be equal to or after {0}",
40899     /**
40900      * @cfg {String} maxTextf
40901      * The error text to display when the date in the cell is after maxValue (defaults to
40902      * 'The date in this field must be before {maxValue}').
40903      */
40904     maxText : "The date in this field must be equal to or before {0}",
40905     /**
40906      * @cfg {String} invalidText
40907      * The error text to display when the date in the field is invalid (defaults to
40908      * '{value} is not a valid date - it must be in the format {format}').
40909      */
40910     invalidText : "{0} is not a valid date - it must be in the format {1}",
40911     /**
40912      * @cfg {String} triggerClass
40913      * An additional CSS class used to style the trigger button.  The trigger will always get the
40914      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40915      * which displays a calendar icon).
40916      */
40917     triggerClass : 'x-form-date-trigger',
40918     
40919
40920     /**
40921      * @cfg {Boolean} useIso
40922      * if enabled, then the date field will use a hidden field to store the 
40923      * real value as iso formated date. default (true)
40924      */ 
40925     useIso : true,
40926     /**
40927      * @cfg {String/Object} autoCreate
40928      * A DomHelper element spec, or true for a default element spec (defaults to
40929      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40930      */ 
40931     // private
40932     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
40933     
40934     // private
40935     hiddenField: false,
40936     
40937     hideMonthPicker : false,
40938     
40939     onRender : function(ct, position)
40940     {
40941         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
40942         if (this.useIso) {
40943             this.el.dom.removeAttribute('name'); 
40944             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40945                     'before', true);
40946             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40947             // prevent input submission
40948             this.hiddenName = this.name;
40949         }
40950             
40951             
40952     },
40953     
40954     // private
40955     validateValue : function(value)
40956     {
40957         value = this.formatDate(value);
40958         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
40959             return false;
40960         }
40961         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40962              return true;
40963         }
40964         var svalue = value;
40965         value = this.parseDate(value);
40966         if(!value){
40967             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40968             return false;
40969         }
40970         var time = value.getTime();
40971         if(this.minValue && time < this.minValue.getTime()){
40972             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40973             return false;
40974         }
40975         if(this.maxValue && time > this.maxValue.getTime()){
40976             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40977             return false;
40978         }
40979         /*if(this.disabledDays){
40980             var day = value.getDay();
40981             for(var i = 0; i < this.disabledDays.length; i++) {
40982                 if(day === this.disabledDays[i]){
40983                     this.markInvalid(this.disabledDaysText);
40984                     return false;
40985                 }
40986             }
40987         }
40988         */
40989         var fvalue = this.formatDate(value);
40990         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
40991             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40992             return false;
40993         }
40994         */
40995         return true;
40996     },
40997
40998     // private
40999     // Provides logic to override the default TriggerField.validateBlur which just returns true
41000     validateBlur : function(){
41001         return !this.menu || !this.menu.isVisible();
41002     },
41003
41004     /**
41005      * Returns the current date value of the date field.
41006      * @return {Date} The date value
41007      */
41008     getValue : function(){
41009         
41010         
41011         
41012         return  this.hiddenField ?
41013                 this.hiddenField.value :
41014                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
41015     },
41016
41017     /**
41018      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
41019      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
41020      * (the default format used is "m/d/y").
41021      * <br />Usage:
41022      * <pre><code>
41023 //All of these calls set the same date value (May 4, 2006)
41024
41025 //Pass a date object:
41026 var dt = new Date('5/4/06');
41027 monthField.setValue(dt);
41028
41029 //Pass a date string (default format):
41030 monthField.setValue('5/4/06');
41031
41032 //Pass a date string (custom format):
41033 monthField.format = 'Y-m-d';
41034 monthField.setValue('2006-5-4');
41035 </code></pre>
41036      * @param {String/Date} date The date or valid date string
41037      */
41038     setValue : function(date){
41039         Roo.log('month setValue' + date);
41040         // can only be first of month..
41041         
41042         var val = this.parseDate(date);
41043         
41044         if (this.hiddenField) {
41045             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
41046         }
41047         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
41048         this.value = this.parseDate(date);
41049     },
41050
41051     // private
41052     parseDate : function(value){
41053         if(!value || value instanceof Date){
41054             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
41055             return value;
41056         }
41057         var v = Date.parseDate(value, this.format);
41058         if (!v && this.useIso) {
41059             v = Date.parseDate(value, 'Y-m-d');
41060         }
41061         if (v) {
41062             // 
41063             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
41064         }
41065         
41066         
41067         if(!v && this.altFormats){
41068             if(!this.altFormatsArray){
41069                 this.altFormatsArray = this.altFormats.split("|");
41070             }
41071             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
41072                 v = Date.parseDate(value, this.altFormatsArray[i]);
41073             }
41074         }
41075         return v;
41076     },
41077
41078     // private
41079     formatDate : function(date, fmt){
41080         return (!date || !(date instanceof Date)) ?
41081                date : date.dateFormat(fmt || this.format);
41082     },
41083
41084     // private
41085     menuListeners : {
41086         select: function(m, d){
41087             this.setValue(d);
41088             this.fireEvent('select', this, d);
41089         },
41090         show : function(){ // retain focus styling
41091             this.onFocus();
41092         },
41093         hide : function(){
41094             this.focus.defer(10, this);
41095             var ml = this.menuListeners;
41096             this.menu.un("select", ml.select,  this);
41097             this.menu.un("show", ml.show,  this);
41098             this.menu.un("hide", ml.hide,  this);
41099         }
41100     },
41101     // private
41102     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
41103     onTriggerClick : function(){
41104         if(this.disabled){
41105             return;
41106         }
41107         if(this.menu == null){
41108             this.menu = new Roo.menu.DateMenu();
41109            
41110         }
41111         
41112         Roo.apply(this.menu.picker,  {
41113             
41114             showClear: this.allowBlank,
41115             minDate : this.minValue,
41116             maxDate : this.maxValue,
41117             disabledDatesRE : this.ddMatch,
41118             disabledDatesText : this.disabledDatesText,
41119             
41120             format : this.useIso ? 'Y-m-d' : this.format,
41121             minText : String.format(this.minText, this.formatDate(this.minValue)),
41122             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
41123             
41124         });
41125          this.menu.on(Roo.apply({}, this.menuListeners, {
41126             scope:this
41127         }));
41128        
41129         
41130         var m = this.menu;
41131         var p = m.picker;
41132         
41133         // hide month picker get's called when we called by 'before hide';
41134         
41135         var ignorehide = true;
41136         p.hideMonthPicker  = function(disableAnim){
41137             if (ignorehide) {
41138                 return;
41139             }
41140              if(this.monthPicker){
41141                 Roo.log("hideMonthPicker called");
41142                 if(disableAnim === true){
41143                     this.monthPicker.hide();
41144                 }else{
41145                     this.monthPicker.slideOut('t', {duration:.2});
41146                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
41147                     p.fireEvent("select", this, this.value);
41148                     m.hide();
41149                 }
41150             }
41151         }
41152         
41153         Roo.log('picker set value');
41154         Roo.log(this.getValue());
41155         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
41156         m.show(this.el, 'tl-bl?');
41157         ignorehide  = false;
41158         // this will trigger hideMonthPicker..
41159         
41160         
41161         // hidden the day picker
41162         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
41163         
41164         
41165         
41166       
41167         
41168         p.showMonthPicker.defer(100, p);
41169     
41170         
41171        
41172     },
41173
41174     beforeBlur : function(){
41175         var v = this.parseDate(this.getRawValue());
41176         if(v){
41177             this.setValue(v);
41178         }
41179     }
41180
41181     /** @cfg {Boolean} grow @hide */
41182     /** @cfg {Number} growMin @hide */
41183     /** @cfg {Number} growMax @hide */
41184     /**
41185      * @hide
41186      * @method autoSize
41187      */
41188 });/*
41189  * Based on:
41190  * Ext JS Library 1.1.1
41191  * Copyright(c) 2006-2007, Ext JS, LLC.
41192  *
41193  * Originally Released Under LGPL - original licence link has changed is not relivant.
41194  *
41195  * Fork - LGPL
41196  * <script type="text/javascript">
41197  */
41198  
41199
41200 /**
41201  * @class Roo.form.ComboBox
41202  * @extends Roo.form.TriggerField
41203  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
41204  * @constructor
41205  * Create a new ComboBox.
41206  * @param {Object} config Configuration options
41207  */
41208 Roo.form.ComboBox = function(config){
41209     Roo.form.ComboBox.superclass.constructor.call(this, config);
41210     this.addEvents({
41211         /**
41212          * @event expand
41213          * Fires when the dropdown list is expanded
41214              * @param {Roo.form.ComboBox} combo This combo box
41215              */
41216         'expand' : true,
41217         /**
41218          * @event collapse
41219          * Fires when the dropdown list is collapsed
41220              * @param {Roo.form.ComboBox} combo This combo box
41221              */
41222         'collapse' : true,
41223         /**
41224          * @event beforeselect
41225          * Fires before a list item is selected. Return false to cancel the selection.
41226              * @param {Roo.form.ComboBox} combo This combo box
41227              * @param {Roo.data.Record} record The data record returned from the underlying store
41228              * @param {Number} index The index of the selected item in the dropdown list
41229              */
41230         'beforeselect' : true,
41231         /**
41232          * @event select
41233          * Fires when a list item is selected
41234              * @param {Roo.form.ComboBox} combo This combo box
41235              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
41236              * @param {Number} index The index of the selected item in the dropdown list
41237              */
41238         'select' : true,
41239         /**
41240          * @event beforequery
41241          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
41242          * The event object passed has these properties:
41243              * @param {Roo.form.ComboBox} combo This combo box
41244              * @param {String} query The query
41245              * @param {Boolean} forceAll true to force "all" query
41246              * @param {Boolean} cancel true to cancel the query
41247              * @param {Object} e The query event object
41248              */
41249         'beforequery': true,
41250          /**
41251          * @event add
41252          * Fires when the 'add' icon is pressed (add a listener to enable add button)
41253              * @param {Roo.form.ComboBox} combo This combo box
41254              */
41255         'add' : true,
41256         /**
41257          * @event edit
41258          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
41259              * @param {Roo.form.ComboBox} combo This combo box
41260              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
41261              */
41262         'edit' : true
41263         
41264         
41265     });
41266     if(this.transform){
41267         this.allowDomMove = false;
41268         var s = Roo.getDom(this.transform);
41269         if(!this.hiddenName){
41270             this.hiddenName = s.name;
41271         }
41272         if(!this.store){
41273             this.mode = 'local';
41274             var d = [], opts = s.options;
41275             for(var i = 0, len = opts.length;i < len; i++){
41276                 var o = opts[i];
41277                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
41278                 if(o.selected) {
41279                     this.value = value;
41280                 }
41281                 d.push([value, o.text]);
41282             }
41283             this.store = new Roo.data.SimpleStore({
41284                 'id': 0,
41285                 fields: ['value', 'text'],
41286                 data : d
41287             });
41288             this.valueField = 'value';
41289             this.displayField = 'text';
41290         }
41291         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
41292         if(!this.lazyRender){
41293             this.target = true;
41294             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
41295             s.parentNode.removeChild(s); // remove it
41296             this.render(this.el.parentNode);
41297         }else{
41298             s.parentNode.removeChild(s); // remove it
41299         }
41300
41301     }
41302     if (this.store) {
41303         this.store = Roo.factory(this.store, Roo.data);
41304     }
41305     
41306     this.selectedIndex = -1;
41307     if(this.mode == 'local'){
41308         if(config.queryDelay === undefined){
41309             this.queryDelay = 10;
41310         }
41311         if(config.minChars === undefined){
41312             this.minChars = 0;
41313         }
41314     }
41315 };
41316
41317 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
41318     /**
41319      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
41320      */
41321     /**
41322      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
41323      * rendering into an Roo.Editor, defaults to false)
41324      */
41325     /**
41326      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
41327      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
41328      */
41329     /**
41330      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
41331      */
41332     /**
41333      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
41334      * the dropdown list (defaults to undefined, with no header element)
41335      */
41336
41337      /**
41338      * @cfg {String/Roo.Template} tpl The template to use to render the output
41339      */
41340      
41341     // private
41342     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
41343     /**
41344      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
41345      */
41346     listWidth: undefined,
41347     /**
41348      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
41349      * mode = 'remote' or 'text' if mode = 'local')
41350      */
41351     displayField: undefined,
41352     /**
41353      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
41354      * mode = 'remote' or 'value' if mode = 'local'). 
41355      * Note: use of a valueField requires the user make a selection
41356      * in order for a value to be mapped.
41357      */
41358     valueField: undefined,
41359     
41360     
41361     /**
41362      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
41363      * field's data value (defaults to the underlying DOM element's name)
41364      */
41365     hiddenName: undefined,
41366     /**
41367      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
41368      */
41369     listClass: '',
41370     /**
41371      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
41372      */
41373     selectedClass: 'x-combo-selected',
41374     /**
41375      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
41376      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
41377      * which displays a downward arrow icon).
41378      */
41379     triggerClass : 'x-form-arrow-trigger',
41380     /**
41381      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
41382      */
41383     shadow:'sides',
41384     /**
41385      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
41386      * anchor positions (defaults to 'tl-bl')
41387      */
41388     listAlign: 'tl-bl?',
41389     /**
41390      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
41391      */
41392     maxHeight: 300,
41393     /**
41394      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
41395      * query specified by the allQuery config option (defaults to 'query')
41396      */
41397     triggerAction: 'query',
41398     /**
41399      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
41400      * (defaults to 4, does not apply if editable = false)
41401      */
41402     minChars : 4,
41403     /**
41404      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
41405      * delay (typeAheadDelay) if it matches a known value (defaults to false)
41406      */
41407     typeAhead: false,
41408     /**
41409      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
41410      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
41411      */
41412     queryDelay: 500,
41413     /**
41414      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
41415      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
41416      */
41417     pageSize: 0,
41418     /**
41419      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
41420      * when editable = true (defaults to false)
41421      */
41422     selectOnFocus:false,
41423     /**
41424      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
41425      */
41426     queryParam: 'query',
41427     /**
41428      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
41429      * when mode = 'remote' (defaults to 'Loading...')
41430      */
41431     loadingText: 'Loading...',
41432     /**
41433      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
41434      */
41435     resizable: false,
41436     /**
41437      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
41438      */
41439     handleHeight : 8,
41440     /**
41441      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
41442      * traditional select (defaults to true)
41443      */
41444     editable: true,
41445     /**
41446      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
41447      */
41448     allQuery: '',
41449     /**
41450      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
41451      */
41452     mode: 'remote',
41453     /**
41454      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
41455      * listWidth has a higher value)
41456      */
41457     minListWidth : 70,
41458     /**
41459      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
41460      * allow the user to set arbitrary text into the field (defaults to false)
41461      */
41462     forceSelection:false,
41463     /**
41464      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
41465      * if typeAhead = true (defaults to 250)
41466      */
41467     typeAheadDelay : 250,
41468     /**
41469      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
41470      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
41471      */
41472     valueNotFoundText : undefined,
41473     /**
41474      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
41475      */
41476     blockFocus : false,
41477     
41478     /**
41479      * @cfg {Boolean} disableClear Disable showing of clear button.
41480      */
41481     disableClear : false,
41482     /**
41483      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
41484      */
41485     alwaysQuery : false,
41486     
41487     //private
41488     addicon : false,
41489     editicon: false,
41490     
41491     // element that contains real text value.. (when hidden is used..)
41492      
41493     // private
41494     onRender : function(ct, position)
41495     {
41496         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
41497         
41498         if(this.hiddenName){
41499             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
41500                     'before', true);
41501             this.hiddenField.value =
41502                 this.hiddenValue !== undefined ? this.hiddenValue :
41503                 this.value !== undefined ? this.value : '';
41504
41505             // prevent input submission
41506             this.el.dom.removeAttribute('name');
41507              
41508              
41509         }
41510         
41511         if(Roo.isGecko){
41512             this.el.dom.setAttribute('autocomplete', 'off');
41513         }
41514
41515         var cls = 'x-combo-list';
41516
41517         this.list = new Roo.Layer({
41518             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
41519         });
41520
41521         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
41522         this.list.setWidth(lw);
41523         this.list.swallowEvent('mousewheel');
41524         this.assetHeight = 0;
41525
41526         if(this.title){
41527             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
41528             this.assetHeight += this.header.getHeight();
41529         }
41530
41531         this.innerList = this.list.createChild({cls:cls+'-inner'});
41532         this.innerList.on('mouseover', this.onViewOver, this);
41533         this.innerList.on('mousemove', this.onViewMove, this);
41534         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41535         
41536         if(this.allowBlank && !this.pageSize && !this.disableClear){
41537             this.footer = this.list.createChild({cls:cls+'-ft'});
41538             this.pageTb = new Roo.Toolbar(this.footer);
41539            
41540         }
41541         if(this.pageSize){
41542             this.footer = this.list.createChild({cls:cls+'-ft'});
41543             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
41544                     {pageSize: this.pageSize});
41545             
41546         }
41547         
41548         if (this.pageTb && this.allowBlank && !this.disableClear) {
41549             var _this = this;
41550             this.pageTb.add(new Roo.Toolbar.Fill(), {
41551                 cls: 'x-btn-icon x-btn-clear',
41552                 text: '&#160;',
41553                 handler: function()
41554                 {
41555                     _this.collapse();
41556                     _this.clearValue();
41557                     _this.onSelect(false, -1);
41558                 }
41559             });
41560         }
41561         if (this.footer) {
41562             this.assetHeight += this.footer.getHeight();
41563         }
41564         
41565
41566         if(!this.tpl){
41567             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
41568         }
41569
41570         this.view = new Roo.View(this.innerList, this.tpl, {
41571             singleSelect:true,
41572             store: this.store,
41573             selectedClass: this.selectedClass
41574         });
41575
41576         this.view.on('click', this.onViewClick, this);
41577
41578         this.store.on('beforeload', this.onBeforeLoad, this);
41579         this.store.on('load', this.onLoad, this);
41580         this.store.on('loadexception', this.onLoadException, this);
41581
41582         if(this.resizable){
41583             this.resizer = new Roo.Resizable(this.list,  {
41584                pinned:true, handles:'se'
41585             });
41586             this.resizer.on('resize', function(r, w, h){
41587                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
41588                 this.listWidth = w;
41589                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
41590                 this.restrictHeight();
41591             }, this);
41592             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
41593         }
41594         if(!this.editable){
41595             this.editable = true;
41596             this.setEditable(false);
41597         }  
41598         
41599         
41600         if (typeof(this.events.add.listeners) != 'undefined') {
41601             
41602             this.addicon = this.wrap.createChild(
41603                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
41604        
41605             this.addicon.on('click', function(e) {
41606                 this.fireEvent('add', this);
41607             }, this);
41608         }
41609         if (typeof(this.events.edit.listeners) != 'undefined') {
41610             
41611             this.editicon = this.wrap.createChild(
41612                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
41613             if (this.addicon) {
41614                 this.editicon.setStyle('margin-left', '40px');
41615             }
41616             this.editicon.on('click', function(e) {
41617                 
41618                 // we fire even  if inothing is selected..
41619                 this.fireEvent('edit', this, this.lastData );
41620                 
41621             }, this);
41622         }
41623         
41624         
41625         
41626     },
41627
41628     // private
41629     initEvents : function(){
41630         Roo.form.ComboBox.superclass.initEvents.call(this);
41631
41632         this.keyNav = new Roo.KeyNav(this.el, {
41633             "up" : function(e){
41634                 this.inKeyMode = true;
41635                 this.selectPrev();
41636             },
41637
41638             "down" : function(e){
41639                 if(!this.isExpanded()){
41640                     this.onTriggerClick();
41641                 }else{
41642                     this.inKeyMode = true;
41643                     this.selectNext();
41644                 }
41645             },
41646
41647             "enter" : function(e){
41648                 this.onViewClick();
41649                 //return true;
41650             },
41651
41652             "esc" : function(e){
41653                 this.collapse();
41654             },
41655
41656             "tab" : function(e){
41657                 this.onViewClick(false);
41658                 this.fireEvent("specialkey", this, e);
41659                 return true;
41660             },
41661
41662             scope : this,
41663
41664             doRelay : function(foo, bar, hname){
41665                 if(hname == 'down' || this.scope.isExpanded()){
41666                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41667                 }
41668                 return true;
41669             },
41670
41671             forceKeyDown: true
41672         });
41673         this.queryDelay = Math.max(this.queryDelay || 10,
41674                 this.mode == 'local' ? 10 : 250);
41675         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
41676         if(this.typeAhead){
41677             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
41678         }
41679         if(this.editable !== false){
41680             this.el.on("keyup", this.onKeyUp, this);
41681         }
41682         if(this.forceSelection){
41683             this.on('blur', this.doForce, this);
41684         }
41685     },
41686
41687     onDestroy : function(){
41688         if(this.view){
41689             this.view.setStore(null);
41690             this.view.el.removeAllListeners();
41691             this.view.el.remove();
41692             this.view.purgeListeners();
41693         }
41694         if(this.list){
41695             this.list.destroy();
41696         }
41697         if(this.store){
41698             this.store.un('beforeload', this.onBeforeLoad, this);
41699             this.store.un('load', this.onLoad, this);
41700             this.store.un('loadexception', this.onLoadException, this);
41701         }
41702         Roo.form.ComboBox.superclass.onDestroy.call(this);
41703     },
41704
41705     // private
41706     fireKey : function(e){
41707         if(e.isNavKeyPress() && !this.list.isVisible()){
41708             this.fireEvent("specialkey", this, e);
41709         }
41710     },
41711
41712     // private
41713     onResize: function(w, h){
41714         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
41715         
41716         if(typeof w != 'number'){
41717             // we do not handle it!?!?
41718             return;
41719         }
41720         var tw = this.trigger.getWidth();
41721         tw += this.addicon ? this.addicon.getWidth() : 0;
41722         tw += this.editicon ? this.editicon.getWidth() : 0;
41723         var x = w - tw;
41724         this.el.setWidth( this.adjustWidth('input', x));
41725             
41726         this.trigger.setStyle('left', x+'px');
41727         
41728         if(this.list && this.listWidth === undefined){
41729             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
41730             this.list.setWidth(lw);
41731             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41732         }
41733         
41734     
41735         
41736     },
41737
41738     /**
41739      * Allow or prevent the user from directly editing the field text.  If false is passed,
41740      * the user will only be able to select from the items defined in the dropdown list.  This method
41741      * is the runtime equivalent of setting the 'editable' config option at config time.
41742      * @param {Boolean} value True to allow the user to directly edit the field text
41743      */
41744     setEditable : function(value){
41745         if(value == this.editable){
41746             return;
41747         }
41748         this.editable = value;
41749         if(!value){
41750             this.el.dom.setAttribute('readOnly', true);
41751             this.el.on('mousedown', this.onTriggerClick,  this);
41752             this.el.addClass('x-combo-noedit');
41753         }else{
41754             this.el.dom.setAttribute('readOnly', false);
41755             this.el.un('mousedown', this.onTriggerClick,  this);
41756             this.el.removeClass('x-combo-noedit');
41757         }
41758     },
41759
41760     // private
41761     onBeforeLoad : function(){
41762         if(!this.hasFocus){
41763             return;
41764         }
41765         this.innerList.update(this.loadingText ?
41766                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
41767         this.restrictHeight();
41768         this.selectedIndex = -1;
41769     },
41770
41771     // private
41772     onLoad : function(){
41773         if(!this.hasFocus){
41774             return;
41775         }
41776         if(this.store.getCount() > 0){
41777             this.expand();
41778             this.restrictHeight();
41779             if(this.lastQuery == this.allQuery){
41780                 if(this.editable){
41781                     this.el.dom.select();
41782                 }
41783                 if(!this.selectByValue(this.value, true)){
41784                     this.select(0, true);
41785                 }
41786             }else{
41787                 this.selectNext();
41788                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
41789                     this.taTask.delay(this.typeAheadDelay);
41790                 }
41791             }
41792         }else{
41793             this.onEmptyResults();
41794         }
41795         //this.el.focus();
41796     },
41797     // private
41798     onLoadException : function()
41799     {
41800         this.collapse();
41801         Roo.log(this.store.reader.jsonData);
41802         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
41803             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
41804         }
41805         
41806         
41807     },
41808     // private
41809     onTypeAhead : function(){
41810         if(this.store.getCount() > 0){
41811             var r = this.store.getAt(0);
41812             var newValue = r.data[this.displayField];
41813             var len = newValue.length;
41814             var selStart = this.getRawValue().length;
41815             if(selStart != len){
41816                 this.setRawValue(newValue);
41817                 this.selectText(selStart, newValue.length);
41818             }
41819         }
41820     },
41821
41822     // private
41823     onSelect : function(record, index){
41824         if(this.fireEvent('beforeselect', this, record, index) !== false){
41825             this.setFromData(index > -1 ? record.data : false);
41826             this.collapse();
41827             this.fireEvent('select', this, record, index);
41828         }
41829     },
41830
41831     /**
41832      * Returns the currently selected field value or empty string if no value is set.
41833      * @return {String} value The selected value
41834      */
41835     getValue : function(){
41836         if(this.valueField){
41837             return typeof this.value != 'undefined' ? this.value : '';
41838         }
41839         return Roo.form.ComboBox.superclass.getValue.call(this);
41840     },
41841
41842     /**
41843      * Clears any text/value currently set in the field
41844      */
41845     clearValue : function(){
41846         if(this.hiddenField){
41847             this.hiddenField.value = '';
41848         }
41849         this.value = '';
41850         this.setRawValue('');
41851         this.lastSelectionText = '';
41852         
41853     },
41854
41855     /**
41856      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
41857      * will be displayed in the field.  If the value does not match the data value of an existing item,
41858      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
41859      * Otherwise the field will be blank (although the value will still be set).
41860      * @param {String} value The value to match
41861      */
41862     setValue : function(v){
41863         var text = v;
41864         if(this.valueField){
41865             var r = this.findRecord(this.valueField, v);
41866             if(r){
41867                 text = r.data[this.displayField];
41868             }else if(this.valueNotFoundText !== undefined){
41869                 text = this.valueNotFoundText;
41870             }
41871         }
41872         this.lastSelectionText = text;
41873         if(this.hiddenField){
41874             this.hiddenField.value = v;
41875         }
41876         Roo.form.ComboBox.superclass.setValue.call(this, text);
41877         this.value = v;
41878     },
41879     /**
41880      * @property {Object} the last set data for the element
41881      */
41882     
41883     lastData : false,
41884     /**
41885      * Sets the value of the field based on a object which is related to the record format for the store.
41886      * @param {Object} value the value to set as. or false on reset?
41887      */
41888     setFromData : function(o){
41889         var dv = ''; // display value
41890         var vv = ''; // value value..
41891         this.lastData = o;
41892         if (this.displayField) {
41893             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
41894         } else {
41895             // this is an error condition!!!
41896             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
41897         }
41898         
41899         if(this.valueField){
41900             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
41901         }
41902         if(this.hiddenField){
41903             this.hiddenField.value = vv;
41904             
41905             this.lastSelectionText = dv;
41906             Roo.form.ComboBox.superclass.setValue.call(this, dv);
41907             this.value = vv;
41908             return;
41909         }
41910         // no hidden field.. - we store the value in 'value', but still display
41911         // display field!!!!
41912         this.lastSelectionText = dv;
41913         Roo.form.ComboBox.superclass.setValue.call(this, dv);
41914         this.value = vv;
41915         
41916         
41917     },
41918     // private
41919     reset : function(){
41920         // overridden so that last data is reset..
41921         this.setValue(this.resetValue);
41922         this.originalValue = this.getValue();
41923         this.clearInvalid();
41924         this.lastData = false;
41925         if (this.view) {
41926             this.view.clearSelections();
41927         }
41928     },
41929     // private
41930     findRecord : function(prop, value){
41931         var record;
41932         if(this.store.getCount() > 0){
41933             this.store.each(function(r){
41934                 if(r.data[prop] == value){
41935                     record = r;
41936                     return false;
41937                 }
41938                 return true;
41939             });
41940         }
41941         return record;
41942     },
41943     
41944     getName: function()
41945     {
41946         // returns hidden if it's set..
41947         if (!this.rendered) {return ''};
41948         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
41949         
41950     },
41951     // private
41952     onViewMove : function(e, t){
41953         this.inKeyMode = false;
41954     },
41955
41956     // private
41957     onViewOver : function(e, t){
41958         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
41959             return;
41960         }
41961         var item = this.view.findItemFromChild(t);
41962         if(item){
41963             var index = this.view.indexOf(item);
41964             this.select(index, false);
41965         }
41966     },
41967
41968     // private
41969     onViewClick : function(doFocus)
41970     {
41971         var index = this.view.getSelectedIndexes()[0];
41972         var r = this.store.getAt(index);
41973         if(r){
41974             this.onSelect(r, index);
41975         }
41976         if(doFocus !== false && !this.blockFocus){
41977             this.el.focus();
41978         }
41979     },
41980
41981     // private
41982     restrictHeight : function(){
41983         this.innerList.dom.style.height = '';
41984         var inner = this.innerList.dom;
41985         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
41986         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
41987         this.list.beginUpdate();
41988         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
41989         this.list.alignTo(this.el, this.listAlign);
41990         this.list.endUpdate();
41991     },
41992
41993     // private
41994     onEmptyResults : function(){
41995         this.collapse();
41996     },
41997
41998     /**
41999      * Returns true if the dropdown list is expanded, else false.
42000      */
42001     isExpanded : function(){
42002         return this.list.isVisible();
42003     },
42004
42005     /**
42006      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
42007      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
42008      * @param {String} value The data value of the item to select
42009      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
42010      * selected item if it is not currently in view (defaults to true)
42011      * @return {Boolean} True if the value matched an item in the list, else false
42012      */
42013     selectByValue : function(v, scrollIntoView){
42014         if(v !== undefined && v !== null){
42015             var r = this.findRecord(this.valueField || this.displayField, v);
42016             if(r){
42017                 this.select(this.store.indexOf(r), scrollIntoView);
42018                 return true;
42019             }
42020         }
42021         return false;
42022     },
42023
42024     /**
42025      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
42026      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
42027      * @param {Number} index The zero-based index of the list item to select
42028      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
42029      * selected item if it is not currently in view (defaults to true)
42030      */
42031     select : function(index, scrollIntoView){
42032         this.selectedIndex = index;
42033         this.view.select(index);
42034         if(scrollIntoView !== false){
42035             var el = this.view.getNode(index);
42036             if(el){
42037                 this.innerList.scrollChildIntoView(el, false);
42038             }
42039         }
42040     },
42041
42042     // private
42043     selectNext : function(){
42044         var ct = this.store.getCount();
42045         if(ct > 0){
42046             if(this.selectedIndex == -1){
42047                 this.select(0);
42048             }else if(this.selectedIndex < ct-1){
42049                 this.select(this.selectedIndex+1);
42050             }
42051         }
42052     },
42053
42054     // private
42055     selectPrev : function(){
42056         var ct = this.store.getCount();
42057         if(ct > 0){
42058             if(this.selectedIndex == -1){
42059                 this.select(0);
42060             }else if(this.selectedIndex != 0){
42061                 this.select(this.selectedIndex-1);
42062             }
42063         }
42064     },
42065
42066     // private
42067     onKeyUp : function(e){
42068         if(this.editable !== false && !e.isSpecialKey()){
42069             this.lastKey = e.getKey();
42070             this.dqTask.delay(this.queryDelay);
42071         }
42072     },
42073
42074     // private
42075     validateBlur : function(){
42076         return !this.list || !this.list.isVisible();   
42077     },
42078
42079     // private
42080     initQuery : function(){
42081         this.doQuery(this.getRawValue());
42082     },
42083
42084     // private
42085     doForce : function(){
42086         if(this.el.dom.value.length > 0){
42087             this.el.dom.value =
42088                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
42089              
42090         }
42091     },
42092
42093     /**
42094      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
42095      * query allowing the query action to be canceled if needed.
42096      * @param {String} query The SQL query to execute
42097      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
42098      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
42099      * saved in the current store (defaults to false)
42100      */
42101     doQuery : function(q, forceAll){
42102         if(q === undefined || q === null){
42103             q = '';
42104         }
42105         var qe = {
42106             query: q,
42107             forceAll: forceAll,
42108             combo: this,
42109             cancel:false
42110         };
42111         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
42112             return false;
42113         }
42114         q = qe.query;
42115         forceAll = qe.forceAll;
42116         if(forceAll === true || (q.length >= this.minChars)){
42117             if(this.lastQuery != q || this.alwaysQuery){
42118                 this.lastQuery = q;
42119                 if(this.mode == 'local'){
42120                     this.selectedIndex = -1;
42121                     if(forceAll){
42122                         this.store.clearFilter();
42123                     }else{
42124                         this.store.filter(this.displayField, q);
42125                     }
42126                     this.onLoad();
42127                 }else{
42128                     this.store.baseParams[this.queryParam] = q;
42129                     this.store.load({
42130                         params: this.getParams(q)
42131                     });
42132                     this.expand();
42133                 }
42134             }else{
42135                 this.selectedIndex = -1;
42136                 this.onLoad();   
42137             }
42138         }
42139     },
42140
42141     // private
42142     getParams : function(q){
42143         var p = {};
42144         //p[this.queryParam] = q;
42145         if(this.pageSize){
42146             p.start = 0;
42147             p.limit = this.pageSize;
42148         }
42149         return p;
42150     },
42151
42152     /**
42153      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
42154      */
42155     collapse : function(){
42156         if(!this.isExpanded()){
42157             return;
42158         }
42159         this.list.hide();
42160         Roo.get(document).un('mousedown', this.collapseIf, this);
42161         Roo.get(document).un('mousewheel', this.collapseIf, this);
42162         if (!this.editable) {
42163             Roo.get(document).un('keydown', this.listKeyPress, this);
42164         }
42165         this.fireEvent('collapse', this);
42166     },
42167
42168     // private
42169     collapseIf : function(e){
42170         if(!e.within(this.wrap) && !e.within(this.list)){
42171             this.collapse();
42172         }
42173     },
42174
42175     /**
42176      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
42177      */
42178     expand : function(){
42179         if(this.isExpanded() || !this.hasFocus){
42180             return;
42181         }
42182         this.list.alignTo(this.el, this.listAlign);
42183         this.list.show();
42184         Roo.get(document).on('mousedown', this.collapseIf, this);
42185         Roo.get(document).on('mousewheel', this.collapseIf, this);
42186         if (!this.editable) {
42187             Roo.get(document).on('keydown', this.listKeyPress, this);
42188         }
42189         
42190         this.fireEvent('expand', this);
42191     },
42192
42193     // private
42194     // Implements the default empty TriggerField.onTriggerClick function
42195     onTriggerClick : function(){
42196         if(this.disabled){
42197             return;
42198         }
42199         if(this.isExpanded()){
42200             this.collapse();
42201             if (!this.blockFocus) {
42202                 this.el.focus();
42203             }
42204             
42205         }else {
42206             this.hasFocus = true;
42207             if(this.triggerAction == 'all') {
42208                 this.doQuery(this.allQuery, true);
42209             } else {
42210                 this.doQuery(this.getRawValue());
42211             }
42212             if (!this.blockFocus) {
42213                 this.el.focus();
42214             }
42215         }
42216     },
42217     listKeyPress : function(e)
42218     {
42219         //Roo.log('listkeypress');
42220         // scroll to first matching element based on key pres..
42221         if (e.isSpecialKey()) {
42222             return false;
42223         }
42224         var k = String.fromCharCode(e.getKey()).toUpperCase();
42225         //Roo.log(k);
42226         var match  = false;
42227         var csel = this.view.getSelectedNodes();
42228         var cselitem = false;
42229         if (csel.length) {
42230             var ix = this.view.indexOf(csel[0]);
42231             cselitem  = this.store.getAt(ix);
42232             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
42233                 cselitem = false;
42234             }
42235             
42236         }
42237         
42238         this.store.each(function(v) { 
42239             if (cselitem) {
42240                 // start at existing selection.
42241                 if (cselitem.id == v.id) {
42242                     cselitem = false;
42243                 }
42244                 return;
42245             }
42246                 
42247             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
42248                 match = this.store.indexOf(v);
42249                 return false;
42250             }
42251         }, this);
42252         
42253         if (match === false) {
42254             return true; // no more action?
42255         }
42256         // scroll to?
42257         this.view.select(match);
42258         var sn = Roo.get(this.view.getSelectedNodes()[0]);
42259         sn.scrollIntoView(sn.dom.parentNode, false);
42260     } 
42261
42262     /** 
42263     * @cfg {Boolean} grow 
42264     * @hide 
42265     */
42266     /** 
42267     * @cfg {Number} growMin 
42268     * @hide 
42269     */
42270     /** 
42271     * @cfg {Number} growMax 
42272     * @hide 
42273     */
42274     /**
42275      * @hide
42276      * @method autoSize
42277      */
42278 });/*
42279  * Copyright(c) 2010-2012, Roo J Solutions Limited
42280  *
42281  * Licence LGPL
42282  *
42283  */
42284
42285 /**
42286  * @class Roo.form.ComboBoxArray
42287  * @extends Roo.form.TextField
42288  * A facebook style adder... for lists of email / people / countries  etc...
42289  * pick multiple items from a combo box, and shows each one.
42290  *
42291  *  Fred [x]  Brian [x]  [Pick another |v]
42292  *
42293  *
42294  *  For this to work: it needs various extra information
42295  *    - normal combo problay has
42296  *      name, hiddenName
42297  *    + displayField, valueField
42298  *
42299  *    For our purpose...
42300  *
42301  *
42302  *   If we change from 'extends' to wrapping...
42303  *   
42304  *  
42305  *
42306  
42307  
42308  * @constructor
42309  * Create a new ComboBoxArray.
42310  * @param {Object} config Configuration options
42311  */
42312  
42313
42314 Roo.form.ComboBoxArray = function(config)
42315 {
42316     this.addEvents({
42317         /**
42318          * @event beforeremove
42319          * Fires before remove the value from the list
42320              * @param {Roo.form.ComboBoxArray} _self This combo box array
42321              * @param {Roo.form.ComboBoxArray.Item} item removed item
42322              */
42323         'beforeremove' : true,
42324         /**
42325          * @event remove
42326          * Fires when remove the value from the list
42327              * @param {Roo.form.ComboBoxArray} _self This combo box array
42328              * @param {Roo.form.ComboBoxArray.Item} item removed item
42329              */
42330         'remove' : true
42331         
42332         
42333     });
42334     
42335     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
42336     
42337     this.items = new Roo.util.MixedCollection(false);
42338     
42339     // construct the child combo...
42340     
42341     
42342     
42343     
42344    
42345     
42346 }
42347
42348  
42349 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
42350
42351     /**
42352      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
42353      */
42354     
42355     lastData : false,
42356     
42357     // behavies liek a hiddne field
42358     inputType:      'hidden',
42359     /**
42360      * @cfg {Number} width The width of the box that displays the selected element
42361      */ 
42362     width:          300,
42363
42364     
42365     
42366     /**
42367      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
42368      */
42369     name : false,
42370     /**
42371      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
42372      */
42373     hiddenName : false,
42374       /**
42375      * @cfg {String} seperator    The value seperator normally ',' 
42376      */
42377     seperator : ',',
42378     
42379     // private the array of items that are displayed..
42380     items  : false,
42381     // private - the hidden field el.
42382     hiddenEl : false,
42383     // private - the filed el..
42384     el : false,
42385     
42386     //validateValue : function() { return true; }, // all values are ok!
42387     //onAddClick: function() { },
42388     
42389     onRender : function(ct, position) 
42390     {
42391         
42392         // create the standard hidden element
42393         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
42394         
42395         
42396         // give fake names to child combo;
42397         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
42398         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
42399         
42400         this.combo = Roo.factory(this.combo, Roo.form);
42401         this.combo.onRender(ct, position);
42402         if (typeof(this.combo.width) != 'undefined') {
42403             this.combo.onResize(this.combo.width,0);
42404         }
42405         
42406         this.combo.initEvents();
42407         
42408         // assigned so form know we need to do this..
42409         this.store          = this.combo.store;
42410         this.valueField     = this.combo.valueField;
42411         this.displayField   = this.combo.displayField ;
42412         
42413         
42414         this.combo.wrap.addClass('x-cbarray-grp');
42415         
42416         var cbwrap = this.combo.wrap.createChild(
42417             {tag: 'div', cls: 'x-cbarray-cb'},
42418             this.combo.el.dom
42419         );
42420         
42421              
42422         this.hiddenEl = this.combo.wrap.createChild({
42423             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
42424         });
42425         this.el = this.combo.wrap.createChild({
42426             tag: 'input',  type:'hidden' , name: this.name, value : ''
42427         });
42428          //   this.el.dom.removeAttribute("name");
42429         
42430         
42431         this.outerWrap = this.combo.wrap;
42432         this.wrap = cbwrap;
42433         
42434         this.outerWrap.setWidth(this.width);
42435         this.outerWrap.dom.removeChild(this.el.dom);
42436         
42437         this.wrap.dom.appendChild(this.el.dom);
42438         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
42439         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
42440         
42441         this.combo.trigger.setStyle('position','relative');
42442         this.combo.trigger.setStyle('left', '0px');
42443         this.combo.trigger.setStyle('top', '2px');
42444         
42445         this.combo.el.setStyle('vertical-align', 'text-bottom');
42446         
42447         //this.trigger.setStyle('vertical-align', 'top');
42448         
42449         // this should use the code from combo really... on('add' ....)
42450         if (this.adder) {
42451             
42452         
42453             this.adder = this.outerWrap.createChild(
42454                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
42455             var _t = this;
42456             this.adder.on('click', function(e) {
42457                 _t.fireEvent('adderclick', this, e);
42458             }, _t);
42459         }
42460         //var _t = this;
42461         //this.adder.on('click', this.onAddClick, _t);
42462         
42463         
42464         this.combo.on('select', function(cb, rec, ix) {
42465             this.addItem(rec.data);
42466             
42467             cb.setValue('');
42468             cb.el.dom.value = '';
42469             //cb.lastData = rec.data;
42470             // add to list
42471             
42472         }, this);
42473         
42474         
42475     },
42476     
42477     
42478     getName: function()
42479     {
42480         // returns hidden if it's set..
42481         if (!this.rendered) {return ''};
42482         return  this.hiddenName ? this.hiddenName : this.name;
42483         
42484     },
42485     
42486     
42487     onResize: function(w, h){
42488         
42489         return;
42490         // not sure if this is needed..
42491         //this.combo.onResize(w,h);
42492         
42493         if(typeof w != 'number'){
42494             // we do not handle it!?!?
42495             return;
42496         }
42497         var tw = this.combo.trigger.getWidth();
42498         tw += this.addicon ? this.addicon.getWidth() : 0;
42499         tw += this.editicon ? this.editicon.getWidth() : 0;
42500         var x = w - tw;
42501         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
42502             
42503         this.combo.trigger.setStyle('left', '0px');
42504         
42505         if(this.list && this.listWidth === undefined){
42506             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
42507             this.list.setWidth(lw);
42508             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42509         }
42510         
42511     
42512         
42513     },
42514     
42515     addItem: function(rec)
42516     {
42517         var valueField = this.combo.valueField;
42518         var displayField = this.combo.displayField;
42519         
42520         if (this.items.indexOfKey(rec[valueField]) > -1) {
42521             //console.log("GOT " + rec.data.id);
42522             return;
42523         }
42524         
42525         var x = new Roo.form.ComboBoxArray.Item({
42526             //id : rec[this.idField],
42527             data : rec,
42528             displayField : displayField ,
42529             tipField : displayField ,
42530             cb : this
42531         });
42532         // use the 
42533         this.items.add(rec[valueField],x);
42534         // add it before the element..
42535         this.updateHiddenEl();
42536         x.render(this.outerWrap, this.wrap.dom);
42537         // add the image handler..
42538     },
42539     
42540     updateHiddenEl : function()
42541     {
42542         this.validate();
42543         if (!this.hiddenEl) {
42544             return;
42545         }
42546         var ar = [];
42547         var idField = this.combo.valueField;
42548         
42549         this.items.each(function(f) {
42550             ar.push(f.data[idField]);
42551         });
42552         this.hiddenEl.dom.value = ar.join(this.seperator);
42553         this.validate();
42554     },
42555     
42556     reset : function()
42557     {
42558         this.items.clear();
42559         
42560         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
42561            el.remove();
42562         });
42563         
42564         this.el.dom.value = '';
42565         if (this.hiddenEl) {
42566             this.hiddenEl.dom.value = '';
42567         }
42568         
42569     },
42570     getValue: function()
42571     {
42572         return this.hiddenEl ? this.hiddenEl.dom.value : '';
42573     },
42574     setValue: function(v) // not a valid action - must use addItems..
42575     {
42576         
42577         this.reset();
42578          
42579         if (this.store.isLocal && (typeof(v) == 'string')) {
42580             // then we can use the store to find the values..
42581             // comma seperated at present.. this needs to allow JSON based encoding..
42582             this.hiddenEl.value  = v;
42583             var v_ar = [];
42584             Roo.each(v.split(this.seperator), function(k) {
42585                 Roo.log("CHECK " + this.valueField + ',' + k);
42586                 var li = this.store.query(this.valueField, k);
42587                 if (!li.length) {
42588                     return;
42589                 }
42590                 var add = {};
42591                 add[this.valueField] = k;
42592                 add[this.displayField] = li.item(0).data[this.displayField];
42593                 
42594                 this.addItem(add);
42595             }, this) 
42596              
42597         }
42598         if (typeof(v) == 'object' ) {
42599             // then let's assume it's an array of objects..
42600             Roo.each(v, function(l) {
42601                 var add = l;
42602                 if (typeof(l) == 'string') {
42603                     add = {};
42604                     add[this.valueField] = l;
42605                     add[this.displayField] = l
42606                 }
42607                 this.addItem(add);
42608             }, this);
42609              
42610         }
42611         
42612         
42613     },
42614     setFromData: function(v)
42615     {
42616         // this recieves an object, if setValues is called.
42617         this.reset();
42618         this.el.dom.value = v[this.displayField];
42619         this.hiddenEl.dom.value = v[this.valueField];
42620         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
42621             return;
42622         }
42623         var kv = v[this.valueField];
42624         var dv = v[this.displayField];
42625         kv = typeof(kv) != 'string' ? '' : kv;
42626         dv = typeof(dv) != 'string' ? '' : dv;
42627         
42628         
42629         var keys = kv.split(this.seperator);
42630         var display = dv.split(this.seperator);
42631         for (var i = 0 ; i < keys.length; i++) {
42632             add = {};
42633             add[this.valueField] = keys[i];
42634             add[this.displayField] = display[i];
42635             this.addItem(add);
42636         }
42637       
42638         
42639     },
42640     
42641     /**
42642      * Validates the combox array value
42643      * @return {Boolean} True if the value is valid, else false
42644      */
42645     validate : function(){
42646         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
42647             this.clearInvalid();
42648             return true;
42649         }
42650         return false;
42651     },
42652     
42653     validateValue : function(value){
42654         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
42655         
42656     },
42657     
42658     /*@
42659      * overide
42660      * 
42661      */
42662     isDirty : function() {
42663         if(this.disabled) {
42664             return false;
42665         }
42666         
42667         try {
42668             var d = Roo.decode(String(this.originalValue));
42669         } catch (e) {
42670             return String(this.getValue()) !== String(this.originalValue);
42671         }
42672         
42673         var originalValue = [];
42674         
42675         for (var i = 0; i < d.length; i++){
42676             originalValue.push(d[i][this.valueField]);
42677         }
42678         
42679         return String(this.getValue()) !== String(originalValue.join(this.seperator));
42680         
42681     }
42682     
42683 });
42684
42685
42686
42687 /**
42688  * @class Roo.form.ComboBoxArray.Item
42689  * @extends Roo.BoxComponent
42690  * A selected item in the list
42691  *  Fred [x]  Brian [x]  [Pick another |v]
42692  * 
42693  * @constructor
42694  * Create a new item.
42695  * @param {Object} config Configuration options
42696  */
42697  
42698 Roo.form.ComboBoxArray.Item = function(config) {
42699     config.id = Roo.id();
42700     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
42701 }
42702
42703 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
42704     data : {},
42705     cb: false,
42706     displayField : false,
42707     tipField : false,
42708     
42709     
42710     defaultAutoCreate : {
42711         tag: 'div',
42712         cls: 'x-cbarray-item',
42713         cn : [ 
42714             { tag: 'div' },
42715             {
42716                 tag: 'img',
42717                 width:16,
42718                 height : 16,
42719                 src : Roo.BLANK_IMAGE_URL ,
42720                 align: 'center'
42721             }
42722         ]
42723         
42724     },
42725     
42726  
42727     onRender : function(ct, position)
42728     {
42729         Roo.form.Field.superclass.onRender.call(this, ct, position);
42730         
42731         if(!this.el){
42732             var cfg = this.getAutoCreate();
42733             this.el = ct.createChild(cfg, position);
42734         }
42735         
42736         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
42737         
42738         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
42739             this.cb.renderer(this.data) :
42740             String.format('{0}',this.data[this.displayField]);
42741         
42742             
42743         this.el.child('div').dom.setAttribute('qtip',
42744                         String.format('{0}',this.data[this.tipField])
42745         );
42746         
42747         this.el.child('img').on('click', this.remove, this);
42748         
42749     },
42750    
42751     remove : function()
42752     {
42753         if(this.cb.disabled){
42754             return;
42755         }
42756         
42757         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
42758             this.cb.items.remove(this);
42759             this.el.child('img').un('click', this.remove, this);
42760             this.el.remove();
42761             this.cb.updateHiddenEl();
42762
42763             this.cb.fireEvent('remove', this.cb, this);
42764         }
42765         
42766     }
42767 });/*
42768  * RooJS Library 1.1.1
42769  * Copyright(c) 2008-2011  Alan Knowles
42770  *
42771  * License - LGPL
42772  */
42773  
42774
42775 /**
42776  * @class Roo.form.ComboNested
42777  * @extends Roo.form.ComboBox
42778  * A combobox for that allows selection of nested items in a list,
42779  * eg.
42780  *
42781  *  Book
42782  *    -> red
42783  *    -> green
42784  *  Table
42785  *    -> square
42786  *      ->red
42787  *      ->green
42788  *    -> rectangle
42789  *      ->green
42790  *      
42791  * 
42792  * @constructor
42793  * Create a new ComboNested
42794  * @param {Object} config Configuration options
42795  */
42796 Roo.form.ComboNested = function(config){
42797     Roo.form.ComboCheck.superclass.constructor.call(this, config);
42798     // should verify some data...
42799     // like
42800     // hiddenName = required..
42801     // displayField = required
42802     // valudField == required
42803     var req= [ 'hiddenName', 'displayField', 'valueField' ];
42804     var _t = this;
42805     Roo.each(req, function(e) {
42806         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
42807             throw "Roo.form.ComboNested : missing value for: " + e;
42808         }
42809     });
42810      
42811     
42812 };
42813
42814 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
42815    
42816     /*
42817      * @config {Number} max Number of columns to show
42818      */
42819     
42820     maxColumns : 3,
42821    
42822     list : null, // the outermost div..
42823     innerLists : null, // the
42824     views : null,
42825     stores : null,
42826     // private
42827     loadingChildren : false,
42828     
42829     onRender : function(ct, position)
42830     {
42831         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
42832         
42833         if(this.hiddenName){
42834             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
42835                     'before', true);
42836             this.hiddenField.value =
42837                 this.hiddenValue !== undefined ? this.hiddenValue :
42838                 this.value !== undefined ? this.value : '';
42839
42840             // prevent input submission
42841             this.el.dom.removeAttribute('name');
42842              
42843              
42844         }
42845         
42846         if(Roo.isGecko){
42847             this.el.dom.setAttribute('autocomplete', 'off');
42848         }
42849
42850         var cls = 'x-combo-list';
42851
42852         this.list = new Roo.Layer({
42853             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
42854         });
42855
42856         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
42857         this.list.setWidth(lw);
42858         this.list.swallowEvent('mousewheel');
42859         this.assetHeight = 0;
42860
42861         if(this.title){
42862             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
42863             this.assetHeight += this.header.getHeight();
42864         }
42865         this.innerLists = [];
42866         this.views = [];
42867         this.stores = [];
42868         for (var i =0 ; i < this.maxColumns; i++) {
42869             this.onRenderList( cls, i);
42870         }
42871         
42872         // always needs footer, as we are going to have an 'OK' button.
42873         this.footer = this.list.createChild({cls:cls+'-ft'});
42874         this.pageTb = new Roo.Toolbar(this.footer);  
42875         var _this = this;
42876         this.pageTb.add(  {
42877             
42878             text: 'Done',
42879             handler: function()
42880             {
42881                 _this.collapse();
42882             }
42883         });
42884         
42885         if ( this.allowBlank && !this.disableClear) {
42886             
42887             this.pageTb.add(new Roo.Toolbar.Fill(), {
42888                 cls: 'x-btn-icon x-btn-clear',
42889                 text: '&#160;',
42890                 handler: function()
42891                 {
42892                     _this.collapse();
42893                     _this.clearValue();
42894                     _this.onSelect(false, -1);
42895                 }
42896             });
42897         }
42898         if (this.footer) {
42899             this.assetHeight += this.footer.getHeight();
42900         }
42901         
42902     },
42903     onRenderList : function (  cls, i)
42904     {
42905         
42906         var lw = Math.floor(
42907                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
42908         );
42909         
42910         this.list.setWidth(lw); // default to '1'
42911
42912         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
42913         //il.on('mouseover', this.onViewOver, this, { list:  i });
42914         //il.on('mousemove', this.onViewMove, this, { list:  i });
42915         il.setWidth(lw);
42916         il.setStyle({ 'overflow-x' : 'hidden'});
42917
42918         if(!this.tpl){
42919             this.tpl = new Roo.Template({
42920                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
42921                 isEmpty: function (value, allValues) {
42922                     //Roo.log(value);
42923                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
42924                     return dl ? 'has-children' : 'no-children'
42925                 }
42926             });
42927         }
42928         
42929         var store  = this.store;
42930         if (i > 0) {
42931             store  = new Roo.data.SimpleStore({
42932                 //fields : this.store.reader.meta.fields,
42933                 reader : this.store.reader,
42934                 data : [ ]
42935             });
42936         }
42937         this.stores[i]  = store;
42938                   
42939         var view = this.views[i] = new Roo.View(
42940             il,
42941             this.tpl,
42942             {
42943                 singleSelect:true,
42944                 store: store,
42945                 selectedClass: this.selectedClass
42946             }
42947         );
42948         view.getEl().setWidth(lw);
42949         view.getEl().setStyle({
42950             position: i < 1 ? 'relative' : 'absolute',
42951             top: 0,
42952             left: (i * lw ) + 'px',
42953             display : i > 0 ? 'none' : 'block'
42954         });
42955         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
42956         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
42957         //view.on('click', this.onViewClick, this, { list : i });
42958
42959         store.on('beforeload', this.onBeforeLoad, this);
42960         store.on('load',  this.onLoad, this, { list  : i});
42961         store.on('loadexception', this.onLoadException, this);
42962
42963         // hide the other vies..
42964         
42965         
42966         
42967     },
42968       
42969     restrictHeight : function()
42970     {
42971         var mh = 0;
42972         Roo.each(this.innerLists, function(il,i) {
42973             var el = this.views[i].getEl();
42974             el.dom.style.height = '';
42975             var inner = el.dom;
42976             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
42977             // only adjust heights on other ones..
42978             mh = Math.max(h, mh);
42979             if (i < 1) {
42980                 
42981                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
42982                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
42983                
42984             }
42985             
42986             
42987         }, this);
42988         
42989         this.list.beginUpdate();
42990         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
42991         this.list.alignTo(this.el, this.listAlign);
42992         this.list.endUpdate();
42993         
42994     },
42995      
42996     
42997     // -- store handlers..
42998     // private
42999     onBeforeLoad : function()
43000     {
43001         if(!this.hasFocus){
43002             return;
43003         }
43004         this.innerLists[0].update(this.loadingText ?
43005                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
43006         this.restrictHeight();
43007         this.selectedIndex = -1;
43008     },
43009     // private
43010     onLoad : function(a,b,c,d)
43011     {
43012         if (!this.loadingChildren) {
43013             // then we are loading the top level. - hide the children
43014             for (var i = 1;i < this.views.length; i++) {
43015                 this.views[i].getEl().setStyle({ display : 'none' });
43016             }
43017             var lw = Math.floor(
43018                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
43019             );
43020         
43021              this.list.setWidth(lw); // default to '1'
43022
43023             
43024         }
43025         if(!this.hasFocus){
43026             return;
43027         }
43028         
43029         if(this.store.getCount() > 0) {
43030             this.expand();
43031             this.restrictHeight();   
43032         } else {
43033             this.onEmptyResults();
43034         }
43035         
43036         if (!this.loadingChildren) {
43037             this.selectActive();
43038         }
43039         /*
43040         this.stores[1].loadData([]);
43041         this.stores[2].loadData([]);
43042         this.views
43043         */    
43044     
43045         //this.el.focus();
43046     },
43047     
43048     
43049     // private
43050     onLoadException : function()
43051     {
43052         this.collapse();
43053         Roo.log(this.store.reader.jsonData);
43054         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
43055             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
43056         }
43057         
43058         
43059     },
43060     // no cleaning of leading spaces on blur here.
43061     cleanLeadingSpace : function(e) { },
43062     
43063
43064     onSelectChange : function (view, sels, opts )
43065     {
43066         var ix = view.getSelectedIndexes();
43067          
43068         if (opts.list > this.maxColumns - 2) {
43069             if (view.store.getCount()<  1) {
43070                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
43071
43072             } else  {
43073                 if (ix.length) {
43074                     // used to clear ?? but if we are loading unselected 
43075                     this.setFromData(view.store.getAt(ix[0]).data);
43076                 }
43077                 
43078             }
43079             
43080             return;
43081         }
43082         
43083         if (!ix.length) {
43084             // this get's fired when trigger opens..
43085            // this.setFromData({});
43086             var str = this.stores[opts.list+1];
43087             str.data.clear(); // removeall wihtout the fire events..
43088             return;
43089         }
43090         
43091         var rec = view.store.getAt(ix[0]);
43092          
43093         this.setFromData(rec.data);
43094         this.fireEvent('select', this, rec, ix[0]);
43095         
43096         var lw = Math.floor(
43097              (
43098                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
43099              ) / this.maxColumns
43100         );
43101         this.loadingChildren = true;
43102         this.stores[opts.list+1].loadDataFromChildren( rec );
43103         this.loadingChildren = false;
43104         var dl = this.stores[opts.list+1]. getTotalCount();
43105         
43106         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
43107         
43108         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
43109         for (var i = opts.list+2; i < this.views.length;i++) {
43110             this.views[i].getEl().setStyle({ display : 'none' });
43111         }
43112         
43113         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
43114         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
43115         
43116         if (this.isLoading) {
43117            // this.selectActive(opts.list);
43118         }
43119          
43120     },
43121     
43122     
43123     
43124     
43125     onDoubleClick : function()
43126     {
43127         this.collapse(); //??
43128     },
43129     
43130      
43131     
43132     
43133     
43134     // private
43135     recordToStack : function(store, prop, value, stack)
43136     {
43137         var cstore = new Roo.data.SimpleStore({
43138             //fields : this.store.reader.meta.fields, // we need array reader.. for
43139             reader : this.store.reader,
43140             data : [ ]
43141         });
43142         var _this = this;
43143         var record  = false;
43144         var srec = false;
43145         if(store.getCount() < 1){
43146             return false;
43147         }
43148         store.each(function(r){
43149             if(r.data[prop] == value){
43150                 record = r;
43151             srec = r;
43152                 return false;
43153             }
43154             if (r.data.cn && r.data.cn.length) {
43155                 cstore.loadDataFromChildren( r);
43156                 var cret = _this.recordToStack(cstore, prop, value, stack);
43157                 if (cret !== false) {
43158                     record = cret;
43159                     srec = r;
43160                     return false;
43161                 }
43162             }
43163              
43164             return true;
43165         });
43166         if (record == false) {
43167             return false
43168         }
43169         stack.unshift(srec);
43170         return record;
43171     },
43172     
43173     /*
43174      * find the stack of stores that match our value.
43175      *
43176      * 
43177      */
43178     
43179     selectActive : function ()
43180     {
43181         // if store is not loaded, then we will need to wait for that to happen first.
43182         var stack = [];
43183         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
43184         for (var i = 0; i < stack.length; i++ ) {
43185             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
43186         }
43187         
43188     }
43189         
43190          
43191     
43192     
43193     
43194     
43195 });/*
43196  * Based on:
43197  * Ext JS Library 1.1.1
43198  * Copyright(c) 2006-2007, Ext JS, LLC.
43199  *
43200  * Originally Released Under LGPL - original licence link has changed is not relivant.
43201  *
43202  * Fork - LGPL
43203  * <script type="text/javascript">
43204  */
43205 /**
43206  * @class Roo.form.Checkbox
43207  * @extends Roo.form.Field
43208  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
43209  * @constructor
43210  * Creates a new Checkbox
43211  * @param {Object} config Configuration options
43212  */
43213 Roo.form.Checkbox = function(config){
43214     Roo.form.Checkbox.superclass.constructor.call(this, config);
43215     this.addEvents({
43216         /**
43217          * @event check
43218          * Fires when the checkbox is checked or unchecked.
43219              * @param {Roo.form.Checkbox} this This checkbox
43220              * @param {Boolean} checked The new checked value
43221              */
43222         check : true
43223     });
43224 };
43225
43226 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
43227     /**
43228      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43229      */
43230     focusClass : undefined,
43231     /**
43232      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43233      */
43234     fieldClass: "x-form-field",
43235     /**
43236      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
43237      */
43238     checked: false,
43239     /**
43240      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43241      * {tag: "input", type: "checkbox", autocomplete: "off"})
43242      */
43243     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
43244     /**
43245      * @cfg {String} boxLabel The text that appears beside the checkbox
43246      */
43247     boxLabel : "",
43248     /**
43249      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
43250      */  
43251     inputValue : '1',
43252     /**
43253      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
43254      */
43255      valueOff: '0', // value when not checked..
43256
43257     actionMode : 'viewEl', 
43258     //
43259     // private
43260     itemCls : 'x-menu-check-item x-form-item',
43261     groupClass : 'x-menu-group-item',
43262     inputType : 'hidden',
43263     
43264     
43265     inSetChecked: false, // check that we are not calling self...
43266     
43267     inputElement: false, // real input element?
43268     basedOn: false, // ????
43269     
43270     isFormField: true, // not sure where this is needed!!!!
43271
43272     onResize : function(){
43273         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
43274         if(!this.boxLabel){
43275             this.el.alignTo(this.wrap, 'c-c');
43276         }
43277     },
43278
43279     initEvents : function(){
43280         Roo.form.Checkbox.superclass.initEvents.call(this);
43281         this.el.on("click", this.onClick,  this);
43282         this.el.on("change", this.onClick,  this);
43283     },
43284
43285
43286     getResizeEl : function(){
43287         return this.wrap;
43288     },
43289
43290     getPositionEl : function(){
43291         return this.wrap;
43292     },
43293
43294     // private
43295     onRender : function(ct, position){
43296         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43297         /*
43298         if(this.inputValue !== undefined){
43299             this.el.dom.value = this.inputValue;
43300         }
43301         */
43302         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
43303         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
43304         var viewEl = this.wrap.createChild({ 
43305             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
43306         this.viewEl = viewEl;   
43307         this.wrap.on('click', this.onClick,  this); 
43308         
43309         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43310         this.el.on('propertychange', this.setFromHidden,  this);  //ie
43311         
43312         
43313         
43314         if(this.boxLabel){
43315             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
43316         //    viewEl.on('click', this.onClick,  this); 
43317         }
43318         //if(this.checked){
43319             this.setChecked(this.checked);
43320         //}else{
43321             //this.checked = this.el.dom;
43322         //}
43323
43324     },
43325
43326     // private
43327     initValue : Roo.emptyFn,
43328
43329     /**
43330      * Returns the checked state of the checkbox.
43331      * @return {Boolean} True if checked, else false
43332      */
43333     getValue : function(){
43334         if(this.el){
43335             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
43336         }
43337         return this.valueOff;
43338         
43339     },
43340
43341         // private
43342     onClick : function(){ 
43343         if (this.disabled) {
43344             return;
43345         }
43346         this.setChecked(!this.checked);
43347
43348         //if(this.el.dom.checked != this.checked){
43349         //    this.setValue(this.el.dom.checked);
43350        // }
43351     },
43352
43353     /**
43354      * Sets the checked state of the checkbox.
43355      * On is always based on a string comparison between inputValue and the param.
43356      * @param {Boolean/String} value - the value to set 
43357      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
43358      */
43359     setValue : function(v,suppressEvent){
43360         
43361         
43362         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
43363         //if(this.el && this.el.dom){
43364         //    this.el.dom.checked = this.checked;
43365         //    this.el.dom.defaultChecked = this.checked;
43366         //}
43367         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
43368         //this.fireEvent("check", this, this.checked);
43369     },
43370     // private..
43371     setChecked : function(state,suppressEvent)
43372     {
43373         if (this.inSetChecked) {
43374             this.checked = state;
43375             return;
43376         }
43377         
43378     
43379         if(this.wrap){
43380             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
43381         }
43382         this.checked = state;
43383         if(suppressEvent !== true){
43384             this.fireEvent('check', this, state);
43385         }
43386         this.inSetChecked = true;
43387         this.el.dom.value = state ? this.inputValue : this.valueOff;
43388         this.inSetChecked = false;
43389         
43390     },
43391     // handle setting of hidden value by some other method!!?!?
43392     setFromHidden: function()
43393     {
43394         if(!this.el){
43395             return;
43396         }
43397         //console.log("SET FROM HIDDEN");
43398         //alert('setFrom hidden');
43399         this.setValue(this.el.dom.value);
43400     },
43401     
43402     onDestroy : function()
43403     {
43404         if(this.viewEl){
43405             Roo.get(this.viewEl).remove();
43406         }
43407          
43408         Roo.form.Checkbox.superclass.onDestroy.call(this);
43409     },
43410     
43411     setBoxLabel : function(str)
43412     {
43413         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
43414     }
43415
43416 });/*
43417  * Based on:
43418  * Ext JS Library 1.1.1
43419  * Copyright(c) 2006-2007, Ext JS, LLC.
43420  *
43421  * Originally Released Under LGPL - original licence link has changed is not relivant.
43422  *
43423  * Fork - LGPL
43424  * <script type="text/javascript">
43425  */
43426  
43427 /**
43428  * @class Roo.form.Radio
43429  * @extends Roo.form.Checkbox
43430  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
43431  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
43432  * @constructor
43433  * Creates a new Radio
43434  * @param {Object} config Configuration options
43435  */
43436 Roo.form.Radio = function(){
43437     Roo.form.Radio.superclass.constructor.apply(this, arguments);
43438 };
43439 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
43440     inputType: 'radio',
43441
43442     /**
43443      * If this radio is part of a group, it will return the selected value
43444      * @return {String}
43445      */
43446     getGroupValue : function(){
43447         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
43448     },
43449     
43450     
43451     onRender : function(ct, position){
43452         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43453         
43454         if(this.inputValue !== undefined){
43455             this.el.dom.value = this.inputValue;
43456         }
43457          
43458         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
43459         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
43460         //var viewEl = this.wrap.createChild({ 
43461         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
43462         //this.viewEl = viewEl;   
43463         //this.wrap.on('click', this.onClick,  this); 
43464         
43465         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43466         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
43467         
43468         
43469         
43470         if(this.boxLabel){
43471             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
43472         //    viewEl.on('click', this.onClick,  this); 
43473         }
43474          if(this.checked){
43475             this.el.dom.checked =   'checked' ;
43476         }
43477          
43478     } 
43479     
43480     
43481 });//<script type="text/javascript">
43482
43483 /*
43484  * Based  Ext JS Library 1.1.1
43485  * Copyright(c) 2006-2007, Ext JS, LLC.
43486  * LGPL
43487  *
43488  */
43489  
43490 /**
43491  * @class Roo.HtmlEditorCore
43492  * @extends Roo.Component
43493  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
43494  *
43495  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
43496  */
43497
43498 Roo.HtmlEditorCore = function(config){
43499     
43500     
43501     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
43502     
43503     
43504     this.addEvents({
43505         /**
43506          * @event initialize
43507          * Fires when the editor is fully initialized (including the iframe)
43508          * @param {Roo.HtmlEditorCore} this
43509          */
43510         initialize: true,
43511         /**
43512          * @event activate
43513          * Fires when the editor is first receives the focus. Any insertion must wait
43514          * until after this event.
43515          * @param {Roo.HtmlEditorCore} this
43516          */
43517         activate: true,
43518          /**
43519          * @event beforesync
43520          * Fires before the textarea is updated with content from the editor iframe. Return false
43521          * to cancel the sync.
43522          * @param {Roo.HtmlEditorCore} this
43523          * @param {String} html
43524          */
43525         beforesync: true,
43526          /**
43527          * @event beforepush
43528          * Fires before the iframe editor is updated with content from the textarea. Return false
43529          * to cancel the push.
43530          * @param {Roo.HtmlEditorCore} this
43531          * @param {String} html
43532          */
43533         beforepush: true,
43534          /**
43535          * @event sync
43536          * Fires when the textarea is updated with content from the editor iframe.
43537          * @param {Roo.HtmlEditorCore} this
43538          * @param {String} html
43539          */
43540         sync: true,
43541          /**
43542          * @event push
43543          * Fires when the iframe editor is updated with content from the textarea.
43544          * @param {Roo.HtmlEditorCore} this
43545          * @param {String} html
43546          */
43547         push: true,
43548         
43549         /**
43550          * @event editorevent
43551          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
43552          * @param {Roo.HtmlEditorCore} this
43553          */
43554         editorevent: true
43555         
43556     });
43557     
43558     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
43559     
43560     // defaults : white / black...
43561     this.applyBlacklists();
43562     
43563     
43564     
43565 };
43566
43567
43568 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
43569
43570
43571      /**
43572      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
43573      */
43574     
43575     owner : false,
43576     
43577      /**
43578      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
43579      *                        Roo.resizable.
43580      */
43581     resizable : false,
43582      /**
43583      * @cfg {Number} height (in pixels)
43584      */   
43585     height: 300,
43586    /**
43587      * @cfg {Number} width (in pixels)
43588      */   
43589     width: 500,
43590     
43591     /**
43592      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
43593      * 
43594      */
43595     stylesheets: false,
43596     
43597     // id of frame..
43598     frameId: false,
43599     
43600     // private properties
43601     validationEvent : false,
43602     deferHeight: true,
43603     initialized : false,
43604     activated : false,
43605     sourceEditMode : false,
43606     onFocus : Roo.emptyFn,
43607     iframePad:3,
43608     hideMode:'offsets',
43609     
43610     clearUp: true,
43611     
43612     // blacklist + whitelisted elements..
43613     black: false,
43614     white: false,
43615      
43616     bodyCls : '',
43617
43618     /**
43619      * Protected method that will not generally be called directly. It
43620      * is called when the editor initializes the iframe with HTML contents. Override this method if you
43621      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
43622      */
43623     getDocMarkup : function(){
43624         // body styles..
43625         var st = '';
43626         
43627         // inherit styels from page...?? 
43628         if (this.stylesheets === false) {
43629             
43630             Roo.get(document.head).select('style').each(function(node) {
43631                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
43632             });
43633             
43634             Roo.get(document.head).select('link').each(function(node) { 
43635                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
43636             });
43637             
43638         } else if (!this.stylesheets.length) {
43639                 // simple..
43640                 st = '<style type="text/css">' +
43641                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
43642                    '</style>';
43643         } else {
43644             for (var i in this.stylesheets) { 
43645                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
43646             }
43647             
43648         }
43649         
43650         st +=  '<style type="text/css">' +
43651             'IMG { cursor: pointer } ' +
43652         '</style>';
43653
43654         var cls = 'roo-htmleditor-body';
43655         
43656         if(this.bodyCls.length){
43657             cls += ' ' + this.bodyCls;
43658         }
43659         
43660         return '<html><head>' + st  +
43661             //<style type="text/css">' +
43662             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
43663             //'</style>' +
43664             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
43665     },
43666
43667     // private
43668     onRender : function(ct, position)
43669     {
43670         var _t = this;
43671         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
43672         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
43673         
43674         
43675         this.el.dom.style.border = '0 none';
43676         this.el.dom.setAttribute('tabIndex', -1);
43677         this.el.addClass('x-hidden hide');
43678         
43679         
43680         
43681         if(Roo.isIE){ // fix IE 1px bogus margin
43682             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
43683         }
43684        
43685         
43686         this.frameId = Roo.id();
43687         
43688          
43689         
43690         var iframe = this.owner.wrap.createChild({
43691             tag: 'iframe',
43692             cls: 'form-control', // bootstrap..
43693             id: this.frameId,
43694             name: this.frameId,
43695             frameBorder : 'no',
43696             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
43697         }, this.el
43698         );
43699         
43700         
43701         this.iframe = iframe.dom;
43702
43703          this.assignDocWin();
43704         
43705         this.doc.designMode = 'on';
43706        
43707         this.doc.open();
43708         this.doc.write(this.getDocMarkup());
43709         this.doc.close();
43710
43711         
43712         var task = { // must defer to wait for browser to be ready
43713             run : function(){
43714                 //console.log("run task?" + this.doc.readyState);
43715                 this.assignDocWin();
43716                 if(this.doc.body || this.doc.readyState == 'complete'){
43717                     try {
43718                         this.doc.designMode="on";
43719                     } catch (e) {
43720                         return;
43721                     }
43722                     Roo.TaskMgr.stop(task);
43723                     this.initEditor.defer(10, this);
43724                 }
43725             },
43726             interval : 10,
43727             duration: 10000,
43728             scope: this
43729         };
43730         Roo.TaskMgr.start(task);
43731
43732     },
43733
43734     // private
43735     onResize : function(w, h)
43736     {
43737          Roo.log('resize: ' +w + ',' + h );
43738         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
43739         if(!this.iframe){
43740             return;
43741         }
43742         if(typeof w == 'number'){
43743             
43744             this.iframe.style.width = w + 'px';
43745         }
43746         if(typeof h == 'number'){
43747             
43748             this.iframe.style.height = h + 'px';
43749             if(this.doc){
43750                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
43751             }
43752         }
43753         
43754     },
43755
43756     /**
43757      * Toggles the editor between standard and source edit mode.
43758      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
43759      */
43760     toggleSourceEdit : function(sourceEditMode){
43761         
43762         this.sourceEditMode = sourceEditMode === true;
43763         
43764         if(this.sourceEditMode){
43765  
43766             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
43767             
43768         }else{
43769             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
43770             //this.iframe.className = '';
43771             this.deferFocus();
43772         }
43773         //this.setSize(this.owner.wrap.getSize());
43774         //this.fireEvent('editmodechange', this, this.sourceEditMode);
43775     },
43776
43777     
43778   
43779
43780     /**
43781      * Protected method that will not generally be called directly. If you need/want
43782      * custom HTML cleanup, this is the method you should override.
43783      * @param {String} html The HTML to be cleaned
43784      * return {String} The cleaned HTML
43785      */
43786     cleanHtml : function(html){
43787         html = String(html);
43788         if(html.length > 5){
43789             if(Roo.isSafari){ // strip safari nonsense
43790                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
43791             }
43792         }
43793         if(html == '&nbsp;'){
43794             html = '';
43795         }
43796         return html;
43797     },
43798
43799     /**
43800      * HTML Editor -> Textarea
43801      * Protected method that will not generally be called directly. Syncs the contents
43802      * of the editor iframe with the textarea.
43803      */
43804     syncValue : function(){
43805         if(this.initialized){
43806             var bd = (this.doc.body || this.doc.documentElement);
43807             //this.cleanUpPaste(); -- this is done else where and causes havoc..
43808             var html = bd.innerHTML;
43809             if(Roo.isSafari){
43810                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
43811                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
43812                 if(m && m[1]){
43813                     html = '<div style="'+m[0]+'">' + html + '</div>';
43814                 }
43815             }
43816             html = this.cleanHtml(html);
43817             // fix up the special chars.. normaly like back quotes in word...
43818             // however we do not want to do this with chinese..
43819             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
43820                 
43821                 var cc = match.charCodeAt();
43822
43823                 // Get the character value, handling surrogate pairs
43824                 if (match.length == 2) {
43825                     // It's a surrogate pair, calculate the Unicode code point
43826                     var high = match.charCodeAt(0) - 0xD800;
43827                     var low  = match.charCodeAt(1) - 0xDC00;
43828                     cc = (high * 0x400) + low + 0x10000;
43829                 }  else if (
43830                     (cc >= 0x4E00 && cc < 0xA000 ) ||
43831                     (cc >= 0x3400 && cc < 0x4E00 ) ||
43832                     (cc >= 0xf900 && cc < 0xfb00 )
43833                 ) {
43834                         return match;
43835                 }  
43836          
43837                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
43838                 return "&#" + cc + ";";
43839                 
43840                 
43841             });
43842             
43843             
43844              
43845             if(this.owner.fireEvent('beforesync', this, html) !== false){
43846                 this.el.dom.value = html;
43847                 this.owner.fireEvent('sync', this, html);
43848             }
43849         }
43850     },
43851
43852     /**
43853      * Protected method that will not generally be called directly. Pushes the value of the textarea
43854      * into the iframe editor.
43855      */
43856     pushValue : function(){
43857         if(this.initialized){
43858             var v = this.el.dom.value.trim();
43859             
43860 //            if(v.length < 1){
43861 //                v = '&#160;';
43862 //            }
43863             
43864             if(this.owner.fireEvent('beforepush', this, v) !== false){
43865                 var d = (this.doc.body || this.doc.documentElement);
43866                 d.innerHTML = v;
43867                 this.cleanUpPaste();
43868                 this.el.dom.value = d.innerHTML;
43869                 this.owner.fireEvent('push', this, v);
43870             }
43871         }
43872     },
43873
43874     // private
43875     deferFocus : function(){
43876         this.focus.defer(10, this);
43877     },
43878
43879     // doc'ed in Field
43880     focus : function(){
43881         if(this.win && !this.sourceEditMode){
43882             this.win.focus();
43883         }else{
43884             this.el.focus();
43885         }
43886     },
43887     
43888     assignDocWin: function()
43889     {
43890         var iframe = this.iframe;
43891         
43892          if(Roo.isIE){
43893             this.doc = iframe.contentWindow.document;
43894             this.win = iframe.contentWindow;
43895         } else {
43896 //            if (!Roo.get(this.frameId)) {
43897 //                return;
43898 //            }
43899 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43900 //            this.win = Roo.get(this.frameId).dom.contentWindow;
43901             
43902             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
43903                 return;
43904             }
43905             
43906             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43907             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
43908         }
43909     },
43910     
43911     // private
43912     initEditor : function(){
43913         //console.log("INIT EDITOR");
43914         this.assignDocWin();
43915         
43916         
43917         
43918         this.doc.designMode="on";
43919         this.doc.open();
43920         this.doc.write(this.getDocMarkup());
43921         this.doc.close();
43922         
43923         var dbody = (this.doc.body || this.doc.documentElement);
43924         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
43925         // this copies styles from the containing element into thsi one..
43926         // not sure why we need all of this..
43927         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
43928         
43929         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
43930         //ss['background-attachment'] = 'fixed'; // w3c
43931         dbody.bgProperties = 'fixed'; // ie
43932         //Roo.DomHelper.applyStyles(dbody, ss);
43933         Roo.EventManager.on(this.doc, {
43934             //'mousedown': this.onEditorEvent,
43935             'mouseup': this.onEditorEvent,
43936             'dblclick': this.onEditorEvent,
43937             'click': this.onEditorEvent,
43938             'keyup': this.onEditorEvent,
43939             buffer:100,
43940             scope: this
43941         });
43942         if(Roo.isGecko){
43943             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
43944         }
43945         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
43946             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
43947         }
43948         this.initialized = true;
43949
43950         this.owner.fireEvent('initialize', this);
43951         this.pushValue();
43952     },
43953
43954     // private
43955     onDestroy : function(){
43956         
43957         
43958         
43959         if(this.rendered){
43960             
43961             //for (var i =0; i < this.toolbars.length;i++) {
43962             //    // fixme - ask toolbars for heights?
43963             //    this.toolbars[i].onDestroy();
43964            // }
43965             
43966             //this.wrap.dom.innerHTML = '';
43967             //this.wrap.remove();
43968         }
43969     },
43970
43971     // private
43972     onFirstFocus : function(){
43973         
43974         this.assignDocWin();
43975         
43976         
43977         this.activated = true;
43978          
43979     
43980         if(Roo.isGecko){ // prevent silly gecko errors
43981             this.win.focus();
43982             var s = this.win.getSelection();
43983             if(!s.focusNode || s.focusNode.nodeType != 3){
43984                 var r = s.getRangeAt(0);
43985                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
43986                 r.collapse(true);
43987                 this.deferFocus();
43988             }
43989             try{
43990                 this.execCmd('useCSS', true);
43991                 this.execCmd('styleWithCSS', false);
43992             }catch(e){}
43993         }
43994         this.owner.fireEvent('activate', this);
43995     },
43996
43997     // private
43998     adjustFont: function(btn){
43999         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
44000         //if(Roo.isSafari){ // safari
44001         //    adjust *= 2;
44002        // }
44003         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
44004         if(Roo.isSafari){ // safari
44005             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
44006             v =  (v < 10) ? 10 : v;
44007             v =  (v > 48) ? 48 : v;
44008             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
44009             
44010         }
44011         
44012         
44013         v = Math.max(1, v+adjust);
44014         
44015         this.execCmd('FontSize', v  );
44016     },
44017
44018     onEditorEvent : function(e)
44019     {
44020         this.owner.fireEvent('editorevent', this, e);
44021       //  this.updateToolbar();
44022         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
44023     },
44024
44025     insertTag : function(tg)
44026     {
44027         // could be a bit smarter... -> wrap the current selected tRoo..
44028         if (tg.toLowerCase() == 'span' ||
44029             tg.toLowerCase() == 'code' ||
44030             tg.toLowerCase() == 'sup' ||
44031             tg.toLowerCase() == 'sub' 
44032             ) {
44033             
44034             range = this.createRange(this.getSelection());
44035             var wrappingNode = this.doc.createElement(tg.toLowerCase());
44036             wrappingNode.appendChild(range.extractContents());
44037             range.insertNode(wrappingNode);
44038
44039             return;
44040             
44041             
44042             
44043         }
44044         this.execCmd("formatblock",   tg);
44045         
44046     },
44047     
44048     insertText : function(txt)
44049     {
44050         
44051         
44052         var range = this.createRange();
44053         range.deleteContents();
44054                //alert(Sender.getAttribute('label'));
44055                
44056         range.insertNode(this.doc.createTextNode(txt));
44057     } ,
44058     
44059      
44060
44061     /**
44062      * Executes a Midas editor command on the editor document and performs necessary focus and
44063      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
44064      * @param {String} cmd The Midas command
44065      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
44066      */
44067     relayCmd : function(cmd, value){
44068         this.win.focus();
44069         this.execCmd(cmd, value);
44070         this.owner.fireEvent('editorevent', this);
44071         //this.updateToolbar();
44072         this.owner.deferFocus();
44073     },
44074
44075     /**
44076      * Executes a Midas editor command directly on the editor document.
44077      * For visual commands, you should use {@link #relayCmd} instead.
44078      * <b>This should only be called after the editor is initialized.</b>
44079      * @param {String} cmd The Midas command
44080      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
44081      */
44082     execCmd : function(cmd, value){
44083         this.doc.execCommand(cmd, false, value === undefined ? null : value);
44084         this.syncValue();
44085     },
44086  
44087  
44088    
44089     /**
44090      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
44091      * to insert tRoo.
44092      * @param {String} text | dom node.. 
44093      */
44094     insertAtCursor : function(text)
44095     {
44096         
44097         if(!this.activated){
44098             return;
44099         }
44100         /*
44101         if(Roo.isIE){
44102             this.win.focus();
44103             var r = this.doc.selection.createRange();
44104             if(r){
44105                 r.collapse(true);
44106                 r.pasteHTML(text);
44107                 this.syncValue();
44108                 this.deferFocus();
44109             
44110             }
44111             return;
44112         }
44113         */
44114         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
44115             this.win.focus();
44116             
44117             
44118             // from jquery ui (MIT licenced)
44119             var range, node;
44120             var win = this.win;
44121             
44122             if (win.getSelection && win.getSelection().getRangeAt) {
44123                 range = win.getSelection().getRangeAt(0);
44124                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
44125                 range.insertNode(node);
44126             } else if (win.document.selection && win.document.selection.createRange) {
44127                 // no firefox support
44128                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
44129                 win.document.selection.createRange().pasteHTML(txt);
44130             } else {
44131                 // no firefox support
44132                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
44133                 this.execCmd('InsertHTML', txt);
44134             } 
44135             
44136             this.syncValue();
44137             
44138             this.deferFocus();
44139         }
44140     },
44141  // private
44142     mozKeyPress : function(e){
44143         if(e.ctrlKey){
44144             var c = e.getCharCode(), cmd;
44145           
44146             if(c > 0){
44147                 c = String.fromCharCode(c).toLowerCase();
44148                 switch(c){
44149                     case 'b':
44150                         cmd = 'bold';
44151                         break;
44152                     case 'i':
44153                         cmd = 'italic';
44154                         break;
44155                     
44156                     case 'u':
44157                         cmd = 'underline';
44158                         break;
44159                     
44160                     case 'v':
44161                         this.cleanUpPaste.defer(100, this);
44162                         return;
44163                         
44164                 }
44165                 if(cmd){
44166                     this.win.focus();
44167                     this.execCmd(cmd);
44168                     this.deferFocus();
44169                     e.preventDefault();
44170                 }
44171                 
44172             }
44173         }
44174     },
44175
44176     // private
44177     fixKeys : function(){ // load time branching for fastest keydown performance
44178         if(Roo.isIE){
44179             return function(e){
44180                 var k = e.getKey(), r;
44181                 if(k == e.TAB){
44182                     e.stopEvent();
44183                     r = this.doc.selection.createRange();
44184                     if(r){
44185                         r.collapse(true);
44186                         r.pasteHTML('&#160;&#160;&#160;&#160;');
44187                         this.deferFocus();
44188                     }
44189                     return;
44190                 }
44191                 
44192                 if(k == e.ENTER){
44193                     r = this.doc.selection.createRange();
44194                     if(r){
44195                         var target = r.parentElement();
44196                         if(!target || target.tagName.toLowerCase() != 'li'){
44197                             e.stopEvent();
44198                             r.pasteHTML('<br />');
44199                             r.collapse(false);
44200                             r.select();
44201                         }
44202                     }
44203                 }
44204                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44205                     this.cleanUpPaste.defer(100, this);
44206                     return;
44207                 }
44208                 
44209                 
44210             };
44211         }else if(Roo.isOpera){
44212             return function(e){
44213                 var k = e.getKey();
44214                 if(k == e.TAB){
44215                     e.stopEvent();
44216                     this.win.focus();
44217                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
44218                     this.deferFocus();
44219                 }
44220                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44221                     this.cleanUpPaste.defer(100, this);
44222                     return;
44223                 }
44224                 
44225             };
44226         }else if(Roo.isSafari){
44227             return function(e){
44228                 var k = e.getKey();
44229                 
44230                 if(k == e.TAB){
44231                     e.stopEvent();
44232                     this.execCmd('InsertText','\t');
44233                     this.deferFocus();
44234                     return;
44235                 }
44236                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44237                     this.cleanUpPaste.defer(100, this);
44238                     return;
44239                 }
44240                 
44241              };
44242         }
44243     }(),
44244     
44245     getAllAncestors: function()
44246     {
44247         var p = this.getSelectedNode();
44248         var a = [];
44249         if (!p) {
44250             a.push(p); // push blank onto stack..
44251             p = this.getParentElement();
44252         }
44253         
44254         
44255         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
44256             a.push(p);
44257             p = p.parentNode;
44258         }
44259         a.push(this.doc.body);
44260         return a;
44261     },
44262     lastSel : false,
44263     lastSelNode : false,
44264     
44265     
44266     getSelection : function() 
44267     {
44268         this.assignDocWin();
44269         return Roo.isIE ? this.doc.selection : this.win.getSelection();
44270     },
44271     
44272     getSelectedNode: function() 
44273     {
44274         // this may only work on Gecko!!!
44275         
44276         // should we cache this!!!!
44277         
44278         
44279         
44280          
44281         var range = this.createRange(this.getSelection()).cloneRange();
44282         
44283         if (Roo.isIE) {
44284             var parent = range.parentElement();
44285             while (true) {
44286                 var testRange = range.duplicate();
44287                 testRange.moveToElementText(parent);
44288                 if (testRange.inRange(range)) {
44289                     break;
44290                 }
44291                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
44292                     break;
44293                 }
44294                 parent = parent.parentElement;
44295             }
44296             return parent;
44297         }
44298         
44299         // is ancestor a text element.
44300         var ac =  range.commonAncestorContainer;
44301         if (ac.nodeType == 3) {
44302             ac = ac.parentNode;
44303         }
44304         
44305         var ar = ac.childNodes;
44306          
44307         var nodes = [];
44308         var other_nodes = [];
44309         var has_other_nodes = false;
44310         for (var i=0;i<ar.length;i++) {
44311             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
44312                 continue;
44313             }
44314             // fullly contained node.
44315             
44316             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
44317                 nodes.push(ar[i]);
44318                 continue;
44319             }
44320             
44321             // probably selected..
44322             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
44323                 other_nodes.push(ar[i]);
44324                 continue;
44325             }
44326             // outer..
44327             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
44328                 continue;
44329             }
44330             
44331             
44332             has_other_nodes = true;
44333         }
44334         if (!nodes.length && other_nodes.length) {
44335             nodes= other_nodes;
44336         }
44337         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
44338             return false;
44339         }
44340         
44341         return nodes[0];
44342     },
44343     createRange: function(sel)
44344     {
44345         // this has strange effects when using with 
44346         // top toolbar - not sure if it's a great idea.
44347         //this.editor.contentWindow.focus();
44348         if (typeof sel != "undefined") {
44349             try {
44350                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
44351             } catch(e) {
44352                 return this.doc.createRange();
44353             }
44354         } else {
44355             return this.doc.createRange();
44356         }
44357     },
44358     getParentElement: function()
44359     {
44360         
44361         this.assignDocWin();
44362         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
44363         
44364         var range = this.createRange(sel);
44365          
44366         try {
44367             var p = range.commonAncestorContainer;
44368             while (p.nodeType == 3) { // text node
44369                 p = p.parentNode;
44370             }
44371             return p;
44372         } catch (e) {
44373             return null;
44374         }
44375     
44376     },
44377     /***
44378      *
44379      * Range intersection.. the hard stuff...
44380      *  '-1' = before
44381      *  '0' = hits..
44382      *  '1' = after.
44383      *         [ -- selected range --- ]
44384      *   [fail]                        [fail]
44385      *
44386      *    basically..
44387      *      if end is before start or  hits it. fail.
44388      *      if start is after end or hits it fail.
44389      *
44390      *   if either hits (but other is outside. - then it's not 
44391      *   
44392      *    
44393      **/
44394     
44395     
44396     // @see http://www.thismuchiknow.co.uk/?p=64.
44397     rangeIntersectsNode : function(range, node)
44398     {
44399         var nodeRange = node.ownerDocument.createRange();
44400         try {
44401             nodeRange.selectNode(node);
44402         } catch (e) {
44403             nodeRange.selectNodeContents(node);
44404         }
44405     
44406         var rangeStartRange = range.cloneRange();
44407         rangeStartRange.collapse(true);
44408     
44409         var rangeEndRange = range.cloneRange();
44410         rangeEndRange.collapse(false);
44411     
44412         var nodeStartRange = nodeRange.cloneRange();
44413         nodeStartRange.collapse(true);
44414     
44415         var nodeEndRange = nodeRange.cloneRange();
44416         nodeEndRange.collapse(false);
44417     
44418         return rangeStartRange.compareBoundaryPoints(
44419                  Range.START_TO_START, nodeEndRange) == -1 &&
44420                rangeEndRange.compareBoundaryPoints(
44421                  Range.START_TO_START, nodeStartRange) == 1;
44422         
44423          
44424     },
44425     rangeCompareNode : function(range, node)
44426     {
44427         var nodeRange = node.ownerDocument.createRange();
44428         try {
44429             nodeRange.selectNode(node);
44430         } catch (e) {
44431             nodeRange.selectNodeContents(node);
44432         }
44433         
44434         
44435         range.collapse(true);
44436     
44437         nodeRange.collapse(true);
44438      
44439         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
44440         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
44441          
44442         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
44443         
44444         var nodeIsBefore   =  ss == 1;
44445         var nodeIsAfter    = ee == -1;
44446         
44447         if (nodeIsBefore && nodeIsAfter) {
44448             return 0; // outer
44449         }
44450         if (!nodeIsBefore && nodeIsAfter) {
44451             return 1; //right trailed.
44452         }
44453         
44454         if (nodeIsBefore && !nodeIsAfter) {
44455             return 2;  // left trailed.
44456         }
44457         // fully contined.
44458         return 3;
44459     },
44460
44461     // private? - in a new class?
44462     cleanUpPaste :  function()
44463     {
44464         // cleans up the whole document..
44465         Roo.log('cleanuppaste');
44466         
44467         this.cleanUpChildren(this.doc.body);
44468         var clean = this.cleanWordChars(this.doc.body.innerHTML);
44469         if (clean != this.doc.body.innerHTML) {
44470             this.doc.body.innerHTML = clean;
44471         }
44472         
44473     },
44474     
44475     cleanWordChars : function(input) {// change the chars to hex code
44476         var he = Roo.HtmlEditorCore;
44477         
44478         var output = input;
44479         Roo.each(he.swapCodes, function(sw) { 
44480             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
44481             
44482             output = output.replace(swapper, sw[1]);
44483         });
44484         
44485         return output;
44486     },
44487     
44488     
44489     cleanUpChildren : function (n)
44490     {
44491         if (!n.childNodes.length) {
44492             return;
44493         }
44494         for (var i = n.childNodes.length-1; i > -1 ; i--) {
44495            this.cleanUpChild(n.childNodes[i]);
44496         }
44497     },
44498     
44499     
44500         
44501     
44502     cleanUpChild : function (node)
44503     {
44504         var ed = this;
44505         //console.log(node);
44506         if (node.nodeName == "#text") {
44507             // clean up silly Windows -- stuff?
44508             return; 
44509         }
44510         if (node.nodeName == "#comment") {
44511             node.parentNode.removeChild(node);
44512             // clean up silly Windows -- stuff?
44513             return; 
44514         }
44515         var lcname = node.tagName.toLowerCase();
44516         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
44517         // whitelist of tags..
44518         
44519         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
44520             // remove node.
44521             node.parentNode.removeChild(node);
44522             return;
44523             
44524         }
44525         
44526         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
44527         
44528         // spans with no attributes - just remove them..
44529         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
44530             remove_keep_children = true;
44531         }
44532         
44533         // remove <a name=....> as rendering on yahoo mailer is borked with this.
44534         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
44535         
44536         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
44537         //    remove_keep_children = true;
44538         //}
44539         
44540         if (remove_keep_children) {
44541             this.cleanUpChildren(node);
44542             // inserts everything just before this node...
44543             while (node.childNodes.length) {
44544                 var cn = node.childNodes[0];
44545                 node.removeChild(cn);
44546                 node.parentNode.insertBefore(cn, node);
44547             }
44548             node.parentNode.removeChild(node);
44549             return;
44550         }
44551         
44552         if (!node.attributes || !node.attributes.length) {
44553             
44554           
44555             
44556             
44557             this.cleanUpChildren(node);
44558             return;
44559         }
44560         
44561         function cleanAttr(n,v)
44562         {
44563             
44564             if (v.match(/^\./) || v.match(/^\//)) {
44565                 return;
44566             }
44567             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
44568                 return;
44569             }
44570             if (v.match(/^#/)) {
44571                 return;
44572             }
44573             if (v.match(/^\{/)) { // allow template editing.
44574                 return;
44575             }
44576 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
44577             node.removeAttribute(n);
44578             
44579         }
44580         
44581         var cwhite = this.cwhite;
44582         var cblack = this.cblack;
44583             
44584         function cleanStyle(n,v)
44585         {
44586             if (v.match(/expression/)) { //XSS?? should we even bother..
44587                 node.removeAttribute(n);
44588                 return;
44589             }
44590             
44591             var parts = v.split(/;/);
44592             var clean = [];
44593             
44594             Roo.each(parts, function(p) {
44595                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
44596                 if (!p.length) {
44597                     return true;
44598                 }
44599                 var l = p.split(':').shift().replace(/\s+/g,'');
44600                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
44601                 
44602                 if ( cwhite.length && cblack.indexOf(l) > -1) {
44603 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
44604                     //node.removeAttribute(n);
44605                     return true;
44606                 }
44607                 //Roo.log()
44608                 // only allow 'c whitelisted system attributes'
44609                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
44610 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
44611                     //node.removeAttribute(n);
44612                     return true;
44613                 }
44614                 
44615                 
44616                  
44617                 
44618                 clean.push(p);
44619                 return true;
44620             });
44621             if (clean.length) { 
44622                 node.setAttribute(n, clean.join(';'));
44623             } else {
44624                 node.removeAttribute(n);
44625             }
44626             
44627         }
44628         
44629         
44630         for (var i = node.attributes.length-1; i > -1 ; i--) {
44631             var a = node.attributes[i];
44632             //console.log(a);
44633             
44634             if (a.name.toLowerCase().substr(0,2)=='on')  {
44635                 node.removeAttribute(a.name);
44636                 continue;
44637             }
44638             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
44639                 node.removeAttribute(a.name);
44640                 continue;
44641             }
44642             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
44643                 cleanAttr(a.name,a.value); // fixme..
44644                 continue;
44645             }
44646             if (a.name == 'style') {
44647                 cleanStyle(a.name,a.value);
44648                 continue;
44649             }
44650             /// clean up MS crap..
44651             // tecnically this should be a list of valid class'es..
44652             
44653             
44654             if (a.name == 'class') {
44655                 if (a.value.match(/^Mso/)) {
44656                     node.removeAttribute('class');
44657                 }
44658                 
44659                 if (a.value.match(/^body$/)) {
44660                     node.removeAttribute('class');
44661                 }
44662                 continue;
44663             }
44664             
44665             // style cleanup!?
44666             // class cleanup?
44667             
44668         }
44669         
44670         
44671         this.cleanUpChildren(node);
44672         
44673         
44674     },
44675     
44676     /**
44677      * Clean up MS wordisms...
44678      */
44679     cleanWord : function(node)
44680     {
44681         if (!node) {
44682             this.cleanWord(this.doc.body);
44683             return;
44684         }
44685         
44686         if(
44687                 node.nodeName == 'SPAN' &&
44688                 !node.hasAttributes() &&
44689                 node.childNodes.length == 1 &&
44690                 node.firstChild.nodeName == "#text"  
44691         ) {
44692             var textNode = node.firstChild;
44693             node.removeChild(textNode);
44694             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44695                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
44696             }
44697             node.parentNode.insertBefore(textNode, node);
44698             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44699                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
44700             }
44701             node.parentNode.removeChild(node);
44702         }
44703         
44704         if (node.nodeName == "#text") {
44705             // clean up silly Windows -- stuff?
44706             return; 
44707         }
44708         if (node.nodeName == "#comment") {
44709             node.parentNode.removeChild(node);
44710             // clean up silly Windows -- stuff?
44711             return; 
44712         }
44713         
44714         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
44715             node.parentNode.removeChild(node);
44716             return;
44717         }
44718         //Roo.log(node.tagName);
44719         // remove - but keep children..
44720         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
44721             //Roo.log('-- removed');
44722             while (node.childNodes.length) {
44723                 var cn = node.childNodes[0];
44724                 node.removeChild(cn);
44725                 node.parentNode.insertBefore(cn, node);
44726                 // move node to parent - and clean it..
44727                 this.cleanWord(cn);
44728             }
44729             node.parentNode.removeChild(node);
44730             /// no need to iterate chidlren = it's got none..
44731             //this.iterateChildren(node, this.cleanWord);
44732             return;
44733         }
44734         // clean styles
44735         if (node.className.length) {
44736             
44737             var cn = node.className.split(/\W+/);
44738             var cna = [];
44739             Roo.each(cn, function(cls) {
44740                 if (cls.match(/Mso[a-zA-Z]+/)) {
44741                     return;
44742                 }
44743                 cna.push(cls);
44744             });
44745             node.className = cna.length ? cna.join(' ') : '';
44746             if (!cna.length) {
44747                 node.removeAttribute("class");
44748             }
44749         }
44750         
44751         if (node.hasAttribute("lang")) {
44752             node.removeAttribute("lang");
44753         }
44754         
44755         if (node.hasAttribute("style")) {
44756             
44757             var styles = node.getAttribute("style").split(";");
44758             var nstyle = [];
44759             Roo.each(styles, function(s) {
44760                 if (!s.match(/:/)) {
44761                     return;
44762                 }
44763                 var kv = s.split(":");
44764                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
44765                     return;
44766                 }
44767                 // what ever is left... we allow.
44768                 nstyle.push(s);
44769             });
44770             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44771             if (!nstyle.length) {
44772                 node.removeAttribute('style');
44773             }
44774         }
44775         this.iterateChildren(node, this.cleanWord);
44776         
44777         
44778         
44779     },
44780     /**
44781      * iterateChildren of a Node, calling fn each time, using this as the scole..
44782      * @param {DomNode} node node to iterate children of.
44783      * @param {Function} fn method of this class to call on each item.
44784      */
44785     iterateChildren : function(node, fn)
44786     {
44787         if (!node.childNodes.length) {
44788                 return;
44789         }
44790         for (var i = node.childNodes.length-1; i > -1 ; i--) {
44791            fn.call(this, node.childNodes[i])
44792         }
44793     },
44794     
44795     
44796     /**
44797      * cleanTableWidths.
44798      *
44799      * Quite often pasting from word etc.. results in tables with column and widths.
44800      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
44801      *
44802      */
44803     cleanTableWidths : function(node)
44804     {
44805          
44806          
44807         if (!node) {
44808             this.cleanTableWidths(this.doc.body);
44809             return;
44810         }
44811         
44812         // ignore list...
44813         if (node.nodeName == "#text" || node.nodeName == "#comment") {
44814             return; 
44815         }
44816         Roo.log(node.tagName);
44817         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
44818             this.iterateChildren(node, this.cleanTableWidths);
44819             return;
44820         }
44821         if (node.hasAttribute('width')) {
44822             node.removeAttribute('width');
44823         }
44824         
44825          
44826         if (node.hasAttribute("style")) {
44827             // pretty basic...
44828             
44829             var styles = node.getAttribute("style").split(";");
44830             var nstyle = [];
44831             Roo.each(styles, function(s) {
44832                 if (!s.match(/:/)) {
44833                     return;
44834                 }
44835                 var kv = s.split(":");
44836                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
44837                     return;
44838                 }
44839                 // what ever is left... we allow.
44840                 nstyle.push(s);
44841             });
44842             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44843             if (!nstyle.length) {
44844                 node.removeAttribute('style');
44845             }
44846         }
44847         
44848         this.iterateChildren(node, this.cleanTableWidths);
44849         
44850         
44851     },
44852     
44853     
44854     
44855     
44856     domToHTML : function(currentElement, depth, nopadtext) {
44857         
44858         depth = depth || 0;
44859         nopadtext = nopadtext || false;
44860     
44861         if (!currentElement) {
44862             return this.domToHTML(this.doc.body);
44863         }
44864         
44865         //Roo.log(currentElement);
44866         var j;
44867         var allText = false;
44868         var nodeName = currentElement.nodeName;
44869         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
44870         
44871         if  (nodeName == '#text') {
44872             
44873             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
44874         }
44875         
44876         
44877         var ret = '';
44878         if (nodeName != 'BODY') {
44879              
44880             var i = 0;
44881             // Prints the node tagName, such as <A>, <IMG>, etc
44882             if (tagName) {
44883                 var attr = [];
44884                 for(i = 0; i < currentElement.attributes.length;i++) {
44885                     // quoting?
44886                     var aname = currentElement.attributes.item(i).name;
44887                     if (!currentElement.attributes.item(i).value.length) {
44888                         continue;
44889                     }
44890                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
44891                 }
44892                 
44893                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
44894             } 
44895             else {
44896                 
44897                 // eack
44898             }
44899         } else {
44900             tagName = false;
44901         }
44902         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
44903             return ret;
44904         }
44905         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
44906             nopadtext = true;
44907         }
44908         
44909         
44910         // Traverse the tree
44911         i = 0;
44912         var currentElementChild = currentElement.childNodes.item(i);
44913         var allText = true;
44914         var innerHTML  = '';
44915         lastnode = '';
44916         while (currentElementChild) {
44917             // Formatting code (indent the tree so it looks nice on the screen)
44918             var nopad = nopadtext;
44919             if (lastnode == 'SPAN') {
44920                 nopad  = true;
44921             }
44922             // text
44923             if  (currentElementChild.nodeName == '#text') {
44924                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
44925                 toadd = nopadtext ? toadd : toadd.trim();
44926                 if (!nopad && toadd.length > 80) {
44927                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
44928                 }
44929                 innerHTML  += toadd;
44930                 
44931                 i++;
44932                 currentElementChild = currentElement.childNodes.item(i);
44933                 lastNode = '';
44934                 continue;
44935             }
44936             allText = false;
44937             
44938             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
44939                 
44940             // Recursively traverse the tree structure of the child node
44941             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
44942             lastnode = currentElementChild.nodeName;
44943             i++;
44944             currentElementChild=currentElement.childNodes.item(i);
44945         }
44946         
44947         ret += innerHTML;
44948         
44949         if (!allText) {
44950                 // The remaining code is mostly for formatting the tree
44951             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
44952         }
44953         
44954         
44955         if (tagName) {
44956             ret+= "</"+tagName+">";
44957         }
44958         return ret;
44959         
44960     },
44961         
44962     applyBlacklists : function()
44963     {
44964         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
44965         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
44966         
44967         this.white = [];
44968         this.black = [];
44969         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
44970             if (b.indexOf(tag) > -1) {
44971                 return;
44972             }
44973             this.white.push(tag);
44974             
44975         }, this);
44976         
44977         Roo.each(w, function(tag) {
44978             if (b.indexOf(tag) > -1) {
44979                 return;
44980             }
44981             if (this.white.indexOf(tag) > -1) {
44982                 return;
44983             }
44984             this.white.push(tag);
44985             
44986         }, this);
44987         
44988         
44989         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
44990             if (w.indexOf(tag) > -1) {
44991                 return;
44992             }
44993             this.black.push(tag);
44994             
44995         }, this);
44996         
44997         Roo.each(b, function(tag) {
44998             if (w.indexOf(tag) > -1) {
44999                 return;
45000             }
45001             if (this.black.indexOf(tag) > -1) {
45002                 return;
45003             }
45004             this.black.push(tag);
45005             
45006         }, this);
45007         
45008         
45009         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
45010         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
45011         
45012         this.cwhite = [];
45013         this.cblack = [];
45014         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
45015             if (b.indexOf(tag) > -1) {
45016                 return;
45017             }
45018             this.cwhite.push(tag);
45019             
45020         }, this);
45021         
45022         Roo.each(w, function(tag) {
45023             if (b.indexOf(tag) > -1) {
45024                 return;
45025             }
45026             if (this.cwhite.indexOf(tag) > -1) {
45027                 return;
45028             }
45029             this.cwhite.push(tag);
45030             
45031         }, this);
45032         
45033         
45034         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
45035             if (w.indexOf(tag) > -1) {
45036                 return;
45037             }
45038             this.cblack.push(tag);
45039             
45040         }, this);
45041         
45042         Roo.each(b, function(tag) {
45043             if (w.indexOf(tag) > -1) {
45044                 return;
45045             }
45046             if (this.cblack.indexOf(tag) > -1) {
45047                 return;
45048             }
45049             this.cblack.push(tag);
45050             
45051         }, this);
45052     },
45053     
45054     setStylesheets : function(stylesheets)
45055     {
45056         if(typeof(stylesheets) == 'string'){
45057             Roo.get(this.iframe.contentDocument.head).createChild({
45058                 tag : 'link',
45059                 rel : 'stylesheet',
45060                 type : 'text/css',
45061                 href : stylesheets
45062             });
45063             
45064             return;
45065         }
45066         var _this = this;
45067      
45068         Roo.each(stylesheets, function(s) {
45069             if(!s.length){
45070                 return;
45071             }
45072             
45073             Roo.get(_this.iframe.contentDocument.head).createChild({
45074                 tag : 'link',
45075                 rel : 'stylesheet',
45076                 type : 'text/css',
45077                 href : s
45078             });
45079         });
45080
45081         
45082     },
45083     
45084     removeStylesheets : function()
45085     {
45086         var _this = this;
45087         
45088         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
45089             s.remove();
45090         });
45091     },
45092     
45093     setStyle : function(style)
45094     {
45095         Roo.get(this.iframe.contentDocument.head).createChild({
45096             tag : 'style',
45097             type : 'text/css',
45098             html : style
45099         });
45100
45101         return;
45102     }
45103     
45104     // hide stuff that is not compatible
45105     /**
45106      * @event blur
45107      * @hide
45108      */
45109     /**
45110      * @event change
45111      * @hide
45112      */
45113     /**
45114      * @event focus
45115      * @hide
45116      */
45117     /**
45118      * @event specialkey
45119      * @hide
45120      */
45121     /**
45122      * @cfg {String} fieldClass @hide
45123      */
45124     /**
45125      * @cfg {String} focusClass @hide
45126      */
45127     /**
45128      * @cfg {String} autoCreate @hide
45129      */
45130     /**
45131      * @cfg {String} inputType @hide
45132      */
45133     /**
45134      * @cfg {String} invalidClass @hide
45135      */
45136     /**
45137      * @cfg {String} invalidText @hide
45138      */
45139     /**
45140      * @cfg {String} msgFx @hide
45141      */
45142     /**
45143      * @cfg {String} validateOnBlur @hide
45144      */
45145 });
45146
45147 Roo.HtmlEditorCore.white = [
45148         'area', 'br', 'img', 'input', 'hr', 'wbr',
45149         
45150        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
45151        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
45152        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
45153        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
45154        'table',   'ul',         'xmp', 
45155        
45156        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
45157       'thead',   'tr', 
45158      
45159       'dir', 'menu', 'ol', 'ul', 'dl',
45160        
45161       'embed',  'object'
45162 ];
45163
45164
45165 Roo.HtmlEditorCore.black = [
45166     //    'embed',  'object', // enable - backend responsiblity to clean thiese
45167         'applet', // 
45168         'base',   'basefont', 'bgsound', 'blink',  'body', 
45169         'frame',  'frameset', 'head',    'html',   'ilayer', 
45170         'iframe', 'layer',  'link',     'meta',    'object',   
45171         'script', 'style' ,'title',  'xml' // clean later..
45172 ];
45173 Roo.HtmlEditorCore.clean = [
45174     'script', 'style', 'title', 'xml'
45175 ];
45176 Roo.HtmlEditorCore.remove = [
45177     'font'
45178 ];
45179 // attributes..
45180
45181 Roo.HtmlEditorCore.ablack = [
45182     'on'
45183 ];
45184     
45185 Roo.HtmlEditorCore.aclean = [ 
45186     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
45187 ];
45188
45189 // protocols..
45190 Roo.HtmlEditorCore.pwhite= [
45191         'http',  'https',  'mailto'
45192 ];
45193
45194 // white listed style attributes.
45195 Roo.HtmlEditorCore.cwhite= [
45196       //  'text-align', /// default is to allow most things..
45197       
45198          
45199 //        'font-size'//??
45200 ];
45201
45202 // black listed style attributes.
45203 Roo.HtmlEditorCore.cblack= [
45204       //  'font-size' -- this can be set by the project 
45205 ];
45206
45207
45208 Roo.HtmlEditorCore.swapCodes   =[ 
45209     [    8211, "--" ], 
45210     [    8212, "--" ], 
45211     [    8216,  "'" ],  
45212     [    8217, "'" ],  
45213     [    8220, '"' ],  
45214     [    8221, '"' ],  
45215     [    8226, "*" ],  
45216     [    8230, "..." ]
45217 ]; 
45218
45219     //<script type="text/javascript">
45220
45221 /*
45222  * Ext JS Library 1.1.1
45223  * Copyright(c) 2006-2007, Ext JS, LLC.
45224  * Licence LGPL
45225  * 
45226  */
45227  
45228  
45229 Roo.form.HtmlEditor = function(config){
45230     
45231     
45232     
45233     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
45234     
45235     if (!this.toolbars) {
45236         this.toolbars = [];
45237     }
45238     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
45239     
45240     
45241 };
45242
45243 /**
45244  * @class Roo.form.HtmlEditor
45245  * @extends Roo.form.Field
45246  * Provides a lightweight HTML Editor component.
45247  *
45248  * This has been tested on Fireforx / Chrome.. IE may not be so great..
45249  * 
45250  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
45251  * supported by this editor.</b><br/><br/>
45252  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
45253  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
45254  */
45255 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
45256     /**
45257      * @cfg {Boolean} clearUp
45258      */
45259     clearUp : true,
45260       /**
45261      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
45262      */
45263     toolbars : false,
45264    
45265      /**
45266      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
45267      *                        Roo.resizable.
45268      */
45269     resizable : false,
45270      /**
45271      * @cfg {Number} height (in pixels)
45272      */   
45273     height: 300,
45274    /**
45275      * @cfg {Number} width (in pixels)
45276      */   
45277     width: 500,
45278     
45279     /**
45280      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
45281      * 
45282      */
45283     stylesheets: false,
45284     
45285     
45286      /**
45287      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
45288      * 
45289      */
45290     cblack: false,
45291     /**
45292      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
45293      * 
45294      */
45295     cwhite: false,
45296     
45297      /**
45298      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
45299      * 
45300      */
45301     black: false,
45302     /**
45303      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
45304      * 
45305      */
45306     white: false,
45307     
45308     // id of frame..
45309     frameId: false,
45310     
45311     // private properties
45312     validationEvent : false,
45313     deferHeight: true,
45314     initialized : false,
45315     activated : false,
45316     
45317     onFocus : Roo.emptyFn,
45318     iframePad:3,
45319     hideMode:'offsets',
45320     
45321     actionMode : 'container', // defaults to hiding it...
45322     
45323     defaultAutoCreate : { // modified by initCompnoent..
45324         tag: "textarea",
45325         style:"width:500px;height:300px;",
45326         autocomplete: "new-password"
45327     },
45328
45329     // private
45330     initComponent : function(){
45331         this.addEvents({
45332             /**
45333              * @event initialize
45334              * Fires when the editor is fully initialized (including the iframe)
45335              * @param {HtmlEditor} this
45336              */
45337             initialize: true,
45338             /**
45339              * @event activate
45340              * Fires when the editor is first receives the focus. Any insertion must wait
45341              * until after this event.
45342              * @param {HtmlEditor} this
45343              */
45344             activate: true,
45345              /**
45346              * @event beforesync
45347              * Fires before the textarea is updated with content from the editor iframe. Return false
45348              * to cancel the sync.
45349              * @param {HtmlEditor} this
45350              * @param {String} html
45351              */
45352             beforesync: true,
45353              /**
45354              * @event beforepush
45355              * Fires before the iframe editor is updated with content from the textarea. Return false
45356              * to cancel the push.
45357              * @param {HtmlEditor} this
45358              * @param {String} html
45359              */
45360             beforepush: true,
45361              /**
45362              * @event sync
45363              * Fires when the textarea is updated with content from the editor iframe.
45364              * @param {HtmlEditor} this
45365              * @param {String} html
45366              */
45367             sync: true,
45368              /**
45369              * @event push
45370              * Fires when the iframe editor is updated with content from the textarea.
45371              * @param {HtmlEditor} this
45372              * @param {String} html
45373              */
45374             push: true,
45375              /**
45376              * @event editmodechange
45377              * Fires when the editor switches edit modes
45378              * @param {HtmlEditor} this
45379              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
45380              */
45381             editmodechange: true,
45382             /**
45383              * @event editorevent
45384              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
45385              * @param {HtmlEditor} this
45386              */
45387             editorevent: true,
45388             /**
45389              * @event firstfocus
45390              * Fires when on first focus - needed by toolbars..
45391              * @param {HtmlEditor} this
45392              */
45393             firstfocus: true,
45394             /**
45395              * @event autosave
45396              * Auto save the htmlEditor value as a file into Events
45397              * @param {HtmlEditor} this
45398              */
45399             autosave: true,
45400             /**
45401              * @event savedpreview
45402              * preview the saved version of htmlEditor
45403              * @param {HtmlEditor} this
45404              */
45405             savedpreview: true,
45406             
45407             /**
45408             * @event stylesheetsclick
45409             * Fires when press the Sytlesheets button
45410             * @param {Roo.HtmlEditorCore} this
45411             */
45412             stylesheetsclick: true
45413         });
45414         this.defaultAutoCreate =  {
45415             tag: "textarea",
45416             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
45417             autocomplete: "new-password"
45418         };
45419     },
45420
45421     /**
45422      * Protected method that will not generally be called directly. It
45423      * is called when the editor creates its toolbar. Override this method if you need to
45424      * add custom toolbar buttons.
45425      * @param {HtmlEditor} editor
45426      */
45427     createToolbar : function(editor){
45428         Roo.log("create toolbars");
45429         if (!editor.toolbars || !editor.toolbars.length) {
45430             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
45431         }
45432         
45433         for (var i =0 ; i < editor.toolbars.length;i++) {
45434             editor.toolbars[i] = Roo.factory(
45435                     typeof(editor.toolbars[i]) == 'string' ?
45436                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
45437                 Roo.form.HtmlEditor);
45438             editor.toolbars[i].init(editor);
45439         }
45440          
45441         
45442     },
45443
45444      
45445     // private
45446     onRender : function(ct, position)
45447     {
45448         var _t = this;
45449         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
45450         
45451         this.wrap = this.el.wrap({
45452             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
45453         });
45454         
45455         this.editorcore.onRender(ct, position);
45456          
45457         if (this.resizable) {
45458             this.resizeEl = new Roo.Resizable(this.wrap, {
45459                 pinned : true,
45460                 wrap: true,
45461                 dynamic : true,
45462                 minHeight : this.height,
45463                 height: this.height,
45464                 handles : this.resizable,
45465                 width: this.width,
45466                 listeners : {
45467                     resize : function(r, w, h) {
45468                         _t.onResize(w,h); // -something
45469                     }
45470                 }
45471             });
45472             
45473         }
45474         this.createToolbar(this);
45475        
45476         
45477         if(!this.width){
45478             this.setSize(this.wrap.getSize());
45479         }
45480         if (this.resizeEl) {
45481             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
45482             // should trigger onReize..
45483         }
45484         
45485         this.keyNav = new Roo.KeyNav(this.el, {
45486             
45487             "tab" : function(e){
45488                 e.preventDefault();
45489                 
45490                 var value = this.getValue();
45491                 
45492                 var start = this.el.dom.selectionStart;
45493                 var end = this.el.dom.selectionEnd;
45494                 
45495                 if(!e.shiftKey){
45496                     
45497                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
45498                     this.el.dom.setSelectionRange(end + 1, end + 1);
45499                     return;
45500                 }
45501                 
45502                 var f = value.substring(0, start).split("\t");
45503                 
45504                 if(f.pop().length != 0){
45505                     return;
45506                 }
45507                 
45508                 this.setValue(f.join("\t") + value.substring(end));
45509                 this.el.dom.setSelectionRange(start - 1, start - 1);
45510                 
45511             },
45512             
45513             "home" : function(e){
45514                 e.preventDefault();
45515                 
45516                 var curr = this.el.dom.selectionStart;
45517                 var lines = this.getValue().split("\n");
45518                 
45519                 if(!lines.length){
45520                     return;
45521                 }
45522                 
45523                 if(e.ctrlKey){
45524                     this.el.dom.setSelectionRange(0, 0);
45525                     return;
45526                 }
45527                 
45528                 var pos = 0;
45529                 
45530                 for (var i = 0; i < lines.length;i++) {
45531                     pos += lines[i].length;
45532                     
45533                     if(i != 0){
45534                         pos += 1;
45535                     }
45536                     
45537                     if(pos < curr){
45538                         continue;
45539                     }
45540                     
45541                     pos -= lines[i].length;
45542                     
45543                     break;
45544                 }
45545                 
45546                 if(!e.shiftKey){
45547                     this.el.dom.setSelectionRange(pos, pos);
45548                     return;
45549                 }
45550                 
45551                 this.el.dom.selectionStart = pos;
45552                 this.el.dom.selectionEnd = curr;
45553             },
45554             
45555             "end" : function(e){
45556                 e.preventDefault();
45557                 
45558                 var curr = this.el.dom.selectionStart;
45559                 var lines = this.getValue().split("\n");
45560                 
45561                 if(!lines.length){
45562                     return;
45563                 }
45564                 
45565                 if(e.ctrlKey){
45566                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
45567                     return;
45568                 }
45569                 
45570                 var pos = 0;
45571                 
45572                 for (var i = 0; i < lines.length;i++) {
45573                     
45574                     pos += lines[i].length;
45575                     
45576                     if(i != 0){
45577                         pos += 1;
45578                     }
45579                     
45580                     if(pos < curr){
45581                         continue;
45582                     }
45583                     
45584                     break;
45585                 }
45586                 
45587                 if(!e.shiftKey){
45588                     this.el.dom.setSelectionRange(pos, pos);
45589                     return;
45590                 }
45591                 
45592                 this.el.dom.selectionStart = curr;
45593                 this.el.dom.selectionEnd = pos;
45594             },
45595
45596             scope : this,
45597
45598             doRelay : function(foo, bar, hname){
45599                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
45600             },
45601
45602             forceKeyDown: true
45603         });
45604         
45605 //        if(this.autosave && this.w){
45606 //            this.autoSaveFn = setInterval(this.autosave, 1000);
45607 //        }
45608     },
45609
45610     // private
45611     onResize : function(w, h)
45612     {
45613         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
45614         var ew = false;
45615         var eh = false;
45616         
45617         if(this.el ){
45618             if(typeof w == 'number'){
45619                 var aw = w - this.wrap.getFrameWidth('lr');
45620                 this.el.setWidth(this.adjustWidth('textarea', aw));
45621                 ew = aw;
45622             }
45623             if(typeof h == 'number'){
45624                 var tbh = 0;
45625                 for (var i =0; i < this.toolbars.length;i++) {
45626                     // fixme - ask toolbars for heights?
45627                     tbh += this.toolbars[i].tb.el.getHeight();
45628                     if (this.toolbars[i].footer) {
45629                         tbh += this.toolbars[i].footer.el.getHeight();
45630                     }
45631                 }
45632                 
45633                 
45634                 
45635                 
45636                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
45637                 ah -= 5; // knock a few pixes off for look..
45638 //                Roo.log(ah);
45639                 this.el.setHeight(this.adjustWidth('textarea', ah));
45640                 var eh = ah;
45641             }
45642         }
45643         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
45644         this.editorcore.onResize(ew,eh);
45645         
45646     },
45647
45648     /**
45649      * Toggles the editor between standard and source edit mode.
45650      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
45651      */
45652     toggleSourceEdit : function(sourceEditMode)
45653     {
45654         this.editorcore.toggleSourceEdit(sourceEditMode);
45655         
45656         if(this.editorcore.sourceEditMode){
45657             Roo.log('editor - showing textarea');
45658             
45659 //            Roo.log('in');
45660 //            Roo.log(this.syncValue());
45661             this.editorcore.syncValue();
45662             this.el.removeClass('x-hidden');
45663             this.el.dom.removeAttribute('tabIndex');
45664             this.el.focus();
45665             
45666             for (var i = 0; i < this.toolbars.length; i++) {
45667                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
45668                     this.toolbars[i].tb.hide();
45669                     this.toolbars[i].footer.hide();
45670                 }
45671             }
45672             
45673         }else{
45674             Roo.log('editor - hiding textarea');
45675 //            Roo.log('out')
45676 //            Roo.log(this.pushValue()); 
45677             this.editorcore.pushValue();
45678             
45679             this.el.addClass('x-hidden');
45680             this.el.dom.setAttribute('tabIndex', -1);
45681             
45682             for (var i = 0; i < this.toolbars.length; i++) {
45683                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
45684                     this.toolbars[i].tb.show();
45685                     this.toolbars[i].footer.show();
45686                 }
45687             }
45688             
45689             //this.deferFocus();
45690         }
45691         
45692         this.setSize(this.wrap.getSize());
45693         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
45694         
45695         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
45696     },
45697  
45698     // private (for BoxComponent)
45699     adjustSize : Roo.BoxComponent.prototype.adjustSize,
45700
45701     // private (for BoxComponent)
45702     getResizeEl : function(){
45703         return this.wrap;
45704     },
45705
45706     // private (for BoxComponent)
45707     getPositionEl : function(){
45708         return this.wrap;
45709     },
45710
45711     // private
45712     initEvents : function(){
45713         this.originalValue = this.getValue();
45714     },
45715
45716     /**
45717      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
45718      * @method
45719      */
45720     markInvalid : Roo.emptyFn,
45721     /**
45722      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
45723      * @method
45724      */
45725     clearInvalid : Roo.emptyFn,
45726
45727     setValue : function(v){
45728         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
45729         this.editorcore.pushValue();
45730     },
45731
45732      
45733     // private
45734     deferFocus : function(){
45735         this.focus.defer(10, this);
45736     },
45737
45738     // doc'ed in Field
45739     focus : function(){
45740         this.editorcore.focus();
45741         
45742     },
45743       
45744
45745     // private
45746     onDestroy : function(){
45747         
45748         
45749         
45750         if(this.rendered){
45751             
45752             for (var i =0; i < this.toolbars.length;i++) {
45753                 // fixme - ask toolbars for heights?
45754                 this.toolbars[i].onDestroy();
45755             }
45756             
45757             this.wrap.dom.innerHTML = '';
45758             this.wrap.remove();
45759         }
45760     },
45761
45762     // private
45763     onFirstFocus : function(){
45764         //Roo.log("onFirstFocus");
45765         this.editorcore.onFirstFocus();
45766          for (var i =0; i < this.toolbars.length;i++) {
45767             this.toolbars[i].onFirstFocus();
45768         }
45769         
45770     },
45771     
45772     // private
45773     syncValue : function()
45774     {
45775         this.editorcore.syncValue();
45776     },
45777     
45778     pushValue : function()
45779     {
45780         this.editorcore.pushValue();
45781     },
45782     
45783     setStylesheets : function(stylesheets)
45784     {
45785         this.editorcore.setStylesheets(stylesheets);
45786     },
45787     
45788     removeStylesheets : function()
45789     {
45790         this.editorcore.removeStylesheets();
45791     }
45792      
45793     
45794     // hide stuff that is not compatible
45795     /**
45796      * @event blur
45797      * @hide
45798      */
45799     /**
45800      * @event change
45801      * @hide
45802      */
45803     /**
45804      * @event focus
45805      * @hide
45806      */
45807     /**
45808      * @event specialkey
45809      * @hide
45810      */
45811     /**
45812      * @cfg {String} fieldClass @hide
45813      */
45814     /**
45815      * @cfg {String} focusClass @hide
45816      */
45817     /**
45818      * @cfg {String} autoCreate @hide
45819      */
45820     /**
45821      * @cfg {String} inputType @hide
45822      */
45823     /**
45824      * @cfg {String} invalidClass @hide
45825      */
45826     /**
45827      * @cfg {String} invalidText @hide
45828      */
45829     /**
45830      * @cfg {String} msgFx @hide
45831      */
45832     /**
45833      * @cfg {String} validateOnBlur @hide
45834      */
45835 });
45836  
45837     // <script type="text/javascript">
45838 /*
45839  * Based on
45840  * Ext JS Library 1.1.1
45841  * Copyright(c) 2006-2007, Ext JS, LLC.
45842  *  
45843  
45844  */
45845
45846 /**
45847  * @class Roo.form.HtmlEditorToolbar1
45848  * Basic Toolbar
45849  * 
45850  * Usage:
45851  *
45852  new Roo.form.HtmlEditor({
45853     ....
45854     toolbars : [
45855         new Roo.form.HtmlEditorToolbar1({
45856             disable : { fonts: 1 , format: 1, ..., ... , ...],
45857             btns : [ .... ]
45858         })
45859     }
45860      
45861  * 
45862  * @cfg {Object} disable List of elements to disable..
45863  * @cfg {Array} btns List of additional buttons.
45864  * 
45865  * 
45866  * NEEDS Extra CSS? 
45867  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
45868  */
45869  
45870 Roo.form.HtmlEditor.ToolbarStandard = function(config)
45871 {
45872     
45873     Roo.apply(this, config);
45874     
45875     // default disabled, based on 'good practice'..
45876     this.disable = this.disable || {};
45877     Roo.applyIf(this.disable, {
45878         fontSize : true,
45879         colors : true,
45880         specialElements : true
45881     });
45882     
45883     
45884     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
45885     // dont call parent... till later.
45886 }
45887
45888 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
45889     
45890     tb: false,
45891     
45892     rendered: false,
45893     
45894     editor : false,
45895     editorcore : false,
45896     /**
45897      * @cfg {Object} disable  List of toolbar elements to disable
45898          
45899      */
45900     disable : false,
45901     
45902     
45903      /**
45904      * @cfg {String} createLinkText The default text for the create link prompt
45905      */
45906     createLinkText : 'Please enter the URL for the link:',
45907     /**
45908      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
45909      */
45910     defaultLinkValue : 'http:/'+'/',
45911    
45912     
45913       /**
45914      * @cfg {Array} fontFamilies An array of available font families
45915      */
45916     fontFamilies : [
45917         'Arial',
45918         'Courier New',
45919         'Tahoma',
45920         'Times New Roman',
45921         'Verdana'
45922     ],
45923     
45924     specialChars : [
45925            "&#169;",
45926           "&#174;",     
45927           "&#8482;",    
45928           "&#163;" ,    
45929          // "&#8212;",    
45930           "&#8230;",    
45931           "&#247;" ,    
45932         //  "&#225;" ,     ?? a acute?
45933            "&#8364;"    , //Euro
45934        //   "&#8220;"    ,
45935         //  "&#8221;"    ,
45936         //  "&#8226;"    ,
45937           "&#176;"  //   , // degrees
45938
45939          // "&#233;"     , // e ecute
45940          // "&#250;"     , // u ecute?
45941     ],
45942     
45943     specialElements : [
45944         {
45945             text: "Insert Table",
45946             xtype: 'MenuItem',
45947             xns : Roo.Menu,
45948             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
45949                 
45950         },
45951         {    
45952             text: "Insert Image",
45953             xtype: 'MenuItem',
45954             xns : Roo.Menu,
45955             ihtml : '<img src="about:blank"/>'
45956             
45957         }
45958         
45959          
45960     ],
45961     
45962     
45963     inputElements : [ 
45964             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
45965             "input:submit", "input:button", "select", "textarea", "label" ],
45966     formats : [
45967         ["p"] ,  
45968         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
45969         ["pre"],[ "code"], 
45970         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
45971         ['div'],['span'],
45972         ['sup'],['sub']
45973     ],
45974     
45975     cleanStyles : [
45976         "font-size"
45977     ],
45978      /**
45979      * @cfg {String} defaultFont default font to use.
45980      */
45981     defaultFont: 'tahoma',
45982    
45983     fontSelect : false,
45984     
45985     
45986     formatCombo : false,
45987     
45988     init : function(editor)
45989     {
45990         this.editor = editor;
45991         this.editorcore = editor.editorcore ? editor.editorcore : editor;
45992         var editorcore = this.editorcore;
45993         
45994         var _t = this;
45995         
45996         var fid = editorcore.frameId;
45997         var etb = this;
45998         function btn(id, toggle, handler){
45999             var xid = fid + '-'+ id ;
46000             return {
46001                 id : xid,
46002                 cmd : id,
46003                 cls : 'x-btn-icon x-edit-'+id,
46004                 enableToggle:toggle !== false,
46005                 scope: _t, // was editor...
46006                 handler:handler||_t.relayBtnCmd,
46007                 clickEvent:'mousedown',
46008                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
46009                 tabIndex:-1
46010             };
46011         }
46012         
46013         
46014         
46015         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
46016         this.tb = tb;
46017          // stop form submits
46018         tb.el.on('click', function(e){
46019             e.preventDefault(); // what does this do?
46020         });
46021
46022         if(!this.disable.font) { // && !Roo.isSafari){
46023             /* why no safari for fonts 
46024             editor.fontSelect = tb.el.createChild({
46025                 tag:'select',
46026                 tabIndex: -1,
46027                 cls:'x-font-select',
46028                 html: this.createFontOptions()
46029             });
46030             
46031             editor.fontSelect.on('change', function(){
46032                 var font = editor.fontSelect.dom.value;
46033                 editor.relayCmd('fontname', font);
46034                 editor.deferFocus();
46035             }, editor);
46036             
46037             tb.add(
46038                 editor.fontSelect.dom,
46039                 '-'
46040             );
46041             */
46042             
46043         };
46044         if(!this.disable.formats){
46045             this.formatCombo = new Roo.form.ComboBox({
46046                 store: new Roo.data.SimpleStore({
46047                     id : 'tag',
46048                     fields: ['tag'],
46049                     data : this.formats // from states.js
46050                 }),
46051                 blockFocus : true,
46052                 name : '',
46053                 //autoCreate : {tag: "div",  size: "20"},
46054                 displayField:'tag',
46055                 typeAhead: false,
46056                 mode: 'local',
46057                 editable : false,
46058                 triggerAction: 'all',
46059                 emptyText:'Add tag',
46060                 selectOnFocus:true,
46061                 width:135,
46062                 listeners : {
46063                     'select': function(c, r, i) {
46064                         editorcore.insertTag(r.get('tag'));
46065                         editor.focus();
46066                     }
46067                 }
46068
46069             });
46070             tb.addField(this.formatCombo);
46071             
46072         }
46073         
46074         if(!this.disable.format){
46075             tb.add(
46076                 btn('bold'),
46077                 btn('italic'),
46078                 btn('underline'),
46079                 btn('strikethrough')
46080             );
46081         };
46082         if(!this.disable.fontSize){
46083             tb.add(
46084                 '-',
46085                 
46086                 
46087                 btn('increasefontsize', false, editorcore.adjustFont),
46088                 btn('decreasefontsize', false, editorcore.adjustFont)
46089             );
46090         };
46091         
46092         
46093         if(!this.disable.colors){
46094             tb.add(
46095                 '-', {
46096                     id:editorcore.frameId +'-forecolor',
46097                     cls:'x-btn-icon x-edit-forecolor',
46098                     clickEvent:'mousedown',
46099                     tooltip: this.buttonTips['forecolor'] || undefined,
46100                     tabIndex:-1,
46101                     menu : new Roo.menu.ColorMenu({
46102                         allowReselect: true,
46103                         focus: Roo.emptyFn,
46104                         value:'000000',
46105                         plain:true,
46106                         selectHandler: function(cp, color){
46107                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
46108                             editor.deferFocus();
46109                         },
46110                         scope: editorcore,
46111                         clickEvent:'mousedown'
46112                     })
46113                 }, {
46114                     id:editorcore.frameId +'backcolor',
46115                     cls:'x-btn-icon x-edit-backcolor',
46116                     clickEvent:'mousedown',
46117                     tooltip: this.buttonTips['backcolor'] || undefined,
46118                     tabIndex:-1,
46119                     menu : new Roo.menu.ColorMenu({
46120                         focus: Roo.emptyFn,
46121                         value:'FFFFFF',
46122                         plain:true,
46123                         allowReselect: true,
46124                         selectHandler: function(cp, color){
46125                             if(Roo.isGecko){
46126                                 editorcore.execCmd('useCSS', false);
46127                                 editorcore.execCmd('hilitecolor', color);
46128                                 editorcore.execCmd('useCSS', true);
46129                                 editor.deferFocus();
46130                             }else{
46131                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
46132                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
46133                                 editor.deferFocus();
46134                             }
46135                         },
46136                         scope:editorcore,
46137                         clickEvent:'mousedown'
46138                     })
46139                 }
46140             );
46141         };
46142         // now add all the items...
46143         
46144
46145         if(!this.disable.alignments){
46146             tb.add(
46147                 '-',
46148                 btn('justifyleft'),
46149                 btn('justifycenter'),
46150                 btn('justifyright')
46151             );
46152         };
46153
46154         //if(!Roo.isSafari){
46155             if(!this.disable.links){
46156                 tb.add(
46157                     '-',
46158                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
46159                 );
46160             };
46161
46162             if(!this.disable.lists){
46163                 tb.add(
46164                     '-',
46165                     btn('insertorderedlist'),
46166                     btn('insertunorderedlist')
46167                 );
46168             }
46169             if(!this.disable.sourceEdit){
46170                 tb.add(
46171                     '-',
46172                     btn('sourceedit', true, function(btn){
46173                         this.toggleSourceEdit(btn.pressed);
46174                     })
46175                 );
46176             }
46177         //}
46178         
46179         var smenu = { };
46180         // special menu.. - needs to be tidied up..
46181         if (!this.disable.special) {
46182             smenu = {
46183                 text: "&#169;",
46184                 cls: 'x-edit-none',
46185                 
46186                 menu : {
46187                     items : []
46188                 }
46189             };
46190             for (var i =0; i < this.specialChars.length; i++) {
46191                 smenu.menu.items.push({
46192                     
46193                     html: this.specialChars[i],
46194                     handler: function(a,b) {
46195                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
46196                         //editor.insertAtCursor(a.html);
46197                         
46198                     },
46199                     tabIndex:-1
46200                 });
46201             }
46202             
46203             
46204             tb.add(smenu);
46205             
46206             
46207         }
46208         
46209         var cmenu = { };
46210         if (!this.disable.cleanStyles) {
46211             cmenu = {
46212                 cls: 'x-btn-icon x-btn-clear',
46213                 
46214                 menu : {
46215                     items : []
46216                 }
46217             };
46218             for (var i =0; i < this.cleanStyles.length; i++) {
46219                 cmenu.menu.items.push({
46220                     actiontype : this.cleanStyles[i],
46221                     html: 'Remove ' + this.cleanStyles[i],
46222                     handler: function(a,b) {
46223 //                        Roo.log(a);
46224 //                        Roo.log(b);
46225                         var c = Roo.get(editorcore.doc.body);
46226                         c.select('[style]').each(function(s) {
46227                             s.dom.style.removeProperty(a.actiontype);
46228                         });
46229                         editorcore.syncValue();
46230                     },
46231                     tabIndex:-1
46232                 });
46233             }
46234              cmenu.menu.items.push({
46235                 actiontype : 'tablewidths',
46236                 html: 'Remove Table Widths',
46237                 handler: function(a,b) {
46238                     editorcore.cleanTableWidths();
46239                     editorcore.syncValue();
46240                 },
46241                 tabIndex:-1
46242             });
46243             cmenu.menu.items.push({
46244                 actiontype : 'word',
46245                 html: 'Remove MS Word Formating',
46246                 handler: function(a,b) {
46247                     editorcore.cleanWord();
46248                     editorcore.syncValue();
46249                 },
46250                 tabIndex:-1
46251             });
46252             
46253             cmenu.menu.items.push({
46254                 actiontype : 'all',
46255                 html: 'Remove All Styles',
46256                 handler: function(a,b) {
46257                     
46258                     var c = Roo.get(editorcore.doc.body);
46259                     c.select('[style]').each(function(s) {
46260                         s.dom.removeAttribute('style');
46261                     });
46262                     editorcore.syncValue();
46263                 },
46264                 tabIndex:-1
46265             });
46266             
46267             cmenu.menu.items.push({
46268                 actiontype : 'all',
46269                 html: 'Remove All CSS Classes',
46270                 handler: function(a,b) {
46271                     
46272                     var c = Roo.get(editorcore.doc.body);
46273                     c.select('[class]').each(function(s) {
46274                         s.dom.removeAttribute('class');
46275                     });
46276                     editorcore.cleanWord();
46277                     editorcore.syncValue();
46278                 },
46279                 tabIndex:-1
46280             });
46281             
46282              cmenu.menu.items.push({
46283                 actiontype : 'tidy',
46284                 html: 'Tidy HTML Source',
46285                 handler: function(a,b) {
46286                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
46287                     editorcore.syncValue();
46288                 },
46289                 tabIndex:-1
46290             });
46291             
46292             
46293             tb.add(cmenu);
46294         }
46295          
46296         if (!this.disable.specialElements) {
46297             var semenu = {
46298                 text: "Other;",
46299                 cls: 'x-edit-none',
46300                 menu : {
46301                     items : []
46302                 }
46303             };
46304             for (var i =0; i < this.specialElements.length; i++) {
46305                 semenu.menu.items.push(
46306                     Roo.apply({ 
46307                         handler: function(a,b) {
46308                             editor.insertAtCursor(this.ihtml);
46309                         }
46310                     }, this.specialElements[i])
46311                 );
46312                     
46313             }
46314             
46315             tb.add(semenu);
46316             
46317             
46318         }
46319          
46320         
46321         if (this.btns) {
46322             for(var i =0; i< this.btns.length;i++) {
46323                 var b = Roo.factory(this.btns[i],Roo.form);
46324                 b.cls =  'x-edit-none';
46325                 
46326                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
46327                     b.cls += ' x-init-enable';
46328                 }
46329                 
46330                 b.scope = editorcore;
46331                 tb.add(b);
46332             }
46333         
46334         }
46335         
46336         
46337         
46338         // disable everything...
46339         
46340         this.tb.items.each(function(item){
46341             
46342            if(
46343                 item.id != editorcore.frameId+ '-sourceedit' && 
46344                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
46345             ){
46346                 
46347                 item.disable();
46348             }
46349         });
46350         this.rendered = true;
46351         
46352         // the all the btns;
46353         editor.on('editorevent', this.updateToolbar, this);
46354         // other toolbars need to implement this..
46355         //editor.on('editmodechange', this.updateToolbar, this);
46356     },
46357     
46358     
46359     relayBtnCmd : function(btn) {
46360         this.editorcore.relayCmd(btn.cmd);
46361     },
46362     // private used internally
46363     createLink : function(){
46364         Roo.log("create link?");
46365         var url = prompt(this.createLinkText, this.defaultLinkValue);
46366         if(url && url != 'http:/'+'/'){
46367             this.editorcore.relayCmd('createlink', url);
46368         }
46369     },
46370
46371     
46372     /**
46373      * Protected method that will not generally be called directly. It triggers
46374      * a toolbar update by reading the markup state of the current selection in the editor.
46375      */
46376     updateToolbar: function(){
46377
46378         if(!this.editorcore.activated){
46379             this.editor.onFirstFocus();
46380             return;
46381         }
46382
46383         var btns = this.tb.items.map, 
46384             doc = this.editorcore.doc,
46385             frameId = this.editorcore.frameId;
46386
46387         if(!this.disable.font && !Roo.isSafari){
46388             /*
46389             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
46390             if(name != this.fontSelect.dom.value){
46391                 this.fontSelect.dom.value = name;
46392             }
46393             */
46394         }
46395         if(!this.disable.format){
46396             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
46397             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
46398             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
46399             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
46400         }
46401         if(!this.disable.alignments){
46402             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
46403             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
46404             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
46405         }
46406         if(!Roo.isSafari && !this.disable.lists){
46407             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
46408             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
46409         }
46410         
46411         var ans = this.editorcore.getAllAncestors();
46412         if (this.formatCombo) {
46413             
46414             
46415             var store = this.formatCombo.store;
46416             this.formatCombo.setValue("");
46417             for (var i =0; i < ans.length;i++) {
46418                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
46419                     // select it..
46420                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
46421                     break;
46422                 }
46423             }
46424         }
46425         
46426         
46427         
46428         // hides menus... - so this cant be on a menu...
46429         Roo.menu.MenuMgr.hideAll();
46430
46431         //this.editorsyncValue();
46432     },
46433    
46434     
46435     createFontOptions : function(){
46436         var buf = [], fs = this.fontFamilies, ff, lc;
46437         
46438         
46439         
46440         for(var i = 0, len = fs.length; i< len; i++){
46441             ff = fs[i];
46442             lc = ff.toLowerCase();
46443             buf.push(
46444                 '<option value="',lc,'" style="font-family:',ff,';"',
46445                     (this.defaultFont == lc ? ' selected="true">' : '>'),
46446                     ff,
46447                 '</option>'
46448             );
46449         }
46450         return buf.join('');
46451     },
46452     
46453     toggleSourceEdit : function(sourceEditMode){
46454         
46455         Roo.log("toolbar toogle");
46456         if(sourceEditMode === undefined){
46457             sourceEditMode = !this.sourceEditMode;
46458         }
46459         this.sourceEditMode = sourceEditMode === true;
46460         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
46461         // just toggle the button?
46462         if(btn.pressed !== this.sourceEditMode){
46463             btn.toggle(this.sourceEditMode);
46464             return;
46465         }
46466         
46467         if(sourceEditMode){
46468             Roo.log("disabling buttons");
46469             this.tb.items.each(function(item){
46470                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
46471                     item.disable();
46472                 }
46473             });
46474           
46475         }else{
46476             Roo.log("enabling buttons");
46477             if(this.editorcore.initialized){
46478                 this.tb.items.each(function(item){
46479                     item.enable();
46480                 });
46481             }
46482             
46483         }
46484         Roo.log("calling toggole on editor");
46485         // tell the editor that it's been pressed..
46486         this.editor.toggleSourceEdit(sourceEditMode);
46487        
46488     },
46489      /**
46490      * Object collection of toolbar tooltips for the buttons in the editor. The key
46491      * is the command id associated with that button and the value is a valid QuickTips object.
46492      * For example:
46493 <pre><code>
46494 {
46495     bold : {
46496         title: 'Bold (Ctrl+B)',
46497         text: 'Make the selected text bold.',
46498         cls: 'x-html-editor-tip'
46499     },
46500     italic : {
46501         title: 'Italic (Ctrl+I)',
46502         text: 'Make the selected text italic.',
46503         cls: 'x-html-editor-tip'
46504     },
46505     ...
46506 </code></pre>
46507     * @type Object
46508      */
46509     buttonTips : {
46510         bold : {
46511             title: 'Bold (Ctrl+B)',
46512             text: 'Make the selected text bold.',
46513             cls: 'x-html-editor-tip'
46514         },
46515         italic : {
46516             title: 'Italic (Ctrl+I)',
46517             text: 'Make the selected text italic.',
46518             cls: 'x-html-editor-tip'
46519         },
46520         underline : {
46521             title: 'Underline (Ctrl+U)',
46522             text: 'Underline the selected text.',
46523             cls: 'x-html-editor-tip'
46524         },
46525         strikethrough : {
46526             title: 'Strikethrough',
46527             text: 'Strikethrough the selected text.',
46528             cls: 'x-html-editor-tip'
46529         },
46530         increasefontsize : {
46531             title: 'Grow Text',
46532             text: 'Increase the font size.',
46533             cls: 'x-html-editor-tip'
46534         },
46535         decreasefontsize : {
46536             title: 'Shrink Text',
46537             text: 'Decrease the font size.',
46538             cls: 'x-html-editor-tip'
46539         },
46540         backcolor : {
46541             title: 'Text Highlight Color',
46542             text: 'Change the background color of the selected text.',
46543             cls: 'x-html-editor-tip'
46544         },
46545         forecolor : {
46546             title: 'Font Color',
46547             text: 'Change the color of the selected text.',
46548             cls: 'x-html-editor-tip'
46549         },
46550         justifyleft : {
46551             title: 'Align Text Left',
46552             text: 'Align text to the left.',
46553             cls: 'x-html-editor-tip'
46554         },
46555         justifycenter : {
46556             title: 'Center Text',
46557             text: 'Center text in the editor.',
46558             cls: 'x-html-editor-tip'
46559         },
46560         justifyright : {
46561             title: 'Align Text Right',
46562             text: 'Align text to the right.',
46563             cls: 'x-html-editor-tip'
46564         },
46565         insertunorderedlist : {
46566             title: 'Bullet List',
46567             text: 'Start a bulleted list.',
46568             cls: 'x-html-editor-tip'
46569         },
46570         insertorderedlist : {
46571             title: 'Numbered List',
46572             text: 'Start a numbered list.',
46573             cls: 'x-html-editor-tip'
46574         },
46575         createlink : {
46576             title: 'Hyperlink',
46577             text: 'Make the selected text a hyperlink.',
46578             cls: 'x-html-editor-tip'
46579         },
46580         sourceedit : {
46581             title: 'Source Edit',
46582             text: 'Switch to source editing mode.',
46583             cls: 'x-html-editor-tip'
46584         }
46585     },
46586     // private
46587     onDestroy : function(){
46588         if(this.rendered){
46589             
46590             this.tb.items.each(function(item){
46591                 if(item.menu){
46592                     item.menu.removeAll();
46593                     if(item.menu.el){
46594                         item.menu.el.destroy();
46595                     }
46596                 }
46597                 item.destroy();
46598             });
46599              
46600         }
46601     },
46602     onFirstFocus: function() {
46603         this.tb.items.each(function(item){
46604            item.enable();
46605         });
46606     }
46607 });
46608
46609
46610
46611
46612 // <script type="text/javascript">
46613 /*
46614  * Based on
46615  * Ext JS Library 1.1.1
46616  * Copyright(c) 2006-2007, Ext JS, LLC.
46617  *  
46618  
46619  */
46620
46621  
46622 /**
46623  * @class Roo.form.HtmlEditor.ToolbarContext
46624  * Context Toolbar
46625  * 
46626  * Usage:
46627  *
46628  new Roo.form.HtmlEditor({
46629     ....
46630     toolbars : [
46631         { xtype: 'ToolbarStandard', styles : {} }
46632         { xtype: 'ToolbarContext', disable : {} }
46633     ]
46634 })
46635
46636      
46637  * 
46638  * @config : {Object} disable List of elements to disable.. (not done yet.)
46639  * @config : {Object} styles  Map of styles available.
46640  * 
46641  */
46642
46643 Roo.form.HtmlEditor.ToolbarContext = function(config)
46644 {
46645     
46646     Roo.apply(this, config);
46647     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
46648     // dont call parent... till later.
46649     this.styles = this.styles || {};
46650 }
46651
46652  
46653
46654 Roo.form.HtmlEditor.ToolbarContext.types = {
46655     'IMG' : {
46656         width : {
46657             title: "Width",
46658             width: 40
46659         },
46660         height:  {
46661             title: "Height",
46662             width: 40
46663         },
46664         align: {
46665             title: "Align",
46666             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
46667             width : 80
46668             
46669         },
46670         border: {
46671             title: "Border",
46672             width: 40
46673         },
46674         alt: {
46675             title: "Alt",
46676             width: 120
46677         },
46678         src : {
46679             title: "Src",
46680             width: 220
46681         }
46682         
46683     },
46684     'A' : {
46685         name : {
46686             title: "Name",
46687             width: 50
46688         },
46689         target:  {
46690             title: "Target",
46691             width: 120
46692         },
46693         href:  {
46694             title: "Href",
46695             width: 220
46696         } // border?
46697         
46698     },
46699     'TABLE' : {
46700         rows : {
46701             title: "Rows",
46702             width: 20
46703         },
46704         cols : {
46705             title: "Cols",
46706             width: 20
46707         },
46708         width : {
46709             title: "Width",
46710             width: 40
46711         },
46712         height : {
46713             title: "Height",
46714             width: 40
46715         },
46716         border : {
46717             title: "Border",
46718             width: 20
46719         }
46720     },
46721     'TD' : {
46722         width : {
46723             title: "Width",
46724             width: 40
46725         },
46726         height : {
46727             title: "Height",
46728             width: 40
46729         },   
46730         align: {
46731             title: "Align",
46732             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
46733             width: 80
46734         },
46735         valign: {
46736             title: "Valign",
46737             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
46738             width: 80
46739         },
46740         colspan: {
46741             title: "Colspan",
46742             width: 20
46743             
46744         },
46745          'font-family'  : {
46746             title : "Font",
46747             style : 'fontFamily',
46748             displayField: 'display',
46749             optname : 'font-family',
46750             width: 140
46751         }
46752     },
46753     'INPUT' : {
46754         name : {
46755             title: "name",
46756             width: 120
46757         },
46758         value : {
46759             title: "Value",
46760             width: 120
46761         },
46762         width : {
46763             title: "Width",
46764             width: 40
46765         }
46766     },
46767     'LABEL' : {
46768         'for' : {
46769             title: "For",
46770             width: 120
46771         }
46772     },
46773     'TEXTAREA' : {
46774           name : {
46775             title: "name",
46776             width: 120
46777         },
46778         rows : {
46779             title: "Rows",
46780             width: 20
46781         },
46782         cols : {
46783             title: "Cols",
46784             width: 20
46785         }
46786     },
46787     'SELECT' : {
46788         name : {
46789             title: "name",
46790             width: 120
46791         },
46792         selectoptions : {
46793             title: "Options",
46794             width: 200
46795         }
46796     },
46797     
46798     // should we really allow this??
46799     // should this just be 
46800     'BODY' : {
46801         title : {
46802             title: "Title",
46803             width: 200,
46804             disabled : true
46805         }
46806     },
46807     'SPAN' : {
46808         'font-family'  : {
46809             title : "Font",
46810             style : 'fontFamily',
46811             displayField: 'display',
46812             optname : 'font-family',
46813             width: 140
46814         }
46815     },
46816     'DIV' : {
46817         'font-family'  : {
46818             title : "Font",
46819             style : 'fontFamily',
46820             displayField: 'display',
46821             optname : 'font-family',
46822             width: 140
46823         }
46824     },
46825      'P' : {
46826         'font-family'  : {
46827             title : "Font",
46828             style : 'fontFamily',
46829             displayField: 'display',
46830             optname : 'font-family',
46831             width: 140
46832         }
46833     },
46834     
46835     '*' : {
46836         // empty..
46837     }
46838
46839 };
46840
46841 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
46842 Roo.form.HtmlEditor.ToolbarContext.stores = false;
46843
46844 Roo.form.HtmlEditor.ToolbarContext.options = {
46845         'font-family'  : [ 
46846                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
46847                 [ 'Courier New', 'Courier New'],
46848                 [ 'Tahoma', 'Tahoma'],
46849                 [ 'Times New Roman,serif', 'Times'],
46850                 [ 'Verdana','Verdana' ]
46851         ]
46852 };
46853
46854 // fixme - these need to be configurable..
46855  
46856
46857 //Roo.form.HtmlEditor.ToolbarContext.types
46858
46859
46860 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
46861     
46862     tb: false,
46863     
46864     rendered: false,
46865     
46866     editor : false,
46867     editorcore : false,
46868     /**
46869      * @cfg {Object} disable  List of toolbar elements to disable
46870          
46871      */
46872     disable : false,
46873     /**
46874      * @cfg {Object} styles List of styles 
46875      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
46876      *
46877      * These must be defined in the page, so they get rendered correctly..
46878      * .headline { }
46879      * TD.underline { }
46880      * 
46881      */
46882     styles : false,
46883     
46884     options: false,
46885     
46886     toolbars : false,
46887     
46888     init : function(editor)
46889     {
46890         this.editor = editor;
46891         this.editorcore = editor.editorcore ? editor.editorcore : editor;
46892         var editorcore = this.editorcore;
46893         
46894         var fid = editorcore.frameId;
46895         var etb = this;
46896         function btn(id, toggle, handler){
46897             var xid = fid + '-'+ id ;
46898             return {
46899                 id : xid,
46900                 cmd : id,
46901                 cls : 'x-btn-icon x-edit-'+id,
46902                 enableToggle:toggle !== false,
46903                 scope: editorcore, // was editor...
46904                 handler:handler||editorcore.relayBtnCmd,
46905                 clickEvent:'mousedown',
46906                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
46907                 tabIndex:-1
46908             };
46909         }
46910         // create a new element.
46911         var wdiv = editor.wrap.createChild({
46912                 tag: 'div'
46913             }, editor.wrap.dom.firstChild.nextSibling, true);
46914         
46915         // can we do this more than once??
46916         
46917          // stop form submits
46918       
46919  
46920         // disable everything...
46921         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46922         this.toolbars = {};
46923            
46924         for (var i in  ty) {
46925           
46926             this.toolbars[i] = this.buildToolbar(ty[i],i);
46927         }
46928         this.tb = this.toolbars.BODY;
46929         this.tb.el.show();
46930         this.buildFooter();
46931         this.footer.show();
46932         editor.on('hide', function( ) { this.footer.hide() }, this);
46933         editor.on('show', function( ) { this.footer.show() }, this);
46934         
46935          
46936         this.rendered = true;
46937         
46938         // the all the btns;
46939         editor.on('editorevent', this.updateToolbar, this);
46940         // other toolbars need to implement this..
46941         //editor.on('editmodechange', this.updateToolbar, this);
46942     },
46943     
46944     
46945     
46946     /**
46947      * Protected method that will not generally be called directly. It triggers
46948      * a toolbar update by reading the markup state of the current selection in the editor.
46949      *
46950      * Note you can force an update by calling on('editorevent', scope, false)
46951      */
46952     updateToolbar: function(editor,ev,sel){
46953
46954         //Roo.log(ev);
46955         // capture mouse up - this is handy for selecting images..
46956         // perhaps should go somewhere else...
46957         if(!this.editorcore.activated){
46958              this.editor.onFirstFocus();
46959             return;
46960         }
46961         
46962         
46963         
46964         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
46965         // selectNode - might want to handle IE?
46966         if (ev &&
46967             (ev.type == 'mouseup' || ev.type == 'click' ) &&
46968             ev.target && ev.target.tagName == 'IMG') {
46969             // they have click on an image...
46970             // let's see if we can change the selection...
46971             sel = ev.target;
46972          
46973               var nodeRange = sel.ownerDocument.createRange();
46974             try {
46975                 nodeRange.selectNode(sel);
46976             } catch (e) {
46977                 nodeRange.selectNodeContents(sel);
46978             }
46979             //nodeRange.collapse(true);
46980             var s = this.editorcore.win.getSelection();
46981             s.removeAllRanges();
46982             s.addRange(nodeRange);
46983         }  
46984         
46985       
46986         var updateFooter = sel ? false : true;
46987         
46988         
46989         var ans = this.editorcore.getAllAncestors();
46990         
46991         // pick
46992         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46993         
46994         if (!sel) { 
46995             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
46996             sel = sel ? sel : this.editorcore.doc.body;
46997             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
46998             
46999         }
47000         // pick a menu that exists..
47001         var tn = sel.tagName.toUpperCase();
47002         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
47003         
47004         tn = sel.tagName.toUpperCase();
47005         
47006         var lastSel = this.tb.selectedNode;
47007         
47008         this.tb.selectedNode = sel;
47009         
47010         // if current menu does not match..
47011         
47012         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
47013                 
47014             this.tb.el.hide();
47015             ///console.log("show: " + tn);
47016             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
47017             this.tb.el.show();
47018             // update name
47019             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
47020             
47021             
47022             // update attributes
47023             if (this.tb.fields) {
47024                 this.tb.fields.each(function(e) {
47025                     if (e.stylename) {
47026                         e.setValue(sel.style[e.stylename]);
47027                         return;
47028                     } 
47029                    e.setValue(sel.getAttribute(e.attrname));
47030                 });
47031             }
47032             
47033             var hasStyles = false;
47034             for(var i in this.styles) {
47035                 hasStyles = true;
47036                 break;
47037             }
47038             
47039             // update styles
47040             if (hasStyles) { 
47041                 var st = this.tb.fields.item(0);
47042                 
47043                 st.store.removeAll();
47044                
47045                 
47046                 var cn = sel.className.split(/\s+/);
47047                 
47048                 var avs = [];
47049                 if (this.styles['*']) {
47050                     
47051                     Roo.each(this.styles['*'], function(v) {
47052                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
47053                     });
47054                 }
47055                 if (this.styles[tn]) { 
47056                     Roo.each(this.styles[tn], function(v) {
47057                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
47058                     });
47059                 }
47060                 
47061                 st.store.loadData(avs);
47062                 st.collapse();
47063                 st.setValue(cn);
47064             }
47065             // flag our selected Node.
47066             this.tb.selectedNode = sel;
47067            
47068            
47069             Roo.menu.MenuMgr.hideAll();
47070
47071         }
47072         
47073         if (!updateFooter) {
47074             //this.footDisp.dom.innerHTML = ''; 
47075             return;
47076         }
47077         // update the footer
47078         //
47079         var html = '';
47080         
47081         this.footerEls = ans.reverse();
47082         Roo.each(this.footerEls, function(a,i) {
47083             if (!a) { return; }
47084             html += html.length ? ' &gt; '  :  '';
47085             
47086             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
47087             
47088         });
47089        
47090         // 
47091         var sz = this.footDisp.up('td').getSize();
47092         this.footDisp.dom.style.width = (sz.width -10) + 'px';
47093         this.footDisp.dom.style.marginLeft = '5px';
47094         
47095         this.footDisp.dom.style.overflow = 'hidden';
47096         
47097         this.footDisp.dom.innerHTML = html;
47098             
47099         //this.editorsyncValue();
47100     },
47101      
47102     
47103    
47104        
47105     // private
47106     onDestroy : function(){
47107         if(this.rendered){
47108             
47109             this.tb.items.each(function(item){
47110                 if(item.menu){
47111                     item.menu.removeAll();
47112                     if(item.menu.el){
47113                         item.menu.el.destroy();
47114                     }
47115                 }
47116                 item.destroy();
47117             });
47118              
47119         }
47120     },
47121     onFirstFocus: function() {
47122         // need to do this for all the toolbars..
47123         this.tb.items.each(function(item){
47124            item.enable();
47125         });
47126     },
47127     buildToolbar: function(tlist, nm)
47128     {
47129         var editor = this.editor;
47130         var editorcore = this.editorcore;
47131          // create a new element.
47132         var wdiv = editor.wrap.createChild({
47133                 tag: 'div'
47134             }, editor.wrap.dom.firstChild.nextSibling, true);
47135         
47136        
47137         var tb = new Roo.Toolbar(wdiv);
47138         // add the name..
47139         
47140         tb.add(nm+ ":&nbsp;");
47141         
47142         var styles = [];
47143         for(var i in this.styles) {
47144             styles.push(i);
47145         }
47146         
47147         // styles...
47148         if (styles && styles.length) {
47149             
47150             // this needs a multi-select checkbox...
47151             tb.addField( new Roo.form.ComboBox({
47152                 store: new Roo.data.SimpleStore({
47153                     id : 'val',
47154                     fields: ['val', 'selected'],
47155                     data : [] 
47156                 }),
47157                 name : '-roo-edit-className',
47158                 attrname : 'className',
47159                 displayField: 'val',
47160                 typeAhead: false,
47161                 mode: 'local',
47162                 editable : false,
47163                 triggerAction: 'all',
47164                 emptyText:'Select Style',
47165                 selectOnFocus:true,
47166                 width: 130,
47167                 listeners : {
47168                     'select': function(c, r, i) {
47169                         // initial support only for on class per el..
47170                         tb.selectedNode.className =  r ? r.get('val') : '';
47171                         editorcore.syncValue();
47172                     }
47173                 }
47174     
47175             }));
47176         }
47177         
47178         var tbc = Roo.form.HtmlEditor.ToolbarContext;
47179         var tbops = tbc.options;
47180         
47181         for (var i in tlist) {
47182             
47183             var item = tlist[i];
47184             tb.add(item.title + ":&nbsp;");
47185             
47186             
47187             //optname == used so you can configure the options available..
47188             var opts = item.opts ? item.opts : false;
47189             if (item.optname) {
47190                 opts = tbops[item.optname];
47191            
47192             }
47193             
47194             if (opts) {
47195                 // opts == pulldown..
47196                 tb.addField( new Roo.form.ComboBox({
47197                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
47198                         id : 'val',
47199                         fields: ['val', 'display'],
47200                         data : opts  
47201                     }),
47202                     name : '-roo-edit-' + i,
47203                     attrname : i,
47204                     stylename : item.style ? item.style : false,
47205                     displayField: item.displayField ? item.displayField : 'val',
47206                     valueField :  'val',
47207                     typeAhead: false,
47208                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
47209                     editable : false,
47210                     triggerAction: 'all',
47211                     emptyText:'Select',
47212                     selectOnFocus:true,
47213                     width: item.width ? item.width  : 130,
47214                     listeners : {
47215                         'select': function(c, r, i) {
47216                             if (c.stylename) {
47217                                 tb.selectedNode.style[c.stylename] =  r.get('val');
47218                                 return;
47219                             }
47220                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
47221                         }
47222                     }
47223
47224                 }));
47225                 continue;
47226                     
47227                  
47228                 
47229                 tb.addField( new Roo.form.TextField({
47230                     name: i,
47231                     width: 100,
47232                     //allowBlank:false,
47233                     value: ''
47234                 }));
47235                 continue;
47236             }
47237             tb.addField( new Roo.form.TextField({
47238                 name: '-roo-edit-' + i,
47239                 attrname : i,
47240                 
47241                 width: item.width,
47242                 //allowBlank:true,
47243                 value: '',
47244                 listeners: {
47245                     'change' : function(f, nv, ov) {
47246                         tb.selectedNode.setAttribute(f.attrname, nv);
47247                         editorcore.syncValue();
47248                     }
47249                 }
47250             }));
47251              
47252         }
47253         
47254         var _this = this;
47255         
47256         if(nm == 'BODY'){
47257             tb.addSeparator();
47258         
47259             tb.addButton( {
47260                 text: 'Stylesheets',
47261
47262                 listeners : {
47263                     click : function ()
47264                     {
47265                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
47266                     }
47267                 }
47268             });
47269         }
47270         
47271         tb.addFill();
47272         tb.addButton( {
47273             text: 'Remove Tag',
47274     
47275             listeners : {
47276                 click : function ()
47277                 {
47278                     // remove
47279                     // undo does not work.
47280                      
47281                     var sn = tb.selectedNode;
47282                     
47283                     var pn = sn.parentNode;
47284                     
47285                     var stn =  sn.childNodes[0];
47286                     var en = sn.childNodes[sn.childNodes.length - 1 ];
47287                     while (sn.childNodes.length) {
47288                         var node = sn.childNodes[0];
47289                         sn.removeChild(node);
47290                         //Roo.log(node);
47291                         pn.insertBefore(node, sn);
47292                         
47293                     }
47294                     pn.removeChild(sn);
47295                     var range = editorcore.createRange();
47296         
47297                     range.setStart(stn,0);
47298                     range.setEnd(en,0); //????
47299                     //range.selectNode(sel);
47300                     
47301                     
47302                     var selection = editorcore.getSelection();
47303                     selection.removeAllRanges();
47304                     selection.addRange(range);
47305                     
47306                     
47307                     
47308                     //_this.updateToolbar(null, null, pn);
47309                     _this.updateToolbar(null, null, null);
47310                     _this.footDisp.dom.innerHTML = ''; 
47311                 }
47312             }
47313             
47314                     
47315                 
47316             
47317         });
47318         
47319         
47320         tb.el.on('click', function(e){
47321             e.preventDefault(); // what does this do?
47322         });
47323         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
47324         tb.el.hide();
47325         tb.name = nm;
47326         // dont need to disable them... as they will get hidden
47327         return tb;
47328          
47329         
47330     },
47331     buildFooter : function()
47332     {
47333         
47334         var fel = this.editor.wrap.createChild();
47335         this.footer = new Roo.Toolbar(fel);
47336         // toolbar has scrolly on left / right?
47337         var footDisp= new Roo.Toolbar.Fill();
47338         var _t = this;
47339         this.footer.add(
47340             {
47341                 text : '&lt;',
47342                 xtype: 'Button',
47343                 handler : function() {
47344                     _t.footDisp.scrollTo('left',0,true)
47345                 }
47346             }
47347         );
47348         this.footer.add( footDisp );
47349         this.footer.add( 
47350             {
47351                 text : '&gt;',
47352                 xtype: 'Button',
47353                 handler : function() {
47354                     // no animation..
47355                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
47356                 }
47357             }
47358         );
47359         var fel = Roo.get(footDisp.el);
47360         fel.addClass('x-editor-context');
47361         this.footDispWrap = fel; 
47362         this.footDispWrap.overflow  = 'hidden';
47363         
47364         this.footDisp = fel.createChild();
47365         this.footDispWrap.on('click', this.onContextClick, this)
47366         
47367         
47368     },
47369     onContextClick : function (ev,dom)
47370     {
47371         ev.preventDefault();
47372         var  cn = dom.className;
47373         //Roo.log(cn);
47374         if (!cn.match(/x-ed-loc-/)) {
47375             return;
47376         }
47377         var n = cn.split('-').pop();
47378         var ans = this.footerEls;
47379         var sel = ans[n];
47380         
47381          // pick
47382         var range = this.editorcore.createRange();
47383         
47384         range.selectNodeContents(sel);
47385         //range.selectNode(sel);
47386         
47387         
47388         var selection = this.editorcore.getSelection();
47389         selection.removeAllRanges();
47390         selection.addRange(range);
47391         
47392         
47393         
47394         this.updateToolbar(null, null, sel);
47395         
47396         
47397     }
47398     
47399     
47400     
47401     
47402     
47403 });
47404
47405
47406
47407
47408
47409 /*
47410  * Based on:
47411  * Ext JS Library 1.1.1
47412  * Copyright(c) 2006-2007, Ext JS, LLC.
47413  *
47414  * Originally Released Under LGPL - original licence link has changed is not relivant.
47415  *
47416  * Fork - LGPL
47417  * <script type="text/javascript">
47418  */
47419  
47420 /**
47421  * @class Roo.form.BasicForm
47422  * @extends Roo.util.Observable
47423  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
47424  * @constructor
47425  * @param {String/HTMLElement/Roo.Element} el The form element or its id
47426  * @param {Object} config Configuration options
47427  */
47428 Roo.form.BasicForm = function(el, config){
47429     this.allItems = [];
47430     this.childForms = [];
47431     Roo.apply(this, config);
47432     /*
47433      * The Roo.form.Field items in this form.
47434      * @type MixedCollection
47435      */
47436      
47437      
47438     this.items = new Roo.util.MixedCollection(false, function(o){
47439         return o.id || (o.id = Roo.id());
47440     });
47441     this.addEvents({
47442         /**
47443          * @event beforeaction
47444          * Fires before any action is performed. Return false to cancel the action.
47445          * @param {Form} this
47446          * @param {Action} action The action to be performed
47447          */
47448         beforeaction: true,
47449         /**
47450          * @event actionfailed
47451          * Fires when an action fails.
47452          * @param {Form} this
47453          * @param {Action} action The action that failed
47454          */
47455         actionfailed : true,
47456         /**
47457          * @event actioncomplete
47458          * Fires when an action is completed.
47459          * @param {Form} this
47460          * @param {Action} action The action that completed
47461          */
47462         actioncomplete : true
47463     });
47464     if(el){
47465         this.initEl(el);
47466     }
47467     Roo.form.BasicForm.superclass.constructor.call(this);
47468     
47469     Roo.form.BasicForm.popover.apply();
47470 };
47471
47472 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
47473     /**
47474      * @cfg {String} method
47475      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
47476      */
47477     /**
47478      * @cfg {DataReader} reader
47479      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
47480      * This is optional as there is built-in support for processing JSON.
47481      */
47482     /**
47483      * @cfg {DataReader} errorReader
47484      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
47485      * This is completely optional as there is built-in support for processing JSON.
47486      */
47487     /**
47488      * @cfg {String} url
47489      * The URL to use for form actions if one isn't supplied in the action options.
47490      */
47491     /**
47492      * @cfg {Boolean} fileUpload
47493      * Set to true if this form is a file upload.
47494      */
47495      
47496     /**
47497      * @cfg {Object} baseParams
47498      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
47499      */
47500      /**
47501      
47502     /**
47503      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
47504      */
47505     timeout: 30,
47506
47507     // private
47508     activeAction : null,
47509
47510     /**
47511      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
47512      * or setValues() data instead of when the form was first created.
47513      */
47514     trackResetOnLoad : false,
47515     
47516     
47517     /**
47518      * childForms - used for multi-tab forms
47519      * @type {Array}
47520      */
47521     childForms : false,
47522     
47523     /**
47524      * allItems - full list of fields.
47525      * @type {Array}
47526      */
47527     allItems : false,
47528     
47529     /**
47530      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
47531      * element by passing it or its id or mask the form itself by passing in true.
47532      * @type Mixed
47533      */
47534     waitMsgTarget : false,
47535     
47536     /**
47537      * @type Boolean
47538      */
47539     disableMask : false,
47540     
47541     /**
47542      * @cfg {Boolean} errorMask (true|false) default false
47543      */
47544     errorMask : false,
47545     
47546     /**
47547      * @cfg {Number} maskOffset Default 100
47548      */
47549     maskOffset : 100,
47550
47551     // private
47552     initEl : function(el){
47553         this.el = Roo.get(el);
47554         this.id = this.el.id || Roo.id();
47555         this.el.on('submit', this.onSubmit, this);
47556         this.el.addClass('x-form');
47557     },
47558
47559     // private
47560     onSubmit : function(e){
47561         e.stopEvent();
47562     },
47563
47564     /**
47565      * Returns true if client-side validation on the form is successful.
47566      * @return Boolean
47567      */
47568     isValid : function(){
47569         var valid = true;
47570         var target = false;
47571         this.items.each(function(f){
47572             if(f.validate()){
47573                 return;
47574             }
47575             
47576             valid = false;
47577                 
47578             if(!target && f.el.isVisible(true)){
47579                 target = f;
47580             }
47581         });
47582         
47583         if(this.errorMask && !valid){
47584             Roo.form.BasicForm.popover.mask(this, target);
47585         }
47586         
47587         return valid;
47588     },
47589     /**
47590      * Returns array of invalid form fields.
47591      * @return Array
47592      */
47593     
47594     invalidFields : function()
47595     {
47596         var ret = [];
47597         this.items.each(function(f){
47598             if(f.validate()){
47599                 return;
47600             }
47601             ret.push(f);
47602             
47603         });
47604         
47605         return ret;
47606     },
47607     
47608     
47609     /**
47610      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
47611      * @return Boolean
47612      */
47613     isDirty : function(){
47614         var dirty = false;
47615         this.items.each(function(f){
47616            if(f.isDirty()){
47617                dirty = true;
47618                return false;
47619            }
47620         });
47621         return dirty;
47622     },
47623     
47624     /**
47625      * Returns true if any fields in this form have changed since their original load. (New version)
47626      * @return Boolean
47627      */
47628     
47629     hasChanged : function()
47630     {
47631         var dirty = false;
47632         this.items.each(function(f){
47633            if(f.hasChanged()){
47634                dirty = true;
47635                return false;
47636            }
47637         });
47638         return dirty;
47639         
47640     },
47641     /**
47642      * Resets all hasChanged to 'false' -
47643      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
47644      * So hasChanged storage is only to be used for this purpose
47645      * @return Boolean
47646      */
47647     resetHasChanged : function()
47648     {
47649         this.items.each(function(f){
47650            f.resetHasChanged();
47651         });
47652         
47653     },
47654     
47655     
47656     /**
47657      * Performs a predefined action (submit or load) or custom actions you define on this form.
47658      * @param {String} actionName The name of the action type
47659      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
47660      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
47661      * accept other config options):
47662      * <pre>
47663 Property          Type             Description
47664 ----------------  ---------------  ----------------------------------------------------------------------------------
47665 url               String           The url for the action (defaults to the form's url)
47666 method            String           The form method to use (defaults to the form's method, or POST if not defined)
47667 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
47668 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
47669                                    validate the form on the client (defaults to false)
47670      * </pre>
47671      * @return {BasicForm} this
47672      */
47673     doAction : function(action, options){
47674         if(typeof action == 'string'){
47675             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
47676         }
47677         if(this.fireEvent('beforeaction', this, action) !== false){
47678             this.beforeAction(action);
47679             action.run.defer(100, action);
47680         }
47681         return this;
47682     },
47683
47684     /**
47685      * Shortcut to do a submit action.
47686      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
47687      * @return {BasicForm} this
47688      */
47689     submit : function(options){
47690         this.doAction('submit', options);
47691         return this;
47692     },
47693
47694     /**
47695      * Shortcut to do a load action.
47696      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
47697      * @return {BasicForm} this
47698      */
47699     load : function(options){
47700         this.doAction('load', options);
47701         return this;
47702     },
47703
47704     /**
47705      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
47706      * @param {Record} record The record to edit
47707      * @return {BasicForm} this
47708      */
47709     updateRecord : function(record){
47710         record.beginEdit();
47711         var fs = record.fields;
47712         fs.each(function(f){
47713             var field = this.findField(f.name);
47714             if(field){
47715                 record.set(f.name, field.getValue());
47716             }
47717         }, this);
47718         record.endEdit();
47719         return this;
47720     },
47721
47722     /**
47723      * Loads an Roo.data.Record into this form.
47724      * @param {Record} record The record to load
47725      * @return {BasicForm} this
47726      */
47727     loadRecord : function(record){
47728         this.setValues(record.data);
47729         return this;
47730     },
47731
47732     // private
47733     beforeAction : function(action){
47734         var o = action.options;
47735         
47736         if(!this.disableMask) {
47737             if(this.waitMsgTarget === true){
47738                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
47739             }else if(this.waitMsgTarget){
47740                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
47741                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
47742             }else {
47743                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
47744             }
47745         }
47746         
47747          
47748     },
47749
47750     // private
47751     afterAction : function(action, success){
47752         this.activeAction = null;
47753         var o = action.options;
47754         
47755         if(!this.disableMask) {
47756             if(this.waitMsgTarget === true){
47757                 this.el.unmask();
47758             }else if(this.waitMsgTarget){
47759                 this.waitMsgTarget.unmask();
47760             }else{
47761                 Roo.MessageBox.updateProgress(1);
47762                 Roo.MessageBox.hide();
47763             }
47764         }
47765         
47766         if(success){
47767             if(o.reset){
47768                 this.reset();
47769             }
47770             Roo.callback(o.success, o.scope, [this, action]);
47771             this.fireEvent('actioncomplete', this, action);
47772             
47773         }else{
47774             
47775             // failure condition..
47776             // we have a scenario where updates need confirming.
47777             // eg. if a locking scenario exists..
47778             // we look for { errors : { needs_confirm : true }} in the response.
47779             if (
47780                 (typeof(action.result) != 'undefined')  &&
47781                 (typeof(action.result.errors) != 'undefined')  &&
47782                 (typeof(action.result.errors.needs_confirm) != 'undefined')
47783            ){
47784                 var _t = this;
47785                 Roo.MessageBox.confirm(
47786                     "Change requires confirmation",
47787                     action.result.errorMsg,
47788                     function(r) {
47789                         if (r != 'yes') {
47790                             return;
47791                         }
47792                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
47793                     }
47794                     
47795                 );
47796                 
47797                 
47798                 
47799                 return;
47800             }
47801             
47802             Roo.callback(o.failure, o.scope, [this, action]);
47803             // show an error message if no failed handler is set..
47804             if (!this.hasListener('actionfailed')) {
47805                 Roo.MessageBox.alert("Error",
47806                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
47807                         action.result.errorMsg :
47808                         "Saving Failed, please check your entries or try again"
47809                 );
47810             }
47811             
47812             this.fireEvent('actionfailed', this, action);
47813         }
47814         
47815     },
47816
47817     /**
47818      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
47819      * @param {String} id The value to search for
47820      * @return Field
47821      */
47822     findField : function(id){
47823         var field = this.items.get(id);
47824         if(!field){
47825             this.items.each(function(f){
47826                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
47827                     field = f;
47828                     return false;
47829                 }
47830             });
47831         }
47832         return field || null;
47833     },
47834
47835     /**
47836      * Add a secondary form to this one, 
47837      * Used to provide tabbed forms. One form is primary, with hidden values 
47838      * which mirror the elements from the other forms.
47839      * 
47840      * @param {Roo.form.Form} form to add.
47841      * 
47842      */
47843     addForm : function(form)
47844     {
47845        
47846         if (this.childForms.indexOf(form) > -1) {
47847             // already added..
47848             return;
47849         }
47850         this.childForms.push(form);
47851         var n = '';
47852         Roo.each(form.allItems, function (fe) {
47853             
47854             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
47855             if (this.findField(n)) { // already added..
47856                 return;
47857             }
47858             var add = new Roo.form.Hidden({
47859                 name : n
47860             });
47861             add.render(this.el);
47862             
47863             this.add( add );
47864         }, this);
47865         
47866     },
47867     /**
47868      * Mark fields in this form invalid in bulk.
47869      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
47870      * @return {BasicForm} this
47871      */
47872     markInvalid : function(errors){
47873         if(errors instanceof Array){
47874             for(var i = 0, len = errors.length; i < len; i++){
47875                 var fieldError = errors[i];
47876                 var f = this.findField(fieldError.id);
47877                 if(f){
47878                     f.markInvalid(fieldError.msg);
47879                 }
47880             }
47881         }else{
47882             var field, id;
47883             for(id in errors){
47884                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
47885                     field.markInvalid(errors[id]);
47886                 }
47887             }
47888         }
47889         Roo.each(this.childForms || [], function (f) {
47890             f.markInvalid(errors);
47891         });
47892         
47893         return this;
47894     },
47895
47896     /**
47897      * Set values for fields in this form in bulk.
47898      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
47899      * @return {BasicForm} this
47900      */
47901     setValues : function(values){
47902         if(values instanceof Array){ // array of objects
47903             for(var i = 0, len = values.length; i < len; i++){
47904                 var v = values[i];
47905                 var f = this.findField(v.id);
47906                 if(f){
47907                     f.setValue(v.value);
47908                     if(this.trackResetOnLoad){
47909                         f.originalValue = f.getValue();
47910                     }
47911                 }
47912             }
47913         }else{ // object hash
47914             var field, id;
47915             for(id in values){
47916                 if(typeof values[id] != 'function' && (field = this.findField(id))){
47917                     
47918                     if (field.setFromData && 
47919                         field.valueField && 
47920                         field.displayField &&
47921                         // combos' with local stores can 
47922                         // be queried via setValue()
47923                         // to set their value..
47924                         (field.store && !field.store.isLocal)
47925                         ) {
47926                         // it's a combo
47927                         var sd = { };
47928                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
47929                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
47930                         field.setFromData(sd);
47931                         
47932                     } else {
47933                         field.setValue(values[id]);
47934                     }
47935                     
47936                     
47937                     if(this.trackResetOnLoad){
47938                         field.originalValue = field.getValue();
47939                     }
47940                 }
47941             }
47942         }
47943         this.resetHasChanged();
47944         
47945         
47946         Roo.each(this.childForms || [], function (f) {
47947             f.setValues(values);
47948             f.resetHasChanged();
47949         });
47950                 
47951         return this;
47952     },
47953  
47954     /**
47955      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
47956      * they are returned as an array.
47957      * @param {Boolean} asString
47958      * @return {Object}
47959      */
47960     getValues : function(asString){
47961         if (this.childForms) {
47962             // copy values from the child forms
47963             Roo.each(this.childForms, function (f) {
47964                 this.setValues(f.getValues());
47965             }, this);
47966         }
47967         
47968         // use formdata
47969         if (typeof(FormData) != 'undefined' && asString !== true) {
47970             // this relies on a 'recent' version of chrome apparently...
47971             try {
47972                 var fd = (new FormData(this.el.dom)).entries();
47973                 var ret = {};
47974                 var ent = fd.next();
47975                 while (!ent.done) {
47976                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
47977                     ent = fd.next();
47978                 };
47979                 return ret;
47980             } catch(e) {
47981                 
47982             }
47983             
47984         }
47985         
47986         
47987         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
47988         if(asString === true){
47989             return fs;
47990         }
47991         return Roo.urlDecode(fs);
47992     },
47993     
47994     /**
47995      * Returns the fields in this form as an object with key/value pairs. 
47996      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
47997      * @return {Object}
47998      */
47999     getFieldValues : function(with_hidden)
48000     {
48001         if (this.childForms) {
48002             // copy values from the child forms
48003             // should this call getFieldValues - probably not as we do not currently copy
48004             // hidden fields when we generate..
48005             Roo.each(this.childForms, function (f) {
48006                 this.setValues(f.getValues());
48007             }, this);
48008         }
48009         
48010         var ret = {};
48011         this.items.each(function(f){
48012             if (!f.getName()) {
48013                 return;
48014             }
48015             var v = f.getValue();
48016             if (f.inputType =='radio') {
48017                 if (typeof(ret[f.getName()]) == 'undefined') {
48018                     ret[f.getName()] = ''; // empty..
48019                 }
48020                 
48021                 if (!f.el.dom.checked) {
48022                     return;
48023                     
48024                 }
48025                 v = f.el.dom.value;
48026                 
48027             }
48028             
48029             // not sure if this supported any more..
48030             if ((typeof(v) == 'object') && f.getRawValue) {
48031                 v = f.getRawValue() ; // dates..
48032             }
48033             // combo boxes where name != hiddenName...
48034             if (f.name != f.getName()) {
48035                 ret[f.name] = f.getRawValue();
48036             }
48037             ret[f.getName()] = v;
48038         });
48039         
48040         return ret;
48041     },
48042
48043     /**
48044      * Clears all invalid messages in this form.
48045      * @return {BasicForm} this
48046      */
48047     clearInvalid : function(){
48048         this.items.each(function(f){
48049            f.clearInvalid();
48050         });
48051         
48052         Roo.each(this.childForms || [], function (f) {
48053             f.clearInvalid();
48054         });
48055         
48056         
48057         return this;
48058     },
48059
48060     /**
48061      * Resets this form.
48062      * @return {BasicForm} this
48063      */
48064     reset : function(){
48065         this.items.each(function(f){
48066             f.reset();
48067         });
48068         
48069         Roo.each(this.childForms || [], function (f) {
48070             f.reset();
48071         });
48072         this.resetHasChanged();
48073         
48074         return this;
48075     },
48076
48077     /**
48078      * Add Roo.form components to this form.
48079      * @param {Field} field1
48080      * @param {Field} field2 (optional)
48081      * @param {Field} etc (optional)
48082      * @return {BasicForm} this
48083      */
48084     add : function(){
48085         this.items.addAll(Array.prototype.slice.call(arguments, 0));
48086         return this;
48087     },
48088
48089
48090     /**
48091      * Removes a field from the items collection (does NOT remove its markup).
48092      * @param {Field} field
48093      * @return {BasicForm} this
48094      */
48095     remove : function(field){
48096         this.items.remove(field);
48097         return this;
48098     },
48099
48100     /**
48101      * Looks at the fields in this form, checks them for an id attribute,
48102      * and calls applyTo on the existing dom element with that id.
48103      * @return {BasicForm} this
48104      */
48105     render : function(){
48106         this.items.each(function(f){
48107             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
48108                 f.applyTo(f.id);
48109             }
48110         });
48111         return this;
48112     },
48113
48114     /**
48115      * Calls {@link Ext#apply} for all fields in this form with the passed object.
48116      * @param {Object} values
48117      * @return {BasicForm} this
48118      */
48119     applyToFields : function(o){
48120         this.items.each(function(f){
48121            Roo.apply(f, o);
48122         });
48123         return this;
48124     },
48125
48126     /**
48127      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
48128      * @param {Object} values
48129      * @return {BasicForm} this
48130      */
48131     applyIfToFields : function(o){
48132         this.items.each(function(f){
48133            Roo.applyIf(f, o);
48134         });
48135         return this;
48136     }
48137 });
48138
48139 // back compat
48140 Roo.BasicForm = Roo.form.BasicForm;
48141
48142 Roo.apply(Roo.form.BasicForm, {
48143     
48144     popover : {
48145         
48146         padding : 5,
48147         
48148         isApplied : false,
48149         
48150         isMasked : false,
48151         
48152         form : false,
48153         
48154         target : false,
48155         
48156         intervalID : false,
48157         
48158         maskEl : false,
48159         
48160         apply : function()
48161         {
48162             if(this.isApplied){
48163                 return;
48164             }
48165             
48166             this.maskEl = {
48167                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
48168                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
48169                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
48170                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
48171             };
48172             
48173             this.maskEl.top.enableDisplayMode("block");
48174             this.maskEl.left.enableDisplayMode("block");
48175             this.maskEl.bottom.enableDisplayMode("block");
48176             this.maskEl.right.enableDisplayMode("block");
48177             
48178             Roo.get(document.body).on('click', function(){
48179                 this.unmask();
48180             }, this);
48181             
48182             Roo.get(document.body).on('touchstart', function(){
48183                 this.unmask();
48184             }, this);
48185             
48186             this.isApplied = true
48187         },
48188         
48189         mask : function(form, target)
48190         {
48191             this.form = form;
48192             
48193             this.target = target;
48194             
48195             if(!this.form.errorMask || !target.el){
48196                 return;
48197             }
48198             
48199             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
48200             
48201             var ot = this.target.el.calcOffsetsTo(scrollable);
48202             
48203             var scrollTo = ot[1] - this.form.maskOffset;
48204             
48205             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
48206             
48207             scrollable.scrollTo('top', scrollTo);
48208             
48209             var el = this.target.wrap || this.target.el;
48210             
48211             var box = el.getBox();
48212             
48213             this.maskEl.top.setStyle('position', 'absolute');
48214             this.maskEl.top.setStyle('z-index', 10000);
48215             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
48216             this.maskEl.top.setLeft(0);
48217             this.maskEl.top.setTop(0);
48218             this.maskEl.top.show();
48219             
48220             this.maskEl.left.setStyle('position', 'absolute');
48221             this.maskEl.left.setStyle('z-index', 10000);
48222             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
48223             this.maskEl.left.setLeft(0);
48224             this.maskEl.left.setTop(box.y - this.padding);
48225             this.maskEl.left.show();
48226
48227             this.maskEl.bottom.setStyle('position', 'absolute');
48228             this.maskEl.bottom.setStyle('z-index', 10000);
48229             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
48230             this.maskEl.bottom.setLeft(0);
48231             this.maskEl.bottom.setTop(box.bottom + this.padding);
48232             this.maskEl.bottom.show();
48233
48234             this.maskEl.right.setStyle('position', 'absolute');
48235             this.maskEl.right.setStyle('z-index', 10000);
48236             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
48237             this.maskEl.right.setLeft(box.right + this.padding);
48238             this.maskEl.right.setTop(box.y - this.padding);
48239             this.maskEl.right.show();
48240
48241             this.intervalID = window.setInterval(function() {
48242                 Roo.form.BasicForm.popover.unmask();
48243             }, 10000);
48244
48245             window.onwheel = function(){ return false;};
48246             
48247             (function(){ this.isMasked = true; }).defer(500, this);
48248             
48249         },
48250         
48251         unmask : function()
48252         {
48253             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
48254                 return;
48255             }
48256             
48257             this.maskEl.top.setStyle('position', 'absolute');
48258             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
48259             this.maskEl.top.hide();
48260
48261             this.maskEl.left.setStyle('position', 'absolute');
48262             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
48263             this.maskEl.left.hide();
48264
48265             this.maskEl.bottom.setStyle('position', 'absolute');
48266             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
48267             this.maskEl.bottom.hide();
48268
48269             this.maskEl.right.setStyle('position', 'absolute');
48270             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
48271             this.maskEl.right.hide();
48272             
48273             window.onwheel = function(){ return true;};
48274             
48275             if(this.intervalID){
48276                 window.clearInterval(this.intervalID);
48277                 this.intervalID = false;
48278             }
48279             
48280             this.isMasked = false;
48281             
48282         }
48283         
48284     }
48285     
48286 });/*
48287  * Based on:
48288  * Ext JS Library 1.1.1
48289  * Copyright(c) 2006-2007, Ext JS, LLC.
48290  *
48291  * Originally Released Under LGPL - original licence link has changed is not relivant.
48292  *
48293  * Fork - LGPL
48294  * <script type="text/javascript">
48295  */
48296
48297 /**
48298  * @class Roo.form.Form
48299  * @extends Roo.form.BasicForm
48300  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
48301  * @constructor
48302  * @param {Object} config Configuration options
48303  */
48304 Roo.form.Form = function(config){
48305     var xitems =  [];
48306     if (config.items) {
48307         xitems = config.items;
48308         delete config.items;
48309     }
48310    
48311     
48312     Roo.form.Form.superclass.constructor.call(this, null, config);
48313     this.url = this.url || this.action;
48314     if(!this.root){
48315         this.root = new Roo.form.Layout(Roo.applyIf({
48316             id: Roo.id()
48317         }, config));
48318     }
48319     this.active = this.root;
48320     /**
48321      * Array of all the buttons that have been added to this form via {@link addButton}
48322      * @type Array
48323      */
48324     this.buttons = [];
48325     this.allItems = [];
48326     this.addEvents({
48327         /**
48328          * @event clientvalidation
48329          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
48330          * @param {Form} this
48331          * @param {Boolean} valid true if the form has passed client-side validation
48332          */
48333         clientvalidation: true,
48334         /**
48335          * @event rendered
48336          * Fires when the form is rendered
48337          * @param {Roo.form.Form} form
48338          */
48339         rendered : true
48340     });
48341     
48342     if (this.progressUrl) {
48343             // push a hidden field onto the list of fields..
48344             this.addxtype( {
48345                     xns: Roo.form, 
48346                     xtype : 'Hidden', 
48347                     name : 'UPLOAD_IDENTIFIER' 
48348             });
48349         }
48350         
48351     
48352     Roo.each(xitems, this.addxtype, this);
48353     
48354 };
48355
48356 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
48357     /**
48358      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
48359      */
48360     /**
48361      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
48362      */
48363     /**
48364      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
48365      */
48366     buttonAlign:'center',
48367
48368     /**
48369      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
48370      */
48371     minButtonWidth:75,
48372
48373     /**
48374      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
48375      * This property cascades to child containers if not set.
48376      */
48377     labelAlign:'left',
48378
48379     /**
48380      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
48381      * fires a looping event with that state. This is required to bind buttons to the valid
48382      * state using the config value formBind:true on the button.
48383      */
48384     monitorValid : false,
48385
48386     /**
48387      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
48388      */
48389     monitorPoll : 200,
48390     
48391     /**
48392      * @cfg {String} progressUrl - Url to return progress data 
48393      */
48394     
48395     progressUrl : false,
48396     /**
48397      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
48398      * sending a formdata with extra parameters - eg uploaded elements.
48399      */
48400     
48401     formData : false,
48402     
48403     /**
48404      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
48405      * fields are added and the column is closed. If no fields are passed the column remains open
48406      * until end() is called.
48407      * @param {Object} config The config to pass to the column
48408      * @param {Field} field1 (optional)
48409      * @param {Field} field2 (optional)
48410      * @param {Field} etc (optional)
48411      * @return Column The column container object
48412      */
48413     column : function(c){
48414         var col = new Roo.form.Column(c);
48415         this.start(col);
48416         if(arguments.length > 1){ // duplicate code required because of Opera
48417             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48418             this.end();
48419         }
48420         return col;
48421     },
48422
48423     /**
48424      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
48425      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
48426      * until end() is called.
48427      * @param {Object} config The config to pass to the fieldset
48428      * @param {Field} field1 (optional)
48429      * @param {Field} field2 (optional)
48430      * @param {Field} etc (optional)
48431      * @return FieldSet The fieldset container object
48432      */
48433     fieldset : function(c){
48434         var fs = new Roo.form.FieldSet(c);
48435         this.start(fs);
48436         if(arguments.length > 1){ // duplicate code required because of Opera
48437             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48438             this.end();
48439         }
48440         return fs;
48441     },
48442
48443     /**
48444      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
48445      * fields are added and the container is closed. If no fields are passed the container remains open
48446      * until end() is called.
48447      * @param {Object} config The config to pass to the Layout
48448      * @param {Field} field1 (optional)
48449      * @param {Field} field2 (optional)
48450      * @param {Field} etc (optional)
48451      * @return Layout The container object
48452      */
48453     container : function(c){
48454         var l = new Roo.form.Layout(c);
48455         this.start(l);
48456         if(arguments.length > 1){ // duplicate code required because of Opera
48457             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48458             this.end();
48459         }
48460         return l;
48461     },
48462
48463     /**
48464      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
48465      * @param {Object} container A Roo.form.Layout or subclass of Layout
48466      * @return {Form} this
48467      */
48468     start : function(c){
48469         // cascade label info
48470         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
48471         this.active.stack.push(c);
48472         c.ownerCt = this.active;
48473         this.active = c;
48474         return this;
48475     },
48476
48477     /**
48478      * Closes the current open container
48479      * @return {Form} this
48480      */
48481     end : function(){
48482         if(this.active == this.root){
48483             return this;
48484         }
48485         this.active = this.active.ownerCt;
48486         return this;
48487     },
48488
48489     /**
48490      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
48491      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
48492      * as the label of the field.
48493      * @param {Field} field1
48494      * @param {Field} field2 (optional)
48495      * @param {Field} etc. (optional)
48496      * @return {Form} this
48497      */
48498     add : function(){
48499         this.active.stack.push.apply(this.active.stack, arguments);
48500         this.allItems.push.apply(this.allItems,arguments);
48501         var r = [];
48502         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
48503             if(a[i].isFormField){
48504                 r.push(a[i]);
48505             }
48506         }
48507         if(r.length > 0){
48508             Roo.form.Form.superclass.add.apply(this, r);
48509         }
48510         return this;
48511     },
48512     
48513
48514     
48515     
48516     
48517      /**
48518      * Find any element that has been added to a form, using it's ID or name
48519      * This can include framesets, columns etc. along with regular fields..
48520      * @param {String} id - id or name to find.
48521      
48522      * @return {Element} e - or false if nothing found.
48523      */
48524     findbyId : function(id)
48525     {
48526         var ret = false;
48527         if (!id) {
48528             return ret;
48529         }
48530         Roo.each(this.allItems, function(f){
48531             if (f.id == id || f.name == id ){
48532                 ret = f;
48533                 return false;
48534             }
48535         });
48536         return ret;
48537     },
48538
48539     
48540     
48541     /**
48542      * Render this form into the passed container. This should only be called once!
48543      * @param {String/HTMLElement/Element} container The element this component should be rendered into
48544      * @return {Form} this
48545      */
48546     render : function(ct)
48547     {
48548         
48549         
48550         
48551         ct = Roo.get(ct);
48552         var o = this.autoCreate || {
48553             tag: 'form',
48554             method : this.method || 'POST',
48555             id : this.id || Roo.id()
48556         };
48557         this.initEl(ct.createChild(o));
48558
48559         this.root.render(this.el);
48560         
48561        
48562              
48563         this.items.each(function(f){
48564             f.render('x-form-el-'+f.id);
48565         });
48566
48567         if(this.buttons.length > 0){
48568             // tables are required to maintain order and for correct IE layout
48569             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
48570                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
48571                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
48572             }}, null, true);
48573             var tr = tb.getElementsByTagName('tr')[0];
48574             for(var i = 0, len = this.buttons.length; i < len; i++) {
48575                 var b = this.buttons[i];
48576                 var td = document.createElement('td');
48577                 td.className = 'x-form-btn-td';
48578                 b.render(tr.appendChild(td));
48579             }
48580         }
48581         if(this.monitorValid){ // initialize after render
48582             this.startMonitoring();
48583         }
48584         this.fireEvent('rendered', this);
48585         return this;
48586     },
48587
48588     /**
48589      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
48590      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
48591      * object or a valid Roo.DomHelper element config
48592      * @param {Function} handler The function called when the button is clicked
48593      * @param {Object} scope (optional) The scope of the handler function
48594      * @return {Roo.Button}
48595      */
48596     addButton : function(config, handler, scope){
48597         var bc = {
48598             handler: handler,
48599             scope: scope,
48600             minWidth: this.minButtonWidth,
48601             hideParent:true
48602         };
48603         if(typeof config == "string"){
48604             bc.text = config;
48605         }else{
48606             Roo.apply(bc, config);
48607         }
48608         var btn = new Roo.Button(null, bc);
48609         this.buttons.push(btn);
48610         return btn;
48611     },
48612
48613      /**
48614      * Adds a series of form elements (using the xtype property as the factory method.
48615      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
48616      * @param {Object} config 
48617      */
48618     
48619     addxtype : function()
48620     {
48621         var ar = Array.prototype.slice.call(arguments, 0);
48622         var ret = false;
48623         for(var i = 0; i < ar.length; i++) {
48624             if (!ar[i]) {
48625                 continue; // skip -- if this happends something invalid got sent, we 
48626                 // should ignore it, as basically that interface element will not show up
48627                 // and that should be pretty obvious!!
48628             }
48629             
48630             if (Roo.form[ar[i].xtype]) {
48631                 ar[i].form = this;
48632                 var fe = Roo.factory(ar[i], Roo.form);
48633                 if (!ret) {
48634                     ret = fe;
48635                 }
48636                 fe.form = this;
48637                 if (fe.store) {
48638                     fe.store.form = this;
48639                 }
48640                 if (fe.isLayout) {  
48641                          
48642                     this.start(fe);
48643                     this.allItems.push(fe);
48644                     if (fe.items && fe.addxtype) {
48645                         fe.addxtype.apply(fe, fe.items);
48646                         delete fe.items;
48647                     }
48648                      this.end();
48649                     continue;
48650                 }
48651                 
48652                 
48653                  
48654                 this.add(fe);
48655               //  console.log('adding ' + ar[i].xtype);
48656             }
48657             if (ar[i].xtype == 'Button') {  
48658                 //console.log('adding button');
48659                 //console.log(ar[i]);
48660                 this.addButton(ar[i]);
48661                 this.allItems.push(fe);
48662                 continue;
48663             }
48664             
48665             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
48666                 alert('end is not supported on xtype any more, use items');
48667             //    this.end();
48668             //    //console.log('adding end');
48669             }
48670             
48671         }
48672         return ret;
48673     },
48674     
48675     /**
48676      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
48677      * option "monitorValid"
48678      */
48679     startMonitoring : function(){
48680         if(!this.bound){
48681             this.bound = true;
48682             Roo.TaskMgr.start({
48683                 run : this.bindHandler,
48684                 interval : this.monitorPoll || 200,
48685                 scope: this
48686             });
48687         }
48688     },
48689
48690     /**
48691      * Stops monitoring of the valid state of this form
48692      */
48693     stopMonitoring : function(){
48694         this.bound = false;
48695     },
48696
48697     // private
48698     bindHandler : function(){
48699         if(!this.bound){
48700             return false; // stops binding
48701         }
48702         var valid = true;
48703         this.items.each(function(f){
48704             if(!f.isValid(true)){
48705                 valid = false;
48706                 return false;
48707             }
48708         });
48709         for(var i = 0, len = this.buttons.length; i < len; i++){
48710             var btn = this.buttons[i];
48711             if(btn.formBind === true && btn.disabled === valid){
48712                 btn.setDisabled(!valid);
48713             }
48714         }
48715         this.fireEvent('clientvalidation', this, valid);
48716     }
48717     
48718     
48719     
48720     
48721     
48722     
48723     
48724     
48725 });
48726
48727
48728 // back compat
48729 Roo.Form = Roo.form.Form;
48730 /*
48731  * Based on:
48732  * Ext JS Library 1.1.1
48733  * Copyright(c) 2006-2007, Ext JS, LLC.
48734  *
48735  * Originally Released Under LGPL - original licence link has changed is not relivant.
48736  *
48737  * Fork - LGPL
48738  * <script type="text/javascript">
48739  */
48740
48741 // as we use this in bootstrap.
48742 Roo.namespace('Roo.form');
48743  /**
48744  * @class Roo.form.Action
48745  * Internal Class used to handle form actions
48746  * @constructor
48747  * @param {Roo.form.BasicForm} el The form element or its id
48748  * @param {Object} config Configuration options
48749  */
48750
48751  
48752  
48753 // define the action interface
48754 Roo.form.Action = function(form, options){
48755     this.form = form;
48756     this.options = options || {};
48757 };
48758 /**
48759  * Client Validation Failed
48760  * @const 
48761  */
48762 Roo.form.Action.CLIENT_INVALID = 'client';
48763 /**
48764  * Server Validation Failed
48765  * @const 
48766  */
48767 Roo.form.Action.SERVER_INVALID = 'server';
48768  /**
48769  * Connect to Server Failed
48770  * @const 
48771  */
48772 Roo.form.Action.CONNECT_FAILURE = 'connect';
48773 /**
48774  * Reading Data from Server Failed
48775  * @const 
48776  */
48777 Roo.form.Action.LOAD_FAILURE = 'load';
48778
48779 Roo.form.Action.prototype = {
48780     type : 'default',
48781     failureType : undefined,
48782     response : undefined,
48783     result : undefined,
48784
48785     // interface method
48786     run : function(options){
48787
48788     },
48789
48790     // interface method
48791     success : function(response){
48792
48793     },
48794
48795     // interface method
48796     handleResponse : function(response){
48797
48798     },
48799
48800     // default connection failure
48801     failure : function(response){
48802         
48803         this.response = response;
48804         this.failureType = Roo.form.Action.CONNECT_FAILURE;
48805         this.form.afterAction(this, false);
48806     },
48807
48808     processResponse : function(response){
48809         this.response = response;
48810         if(!response.responseText){
48811             return true;
48812         }
48813         this.result = this.handleResponse(response);
48814         return this.result;
48815     },
48816
48817     // utility functions used internally
48818     getUrl : function(appendParams){
48819         var url = this.options.url || this.form.url || this.form.el.dom.action;
48820         if(appendParams){
48821             var p = this.getParams();
48822             if(p){
48823                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
48824             }
48825         }
48826         return url;
48827     },
48828
48829     getMethod : function(){
48830         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
48831     },
48832
48833     getParams : function(){
48834         var bp = this.form.baseParams;
48835         var p = this.options.params;
48836         if(p){
48837             if(typeof p == "object"){
48838                 p = Roo.urlEncode(Roo.applyIf(p, bp));
48839             }else if(typeof p == 'string' && bp){
48840                 p += '&' + Roo.urlEncode(bp);
48841             }
48842         }else if(bp){
48843             p = Roo.urlEncode(bp);
48844         }
48845         return p;
48846     },
48847
48848     createCallback : function(){
48849         return {
48850             success: this.success,
48851             failure: this.failure,
48852             scope: this,
48853             timeout: (this.form.timeout*1000),
48854             upload: this.form.fileUpload ? this.success : undefined
48855         };
48856     }
48857 };
48858
48859 Roo.form.Action.Submit = function(form, options){
48860     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
48861 };
48862
48863 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
48864     type : 'submit',
48865
48866     haveProgress : false,
48867     uploadComplete : false,
48868     
48869     // uploadProgress indicator.
48870     uploadProgress : function()
48871     {
48872         if (!this.form.progressUrl) {
48873             return;
48874         }
48875         
48876         if (!this.haveProgress) {
48877             Roo.MessageBox.progress("Uploading", "Uploading");
48878         }
48879         if (this.uploadComplete) {
48880            Roo.MessageBox.hide();
48881            return;
48882         }
48883         
48884         this.haveProgress = true;
48885    
48886         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
48887         
48888         var c = new Roo.data.Connection();
48889         c.request({
48890             url : this.form.progressUrl,
48891             params: {
48892                 id : uid
48893             },
48894             method: 'GET',
48895             success : function(req){
48896                //console.log(data);
48897                 var rdata = false;
48898                 var edata;
48899                 try  {
48900                    rdata = Roo.decode(req.responseText)
48901                 } catch (e) {
48902                     Roo.log("Invalid data from server..");
48903                     Roo.log(edata);
48904                     return;
48905                 }
48906                 if (!rdata || !rdata.success) {
48907                     Roo.log(rdata);
48908                     Roo.MessageBox.alert(Roo.encode(rdata));
48909                     return;
48910                 }
48911                 var data = rdata.data;
48912                 
48913                 if (this.uploadComplete) {
48914                    Roo.MessageBox.hide();
48915                    return;
48916                 }
48917                    
48918                 if (data){
48919                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
48920                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
48921                     );
48922                 }
48923                 this.uploadProgress.defer(2000,this);
48924             },
48925        
48926             failure: function(data) {
48927                 Roo.log('progress url failed ');
48928                 Roo.log(data);
48929             },
48930             scope : this
48931         });
48932            
48933     },
48934     
48935     
48936     run : function()
48937     {
48938         // run get Values on the form, so it syncs any secondary forms.
48939         this.form.getValues();
48940         
48941         var o = this.options;
48942         var method = this.getMethod();
48943         var isPost = method == 'POST';
48944         if(o.clientValidation === false || this.form.isValid()){
48945             
48946             if (this.form.progressUrl) {
48947                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
48948                     (new Date() * 1) + '' + Math.random());
48949                     
48950             } 
48951             
48952             
48953             Roo.Ajax.request(Roo.apply(this.createCallback(), {
48954                 form:this.form.el.dom,
48955                 url:this.getUrl(!isPost),
48956                 method: method,
48957                 params:isPost ? this.getParams() : null,
48958                 isUpload: this.form.fileUpload,
48959                 formData : this.form.formData
48960             }));
48961             
48962             this.uploadProgress();
48963
48964         }else if (o.clientValidation !== false){ // client validation failed
48965             this.failureType = Roo.form.Action.CLIENT_INVALID;
48966             this.form.afterAction(this, false);
48967         }
48968     },
48969
48970     success : function(response)
48971     {
48972         this.uploadComplete= true;
48973         if (this.haveProgress) {
48974             Roo.MessageBox.hide();
48975         }
48976         
48977         
48978         var result = this.processResponse(response);
48979         if(result === true || result.success){
48980             this.form.afterAction(this, true);
48981             return;
48982         }
48983         if(result.errors){
48984             this.form.markInvalid(result.errors);
48985             this.failureType = Roo.form.Action.SERVER_INVALID;
48986         }
48987         this.form.afterAction(this, false);
48988     },
48989     failure : function(response)
48990     {
48991         this.uploadComplete= true;
48992         if (this.haveProgress) {
48993             Roo.MessageBox.hide();
48994         }
48995         
48996         this.response = response;
48997         this.failureType = Roo.form.Action.CONNECT_FAILURE;
48998         this.form.afterAction(this, false);
48999     },
49000     
49001     handleResponse : function(response){
49002         if(this.form.errorReader){
49003             var rs = this.form.errorReader.read(response);
49004             var errors = [];
49005             if(rs.records){
49006                 for(var i = 0, len = rs.records.length; i < len; i++) {
49007                     var r = rs.records[i];
49008                     errors[i] = r.data;
49009                 }
49010             }
49011             if(errors.length < 1){
49012                 errors = null;
49013             }
49014             return {
49015                 success : rs.success,
49016                 errors : errors
49017             };
49018         }
49019         var ret = false;
49020         try {
49021             ret = Roo.decode(response.responseText);
49022         } catch (e) {
49023             ret = {
49024                 success: false,
49025                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
49026                 errors : []
49027             };
49028         }
49029         return ret;
49030         
49031     }
49032 });
49033
49034
49035 Roo.form.Action.Load = function(form, options){
49036     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
49037     this.reader = this.form.reader;
49038 };
49039
49040 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
49041     type : 'load',
49042
49043     run : function(){
49044         
49045         Roo.Ajax.request(Roo.apply(
49046                 this.createCallback(), {
49047                     method:this.getMethod(),
49048                     url:this.getUrl(false),
49049                     params:this.getParams()
49050         }));
49051     },
49052
49053     success : function(response){
49054         
49055         var result = this.processResponse(response);
49056         if(result === true || !result.success || !result.data){
49057             this.failureType = Roo.form.Action.LOAD_FAILURE;
49058             this.form.afterAction(this, false);
49059             return;
49060         }
49061         this.form.clearInvalid();
49062         this.form.setValues(result.data);
49063         this.form.afterAction(this, true);
49064     },
49065
49066     handleResponse : function(response){
49067         if(this.form.reader){
49068             var rs = this.form.reader.read(response);
49069             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
49070             return {
49071                 success : rs.success,
49072                 data : data
49073             };
49074         }
49075         return Roo.decode(response.responseText);
49076     }
49077 });
49078
49079 Roo.form.Action.ACTION_TYPES = {
49080     'load' : Roo.form.Action.Load,
49081     'submit' : Roo.form.Action.Submit
49082 };/*
49083  * Based on:
49084  * Ext JS Library 1.1.1
49085  * Copyright(c) 2006-2007, Ext JS, LLC.
49086  *
49087  * Originally Released Under LGPL - original licence link has changed is not relivant.
49088  *
49089  * Fork - LGPL
49090  * <script type="text/javascript">
49091  */
49092  
49093 /**
49094  * @class Roo.form.Layout
49095  * @extends Roo.Component
49096  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
49097  * @constructor
49098  * @param {Object} config Configuration options
49099  */
49100 Roo.form.Layout = function(config){
49101     var xitems = [];
49102     if (config.items) {
49103         xitems = config.items;
49104         delete config.items;
49105     }
49106     Roo.form.Layout.superclass.constructor.call(this, config);
49107     this.stack = [];
49108     Roo.each(xitems, this.addxtype, this);
49109      
49110 };
49111
49112 Roo.extend(Roo.form.Layout, Roo.Component, {
49113     /**
49114      * @cfg {String/Object} autoCreate
49115      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
49116      */
49117     /**
49118      * @cfg {String/Object/Function} style
49119      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
49120      * a function which returns such a specification.
49121      */
49122     /**
49123      * @cfg {String} labelAlign
49124      * Valid values are "left," "top" and "right" (defaults to "left")
49125      */
49126     /**
49127      * @cfg {Number} labelWidth
49128      * Fixed width in pixels of all field labels (defaults to undefined)
49129      */
49130     /**
49131      * @cfg {Boolean} clear
49132      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
49133      */
49134     clear : true,
49135     /**
49136      * @cfg {String} labelSeparator
49137      * The separator to use after field labels (defaults to ':')
49138      */
49139     labelSeparator : ':',
49140     /**
49141      * @cfg {Boolean} hideLabels
49142      * True to suppress the display of field labels in this layout (defaults to false)
49143      */
49144     hideLabels : false,
49145
49146     // private
49147     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
49148     
49149     isLayout : true,
49150     
49151     // private
49152     onRender : function(ct, position){
49153         if(this.el){ // from markup
49154             this.el = Roo.get(this.el);
49155         }else {  // generate
49156             var cfg = this.getAutoCreate();
49157             this.el = ct.createChild(cfg, position);
49158         }
49159         if(this.style){
49160             this.el.applyStyles(this.style);
49161         }
49162         if(this.labelAlign){
49163             this.el.addClass('x-form-label-'+this.labelAlign);
49164         }
49165         if(this.hideLabels){
49166             this.labelStyle = "display:none";
49167             this.elementStyle = "padding-left:0;";
49168         }else{
49169             if(typeof this.labelWidth == 'number'){
49170                 this.labelStyle = "width:"+this.labelWidth+"px;";
49171                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
49172             }
49173             if(this.labelAlign == 'top'){
49174                 this.labelStyle = "width:auto;";
49175                 this.elementStyle = "padding-left:0;";
49176             }
49177         }
49178         var stack = this.stack;
49179         var slen = stack.length;
49180         if(slen > 0){
49181             if(!this.fieldTpl){
49182                 var t = new Roo.Template(
49183                     '<div class="x-form-item {5}">',
49184                         '<label for="{0}" style="{2}">{1}{4}</label>',
49185                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
49186                         '</div>',
49187                     '</div><div class="x-form-clear-left"></div>'
49188                 );
49189                 t.disableFormats = true;
49190                 t.compile();
49191                 Roo.form.Layout.prototype.fieldTpl = t;
49192             }
49193             for(var i = 0; i < slen; i++) {
49194                 if(stack[i].isFormField){
49195                     this.renderField(stack[i]);
49196                 }else{
49197                     this.renderComponent(stack[i]);
49198                 }
49199             }
49200         }
49201         if(this.clear){
49202             this.el.createChild({cls:'x-form-clear'});
49203         }
49204     },
49205
49206     // private
49207     renderField : function(f){
49208         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
49209                f.id, //0
49210                f.fieldLabel, //1
49211                f.labelStyle||this.labelStyle||'', //2
49212                this.elementStyle||'', //3
49213                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
49214                f.itemCls||this.itemCls||''  //5
49215        ], true).getPrevSibling());
49216     },
49217
49218     // private
49219     renderComponent : function(c){
49220         c.render(c.isLayout ? this.el : this.el.createChild());    
49221     },
49222     /**
49223      * Adds a object form elements (using the xtype property as the factory method.)
49224      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
49225      * @param {Object} config 
49226      */
49227     addxtype : function(o)
49228     {
49229         // create the lement.
49230         o.form = this.form;
49231         var fe = Roo.factory(o, Roo.form);
49232         this.form.allItems.push(fe);
49233         this.stack.push(fe);
49234         
49235         if (fe.isFormField) {
49236             this.form.items.add(fe);
49237         }
49238          
49239         return fe;
49240     }
49241 });
49242
49243 /**
49244  * @class Roo.form.Column
49245  * @extends Roo.form.Layout
49246  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
49247  * @constructor
49248  * @param {Object} config Configuration options
49249  */
49250 Roo.form.Column = function(config){
49251     Roo.form.Column.superclass.constructor.call(this, config);
49252 };
49253
49254 Roo.extend(Roo.form.Column, Roo.form.Layout, {
49255     /**
49256      * @cfg {Number/String} width
49257      * The fixed width of the column in pixels or CSS value (defaults to "auto")
49258      */
49259     /**
49260      * @cfg {String/Object} autoCreate
49261      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
49262      */
49263
49264     // private
49265     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
49266
49267     // private
49268     onRender : function(ct, position){
49269         Roo.form.Column.superclass.onRender.call(this, ct, position);
49270         if(this.width){
49271             this.el.setWidth(this.width);
49272         }
49273     }
49274 });
49275
49276
49277 /**
49278  * @class Roo.form.Row
49279  * @extends Roo.form.Layout
49280  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
49281  * @constructor
49282  * @param {Object} config Configuration options
49283  */
49284
49285  
49286 Roo.form.Row = function(config){
49287     Roo.form.Row.superclass.constructor.call(this, config);
49288 };
49289  
49290 Roo.extend(Roo.form.Row, Roo.form.Layout, {
49291       /**
49292      * @cfg {Number/String} width
49293      * The fixed width of the column in pixels or CSS value (defaults to "auto")
49294      */
49295     /**
49296      * @cfg {Number/String} height
49297      * The fixed height of the column in pixels or CSS value (defaults to "auto")
49298      */
49299     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
49300     
49301     padWidth : 20,
49302     // private
49303     onRender : function(ct, position){
49304         //console.log('row render');
49305         if(!this.rowTpl){
49306             var t = new Roo.Template(
49307                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
49308                     '<label for="{0}" style="{2}">{1}{4}</label>',
49309                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
49310                     '</div>',
49311                 '</div>'
49312             );
49313             t.disableFormats = true;
49314             t.compile();
49315             Roo.form.Layout.prototype.rowTpl = t;
49316         }
49317         this.fieldTpl = this.rowTpl;
49318         
49319         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
49320         var labelWidth = 100;
49321         
49322         if ((this.labelAlign != 'top')) {
49323             if (typeof this.labelWidth == 'number') {
49324                 labelWidth = this.labelWidth
49325             }
49326             this.padWidth =  20 + labelWidth;
49327             
49328         }
49329         
49330         Roo.form.Column.superclass.onRender.call(this, ct, position);
49331         if(this.width){
49332             this.el.setWidth(this.width);
49333         }
49334         if(this.height){
49335             this.el.setHeight(this.height);
49336         }
49337     },
49338     
49339     // private
49340     renderField : function(f){
49341         f.fieldEl = this.fieldTpl.append(this.el, [
49342                f.id, f.fieldLabel,
49343                f.labelStyle||this.labelStyle||'',
49344                this.elementStyle||'',
49345                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
49346                f.itemCls||this.itemCls||'',
49347                f.width ? f.width + this.padWidth : 160 + this.padWidth
49348        ],true);
49349     }
49350 });
49351  
49352
49353 /**
49354  * @class Roo.form.FieldSet
49355  * @extends Roo.form.Layout
49356  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
49357  * @constructor
49358  * @param {Object} config Configuration options
49359  */
49360 Roo.form.FieldSet = function(config){
49361     Roo.form.FieldSet.superclass.constructor.call(this, config);
49362 };
49363
49364 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
49365     /**
49366      * @cfg {String} legend
49367      * The text to display as the legend for the FieldSet (defaults to '')
49368      */
49369     /**
49370      * @cfg {String/Object} autoCreate
49371      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
49372      */
49373
49374     // private
49375     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
49376
49377     // private
49378     onRender : function(ct, position){
49379         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
49380         if(this.legend){
49381             this.setLegend(this.legend);
49382         }
49383     },
49384
49385     // private
49386     setLegend : function(text){
49387         if(this.rendered){
49388             this.el.child('legend').update(text);
49389         }
49390     }
49391 });/*
49392  * Based on:
49393  * Ext JS Library 1.1.1
49394  * Copyright(c) 2006-2007, Ext JS, LLC.
49395  *
49396  * Originally Released Under LGPL - original licence link has changed is not relivant.
49397  *
49398  * Fork - LGPL
49399  * <script type="text/javascript">
49400  */
49401 /**
49402  * @class Roo.form.VTypes
49403  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
49404  * @singleton
49405  */
49406 Roo.form.VTypes = function(){
49407     // closure these in so they are only created once.
49408     var alpha = /^[a-zA-Z_]+$/;
49409     var alphanum = /^[a-zA-Z0-9_]+$/;
49410     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
49411     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
49412
49413     // All these messages and functions are configurable
49414     return {
49415         /**
49416          * The function used to validate email addresses
49417          * @param {String} value The email address
49418          */
49419         'email' : function(v){
49420             return email.test(v);
49421         },
49422         /**
49423          * The error text to display when the email validation function returns false
49424          * @type String
49425          */
49426         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
49427         /**
49428          * The keystroke filter mask to be applied on email input
49429          * @type RegExp
49430          */
49431         'emailMask' : /[a-z0-9_\.\-@]/i,
49432
49433         /**
49434          * The function used to validate URLs
49435          * @param {String} value The URL
49436          */
49437         'url' : function(v){
49438             return url.test(v);
49439         },
49440         /**
49441          * The error text to display when the url validation function returns false
49442          * @type String
49443          */
49444         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
49445         
49446         /**
49447          * The function used to validate alpha values
49448          * @param {String} value The value
49449          */
49450         'alpha' : function(v){
49451             return alpha.test(v);
49452         },
49453         /**
49454          * The error text to display when the alpha validation function returns false
49455          * @type String
49456          */
49457         'alphaText' : 'This field should only contain letters and _',
49458         /**
49459          * The keystroke filter mask to be applied on alpha input
49460          * @type RegExp
49461          */
49462         'alphaMask' : /[a-z_]/i,
49463
49464         /**
49465          * The function used to validate alphanumeric values
49466          * @param {String} value The value
49467          */
49468         'alphanum' : function(v){
49469             return alphanum.test(v);
49470         },
49471         /**
49472          * The error text to display when the alphanumeric validation function returns false
49473          * @type String
49474          */
49475         'alphanumText' : 'This field should only contain letters, numbers and _',
49476         /**
49477          * The keystroke filter mask to be applied on alphanumeric input
49478          * @type RegExp
49479          */
49480         'alphanumMask' : /[a-z0-9_]/i
49481     };
49482 }();//<script type="text/javascript">
49483
49484 /**
49485  * @class Roo.form.FCKeditor
49486  * @extends Roo.form.TextArea
49487  * Wrapper around the FCKEditor http://www.fckeditor.net
49488  * @constructor
49489  * Creates a new FCKeditor
49490  * @param {Object} config Configuration options
49491  */
49492 Roo.form.FCKeditor = function(config){
49493     Roo.form.FCKeditor.superclass.constructor.call(this, config);
49494     this.addEvents({
49495          /**
49496          * @event editorinit
49497          * Fired when the editor is initialized - you can add extra handlers here..
49498          * @param {FCKeditor} this
49499          * @param {Object} the FCK object.
49500          */
49501         editorinit : true
49502     });
49503     
49504     
49505 };
49506 Roo.form.FCKeditor.editors = { };
49507 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
49508 {
49509     //defaultAutoCreate : {
49510     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
49511     //},
49512     // private
49513     /**
49514      * @cfg {Object} fck options - see fck manual for details.
49515      */
49516     fckconfig : false,
49517     
49518     /**
49519      * @cfg {Object} fck toolbar set (Basic or Default)
49520      */
49521     toolbarSet : 'Basic',
49522     /**
49523      * @cfg {Object} fck BasePath
49524      */ 
49525     basePath : '/fckeditor/',
49526     
49527     
49528     frame : false,
49529     
49530     value : '',
49531     
49532    
49533     onRender : function(ct, position)
49534     {
49535         if(!this.el){
49536             this.defaultAutoCreate = {
49537                 tag: "textarea",
49538                 style:"width:300px;height:60px;",
49539                 autocomplete: "new-password"
49540             };
49541         }
49542         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
49543         /*
49544         if(this.grow){
49545             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
49546             if(this.preventScrollbars){
49547                 this.el.setStyle("overflow", "hidden");
49548             }
49549             this.el.setHeight(this.growMin);
49550         }
49551         */
49552         //console.log('onrender' + this.getId() );
49553         Roo.form.FCKeditor.editors[this.getId()] = this;
49554          
49555
49556         this.replaceTextarea() ;
49557         
49558     },
49559     
49560     getEditor : function() {
49561         return this.fckEditor;
49562     },
49563     /**
49564      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
49565      * @param {Mixed} value The value to set
49566      */
49567     
49568     
49569     setValue : function(value)
49570     {
49571         //console.log('setValue: ' + value);
49572         
49573         if(typeof(value) == 'undefined') { // not sure why this is happending...
49574             return;
49575         }
49576         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
49577         
49578         //if(!this.el || !this.getEditor()) {
49579         //    this.value = value;
49580             //this.setValue.defer(100,this,[value]);    
49581         //    return;
49582         //} 
49583         
49584         if(!this.getEditor()) {
49585             return;
49586         }
49587         
49588         this.getEditor().SetData(value);
49589         
49590         //
49591
49592     },
49593
49594     /**
49595      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
49596      * @return {Mixed} value The field value
49597      */
49598     getValue : function()
49599     {
49600         
49601         if (this.frame && this.frame.dom.style.display == 'none') {
49602             return Roo.form.FCKeditor.superclass.getValue.call(this);
49603         }
49604         
49605         if(!this.el || !this.getEditor()) {
49606            
49607            // this.getValue.defer(100,this); 
49608             return this.value;
49609         }
49610        
49611         
49612         var value=this.getEditor().GetData();
49613         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
49614         return Roo.form.FCKeditor.superclass.getValue.call(this);
49615         
49616
49617     },
49618
49619     /**
49620      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
49621      * @return {Mixed} value The field value
49622      */
49623     getRawValue : function()
49624     {
49625         if (this.frame && this.frame.dom.style.display == 'none') {
49626             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
49627         }
49628         
49629         if(!this.el || !this.getEditor()) {
49630             //this.getRawValue.defer(100,this); 
49631             return this.value;
49632             return;
49633         }
49634         
49635         
49636         
49637         var value=this.getEditor().GetData();
49638         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
49639         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
49640          
49641     },
49642     
49643     setSize : function(w,h) {
49644         
49645         
49646         
49647         //if (this.frame && this.frame.dom.style.display == 'none') {
49648         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
49649         //    return;
49650         //}
49651         //if(!this.el || !this.getEditor()) {
49652         //    this.setSize.defer(100,this, [w,h]); 
49653         //    return;
49654         //}
49655         
49656         
49657         
49658         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
49659         
49660         this.frame.dom.setAttribute('width', w);
49661         this.frame.dom.setAttribute('height', h);
49662         this.frame.setSize(w,h);
49663         
49664     },
49665     
49666     toggleSourceEdit : function(value) {
49667         
49668       
49669          
49670         this.el.dom.style.display = value ? '' : 'none';
49671         this.frame.dom.style.display = value ?  'none' : '';
49672         
49673     },
49674     
49675     
49676     focus: function(tag)
49677     {
49678         if (this.frame.dom.style.display == 'none') {
49679             return Roo.form.FCKeditor.superclass.focus.call(this);
49680         }
49681         if(!this.el || !this.getEditor()) {
49682             this.focus.defer(100,this, [tag]); 
49683             return;
49684         }
49685         
49686         
49687         
49688         
49689         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
49690         this.getEditor().Focus();
49691         if (tgs.length) {
49692             if (!this.getEditor().Selection.GetSelection()) {
49693                 this.focus.defer(100,this, [tag]); 
49694                 return;
49695             }
49696             
49697             
49698             var r = this.getEditor().EditorDocument.createRange();
49699             r.setStart(tgs[0],0);
49700             r.setEnd(tgs[0],0);
49701             this.getEditor().Selection.GetSelection().removeAllRanges();
49702             this.getEditor().Selection.GetSelection().addRange(r);
49703             this.getEditor().Focus();
49704         }
49705         
49706     },
49707     
49708     
49709     
49710     replaceTextarea : function()
49711     {
49712         if ( document.getElementById( this.getId() + '___Frame' ) ) {
49713             return ;
49714         }
49715         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
49716         //{
49717             // We must check the elements firstly using the Id and then the name.
49718         var oTextarea = document.getElementById( this.getId() );
49719         
49720         var colElementsByName = document.getElementsByName( this.getId() ) ;
49721          
49722         oTextarea.style.display = 'none' ;
49723
49724         if ( oTextarea.tabIndex ) {            
49725             this.TabIndex = oTextarea.tabIndex ;
49726         }
49727         
49728         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
49729         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
49730         this.frame = Roo.get(this.getId() + '___Frame')
49731     },
49732     
49733     _getConfigHtml : function()
49734     {
49735         var sConfig = '' ;
49736
49737         for ( var o in this.fckconfig ) {
49738             sConfig += sConfig.length > 0  ? '&amp;' : '';
49739             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
49740         }
49741
49742         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
49743     },
49744     
49745     
49746     _getIFrameHtml : function()
49747     {
49748         var sFile = 'fckeditor.html' ;
49749         /* no idea what this is about..
49750         try
49751         {
49752             if ( (/fcksource=true/i).test( window.top.location.search ) )
49753                 sFile = 'fckeditor.original.html' ;
49754         }
49755         catch (e) { 
49756         */
49757
49758         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
49759         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
49760         
49761         
49762         var html = '<iframe id="' + this.getId() +
49763             '___Frame" src="' + sLink +
49764             '" width="' + this.width +
49765             '" height="' + this.height + '"' +
49766             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
49767             ' frameborder="0" scrolling="no"></iframe>' ;
49768
49769         return html ;
49770     },
49771     
49772     _insertHtmlBefore : function( html, element )
49773     {
49774         if ( element.insertAdjacentHTML )       {
49775             // IE
49776             element.insertAdjacentHTML( 'beforeBegin', html ) ;
49777         } else { // Gecko
49778             var oRange = document.createRange() ;
49779             oRange.setStartBefore( element ) ;
49780             var oFragment = oRange.createContextualFragment( html );
49781             element.parentNode.insertBefore( oFragment, element ) ;
49782         }
49783     }
49784     
49785     
49786   
49787     
49788     
49789     
49790     
49791
49792 });
49793
49794 //Roo.reg('fckeditor', Roo.form.FCKeditor);
49795
49796 function FCKeditor_OnComplete(editorInstance){
49797     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
49798     f.fckEditor = editorInstance;
49799     //console.log("loaded");
49800     f.fireEvent('editorinit', f, editorInstance);
49801
49802   
49803
49804  
49805
49806
49807
49808
49809
49810
49811
49812
49813
49814
49815
49816
49817
49818
49819
49820 //<script type="text/javascript">
49821 /**
49822  * @class Roo.form.GridField
49823  * @extends Roo.form.Field
49824  * Embed a grid (or editable grid into a form)
49825  * STATUS ALPHA
49826  * 
49827  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
49828  * it needs 
49829  * xgrid.store = Roo.data.Store
49830  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
49831  * xgrid.store.reader = Roo.data.JsonReader 
49832  * 
49833  * 
49834  * @constructor
49835  * Creates a new GridField
49836  * @param {Object} config Configuration options
49837  */
49838 Roo.form.GridField = function(config){
49839     Roo.form.GridField.superclass.constructor.call(this, config);
49840      
49841 };
49842
49843 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
49844     /**
49845      * @cfg {Number} width  - used to restrict width of grid..
49846      */
49847     width : 100,
49848     /**
49849      * @cfg {Number} height - used to restrict height of grid..
49850      */
49851     height : 50,
49852      /**
49853      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
49854          * 
49855          *}
49856      */
49857     xgrid : false, 
49858     /**
49859      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
49860      * {tag: "input", type: "checkbox", autocomplete: "off"})
49861      */
49862    // defaultAutoCreate : { tag: 'div' },
49863     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
49864     /**
49865      * @cfg {String} addTitle Text to include for adding a title.
49866      */
49867     addTitle : false,
49868     //
49869     onResize : function(){
49870         Roo.form.Field.superclass.onResize.apply(this, arguments);
49871     },
49872
49873     initEvents : function(){
49874         // Roo.form.Checkbox.superclass.initEvents.call(this);
49875         // has no events...
49876        
49877     },
49878
49879
49880     getResizeEl : function(){
49881         return this.wrap;
49882     },
49883
49884     getPositionEl : function(){
49885         return this.wrap;
49886     },
49887
49888     // private
49889     onRender : function(ct, position){
49890         
49891         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
49892         var style = this.style;
49893         delete this.style;
49894         
49895         Roo.form.GridField.superclass.onRender.call(this, ct, position);
49896         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
49897         this.viewEl = this.wrap.createChild({ tag: 'div' });
49898         if (style) {
49899             this.viewEl.applyStyles(style);
49900         }
49901         if (this.width) {
49902             this.viewEl.setWidth(this.width);
49903         }
49904         if (this.height) {
49905             this.viewEl.setHeight(this.height);
49906         }
49907         //if(this.inputValue !== undefined){
49908         //this.setValue(this.value);
49909         
49910         
49911         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
49912         
49913         
49914         this.grid.render();
49915         this.grid.getDataSource().on('remove', this.refreshValue, this);
49916         this.grid.getDataSource().on('update', this.refreshValue, this);
49917         this.grid.on('afteredit', this.refreshValue, this);
49918  
49919     },
49920      
49921     
49922     /**
49923      * Sets the value of the item. 
49924      * @param {String} either an object  or a string..
49925      */
49926     setValue : function(v){
49927         //this.value = v;
49928         v = v || []; // empty set..
49929         // this does not seem smart - it really only affects memoryproxy grids..
49930         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
49931             var ds = this.grid.getDataSource();
49932             // assumes a json reader..
49933             var data = {}
49934             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
49935             ds.loadData( data);
49936         }
49937         // clear selection so it does not get stale.
49938         if (this.grid.sm) { 
49939             this.grid.sm.clearSelections();
49940         }
49941         
49942         Roo.form.GridField.superclass.setValue.call(this, v);
49943         this.refreshValue();
49944         // should load data in the grid really....
49945     },
49946     
49947     // private
49948     refreshValue: function() {
49949          var val = [];
49950         this.grid.getDataSource().each(function(r) {
49951             val.push(r.data);
49952         });
49953         this.el.dom.value = Roo.encode(val);
49954     }
49955     
49956      
49957     
49958     
49959 });/*
49960  * Based on:
49961  * Ext JS Library 1.1.1
49962  * Copyright(c) 2006-2007, Ext JS, LLC.
49963  *
49964  * Originally Released Under LGPL - original licence link has changed is not relivant.
49965  *
49966  * Fork - LGPL
49967  * <script type="text/javascript">
49968  */
49969 /**
49970  * @class Roo.form.DisplayField
49971  * @extends Roo.form.Field
49972  * A generic Field to display non-editable data.
49973  * @cfg {Boolean} closable (true|false) default false
49974  * @constructor
49975  * Creates a new Display Field item.
49976  * @param {Object} config Configuration options
49977  */
49978 Roo.form.DisplayField = function(config){
49979     Roo.form.DisplayField.superclass.constructor.call(this, config);
49980     
49981     this.addEvents({
49982         /**
49983          * @event close
49984          * Fires after the click the close btn
49985              * @param {Roo.form.DisplayField} this
49986              */
49987         close : true
49988     });
49989 };
49990
49991 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
49992     inputType:      'hidden',
49993     allowBlank:     true,
49994     readOnly:         true,
49995     
49996  
49997     /**
49998      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
49999      */
50000     focusClass : undefined,
50001     /**
50002      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
50003      */
50004     fieldClass: 'x-form-field',
50005     
50006      /**
50007      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
50008      */
50009     valueRenderer: undefined,
50010     
50011     width: 100,
50012     /**
50013      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
50014      * {tag: "input", type: "checkbox", autocomplete: "off"})
50015      */
50016      
50017  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
50018  
50019     closable : false,
50020     
50021     onResize : function(){
50022         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
50023         
50024     },
50025
50026     initEvents : function(){
50027         // Roo.form.Checkbox.superclass.initEvents.call(this);
50028         // has no events...
50029         
50030         if(this.closable){
50031             this.closeEl.on('click', this.onClose, this);
50032         }
50033        
50034     },
50035
50036
50037     getResizeEl : function(){
50038         return this.wrap;
50039     },
50040
50041     getPositionEl : function(){
50042         return this.wrap;
50043     },
50044
50045     // private
50046     onRender : function(ct, position){
50047         
50048         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
50049         //if(this.inputValue !== undefined){
50050         this.wrap = this.el.wrap();
50051         
50052         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
50053         
50054         if(this.closable){
50055             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
50056         }
50057         
50058         if (this.bodyStyle) {
50059             this.viewEl.applyStyles(this.bodyStyle);
50060         }
50061         //this.viewEl.setStyle('padding', '2px');
50062         
50063         this.setValue(this.value);
50064         
50065     },
50066 /*
50067     // private
50068     initValue : Roo.emptyFn,
50069
50070   */
50071
50072         // private
50073     onClick : function(){
50074         
50075     },
50076
50077     /**
50078      * Sets the checked state of the checkbox.
50079      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
50080      */
50081     setValue : function(v){
50082         this.value = v;
50083         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
50084         // this might be called before we have a dom element..
50085         if (!this.viewEl) {
50086             return;
50087         }
50088         this.viewEl.dom.innerHTML = html;
50089         Roo.form.DisplayField.superclass.setValue.call(this, v);
50090
50091     },
50092     
50093     onClose : function(e)
50094     {
50095         e.preventDefault();
50096         
50097         this.fireEvent('close', this);
50098     }
50099 });/*
50100  * 
50101  * Licence- LGPL
50102  * 
50103  */
50104
50105 /**
50106  * @class Roo.form.DayPicker
50107  * @extends Roo.form.Field
50108  * A Day picker show [M] [T] [W] ....
50109  * @constructor
50110  * Creates a new Day Picker
50111  * @param {Object} config Configuration options
50112  */
50113 Roo.form.DayPicker= function(config){
50114     Roo.form.DayPicker.superclass.constructor.call(this, config);
50115      
50116 };
50117
50118 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
50119     /**
50120      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
50121      */
50122     focusClass : undefined,
50123     /**
50124      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
50125      */
50126     fieldClass: "x-form-field",
50127    
50128     /**
50129      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
50130      * {tag: "input", type: "checkbox", autocomplete: "off"})
50131      */
50132     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
50133     
50134    
50135     actionMode : 'viewEl', 
50136     //
50137     // private
50138  
50139     inputType : 'hidden',
50140     
50141      
50142     inputElement: false, // real input element?
50143     basedOn: false, // ????
50144     
50145     isFormField: true, // not sure where this is needed!!!!
50146
50147     onResize : function(){
50148         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
50149         if(!this.boxLabel){
50150             this.el.alignTo(this.wrap, 'c-c');
50151         }
50152     },
50153
50154     initEvents : function(){
50155         Roo.form.Checkbox.superclass.initEvents.call(this);
50156         this.el.on("click", this.onClick,  this);
50157         this.el.on("change", this.onClick,  this);
50158     },
50159
50160
50161     getResizeEl : function(){
50162         return this.wrap;
50163     },
50164
50165     getPositionEl : function(){
50166         return this.wrap;
50167     },
50168
50169     
50170     // private
50171     onRender : function(ct, position){
50172         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
50173        
50174         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
50175         
50176         var r1 = '<table><tr>';
50177         var r2 = '<tr class="x-form-daypick-icons">';
50178         for (var i=0; i < 7; i++) {
50179             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
50180             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
50181         }
50182         
50183         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
50184         viewEl.select('img').on('click', this.onClick, this);
50185         this.viewEl = viewEl;   
50186         
50187         
50188         // this will not work on Chrome!!!
50189         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
50190         this.el.on('propertychange', this.setFromHidden,  this);  //ie
50191         
50192         
50193           
50194
50195     },
50196
50197     // private
50198     initValue : Roo.emptyFn,
50199
50200     /**
50201      * Returns the checked state of the checkbox.
50202      * @return {Boolean} True if checked, else false
50203      */
50204     getValue : function(){
50205         return this.el.dom.value;
50206         
50207     },
50208
50209         // private
50210     onClick : function(e){ 
50211         //this.setChecked(!this.checked);
50212         Roo.get(e.target).toggleClass('x-menu-item-checked');
50213         this.refreshValue();
50214         //if(this.el.dom.checked != this.checked){
50215         //    this.setValue(this.el.dom.checked);
50216        // }
50217     },
50218     
50219     // private
50220     refreshValue : function()
50221     {
50222         var val = '';
50223         this.viewEl.select('img',true).each(function(e,i,n)  {
50224             val += e.is(".x-menu-item-checked") ? String(n) : '';
50225         });
50226         this.setValue(val, true);
50227     },
50228
50229     /**
50230      * Sets the checked state of the checkbox.
50231      * On is always based on a string comparison between inputValue and the param.
50232      * @param {Boolean/String} value - the value to set 
50233      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
50234      */
50235     setValue : function(v,suppressEvent){
50236         if (!this.el.dom) {
50237             return;
50238         }
50239         var old = this.el.dom.value ;
50240         this.el.dom.value = v;
50241         if (suppressEvent) {
50242             return ;
50243         }
50244          
50245         // update display..
50246         this.viewEl.select('img',true).each(function(e,i,n)  {
50247             
50248             var on = e.is(".x-menu-item-checked");
50249             var newv = v.indexOf(String(n)) > -1;
50250             if (on != newv) {
50251                 e.toggleClass('x-menu-item-checked');
50252             }
50253             
50254         });
50255         
50256         
50257         this.fireEvent('change', this, v, old);
50258         
50259         
50260     },
50261    
50262     // handle setting of hidden value by some other method!!?!?
50263     setFromHidden: function()
50264     {
50265         if(!this.el){
50266             return;
50267         }
50268         //console.log("SET FROM HIDDEN");
50269         //alert('setFrom hidden');
50270         this.setValue(this.el.dom.value);
50271     },
50272     
50273     onDestroy : function()
50274     {
50275         if(this.viewEl){
50276             Roo.get(this.viewEl).remove();
50277         }
50278          
50279         Roo.form.DayPicker.superclass.onDestroy.call(this);
50280     }
50281
50282 });/*
50283  * RooJS Library 1.1.1
50284  * Copyright(c) 2008-2011  Alan Knowles
50285  *
50286  * License - LGPL
50287  */
50288  
50289
50290 /**
50291  * @class Roo.form.ComboCheck
50292  * @extends Roo.form.ComboBox
50293  * A combobox for multiple select items.
50294  *
50295  * FIXME - could do with a reset button..
50296  * 
50297  * @constructor
50298  * Create a new ComboCheck
50299  * @param {Object} config Configuration options
50300  */
50301 Roo.form.ComboCheck = function(config){
50302     Roo.form.ComboCheck.superclass.constructor.call(this, config);
50303     // should verify some data...
50304     // like
50305     // hiddenName = required..
50306     // displayField = required
50307     // valudField == required
50308     var req= [ 'hiddenName', 'displayField', 'valueField' ];
50309     var _t = this;
50310     Roo.each(req, function(e) {
50311         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
50312             throw "Roo.form.ComboCheck : missing value for: " + e;
50313         }
50314     });
50315     
50316     
50317 };
50318
50319 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
50320      
50321      
50322     editable : false,
50323      
50324     selectedClass: 'x-menu-item-checked', 
50325     
50326     // private
50327     onRender : function(ct, position){
50328         var _t = this;
50329         
50330         
50331         
50332         if(!this.tpl){
50333             var cls = 'x-combo-list';
50334
50335             
50336             this.tpl =  new Roo.Template({
50337                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
50338                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
50339                    '<span>{' + this.displayField + '}</span>' +
50340                     '</div>' 
50341                 
50342             });
50343         }
50344  
50345         
50346         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
50347         this.view.singleSelect = false;
50348         this.view.multiSelect = true;
50349         this.view.toggleSelect = true;
50350         this.pageTb.add(new Roo.Toolbar.Fill(), {
50351             
50352             text: 'Done',
50353             handler: function()
50354             {
50355                 _t.collapse();
50356             }
50357         });
50358     },
50359     
50360     onViewOver : function(e, t){
50361         // do nothing...
50362         return;
50363         
50364     },
50365     
50366     onViewClick : function(doFocus,index){
50367         return;
50368         
50369     },
50370     select: function () {
50371         //Roo.log("SELECT CALLED");
50372     },
50373      
50374     selectByValue : function(xv, scrollIntoView){
50375         var ar = this.getValueArray();
50376         var sels = [];
50377         
50378         Roo.each(ar, function(v) {
50379             if(v === undefined || v === null){
50380                 return;
50381             }
50382             var r = this.findRecord(this.valueField, v);
50383             if(r){
50384                 sels.push(this.store.indexOf(r))
50385                 
50386             }
50387         },this);
50388         this.view.select(sels);
50389         return false;
50390     },
50391     
50392     
50393     
50394     onSelect : function(record, index){
50395        // Roo.log("onselect Called");
50396        // this is only called by the clear button now..
50397         this.view.clearSelections();
50398         this.setValue('[]');
50399         if (this.value != this.valueBefore) {
50400             this.fireEvent('change', this, this.value, this.valueBefore);
50401             this.valueBefore = this.value;
50402         }
50403     },
50404     getValueArray : function()
50405     {
50406         var ar = [] ;
50407         
50408         try {
50409             //Roo.log(this.value);
50410             if (typeof(this.value) == 'undefined') {
50411                 return [];
50412             }
50413             var ar = Roo.decode(this.value);
50414             return  ar instanceof Array ? ar : []; //?? valid?
50415             
50416         } catch(e) {
50417             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
50418             return [];
50419         }
50420          
50421     },
50422     expand : function ()
50423     {
50424         
50425         Roo.form.ComboCheck.superclass.expand.call(this);
50426         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
50427         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
50428         
50429
50430     },
50431     
50432     collapse : function(){
50433         Roo.form.ComboCheck.superclass.collapse.call(this);
50434         var sl = this.view.getSelectedIndexes();
50435         var st = this.store;
50436         var nv = [];
50437         var tv = [];
50438         var r;
50439         Roo.each(sl, function(i) {
50440             r = st.getAt(i);
50441             nv.push(r.get(this.valueField));
50442         },this);
50443         this.setValue(Roo.encode(nv));
50444         if (this.value != this.valueBefore) {
50445
50446             this.fireEvent('change', this, this.value, this.valueBefore);
50447             this.valueBefore = this.value;
50448         }
50449         
50450     },
50451     
50452     setValue : function(v){
50453         // Roo.log(v);
50454         this.value = v;
50455         
50456         var vals = this.getValueArray();
50457         var tv = [];
50458         Roo.each(vals, function(k) {
50459             var r = this.findRecord(this.valueField, k);
50460             if(r){
50461                 tv.push(r.data[this.displayField]);
50462             }else if(this.valueNotFoundText !== undefined){
50463                 tv.push( this.valueNotFoundText );
50464             }
50465         },this);
50466        // Roo.log(tv);
50467         
50468         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
50469         this.hiddenField.value = v;
50470         this.value = v;
50471     }
50472     
50473 });/*
50474  * Based on:
50475  * Ext JS Library 1.1.1
50476  * Copyright(c) 2006-2007, Ext JS, LLC.
50477  *
50478  * Originally Released Under LGPL - original licence link has changed is not relivant.
50479  *
50480  * Fork - LGPL
50481  * <script type="text/javascript">
50482  */
50483  
50484 /**
50485  * @class Roo.form.Signature
50486  * @extends Roo.form.Field
50487  * Signature field.  
50488  * @constructor
50489  * 
50490  * @param {Object} config Configuration options
50491  */
50492
50493 Roo.form.Signature = function(config){
50494     Roo.form.Signature.superclass.constructor.call(this, config);
50495     
50496     this.addEvents({// not in used??
50497          /**
50498          * @event confirm
50499          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
50500              * @param {Roo.form.Signature} combo This combo box
50501              */
50502         'confirm' : true,
50503         /**
50504          * @event reset
50505          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
50506              * @param {Roo.form.ComboBox} combo This combo box
50507              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
50508              */
50509         'reset' : true
50510     });
50511 };
50512
50513 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
50514     /**
50515      * @cfg {Object} labels Label to use when rendering a form.
50516      * defaults to 
50517      * labels : { 
50518      *      clear : "Clear",
50519      *      confirm : "Confirm"
50520      *  }
50521      */
50522     labels : { 
50523         clear : "Clear",
50524         confirm : "Confirm"
50525     },
50526     /**
50527      * @cfg {Number} width The signature panel width (defaults to 300)
50528      */
50529     width: 300,
50530     /**
50531      * @cfg {Number} height The signature panel height (defaults to 100)
50532      */
50533     height : 100,
50534     /**
50535      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
50536      */
50537     allowBlank : false,
50538     
50539     //private
50540     // {Object} signPanel The signature SVG panel element (defaults to {})
50541     signPanel : {},
50542     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
50543     isMouseDown : false,
50544     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
50545     isConfirmed : false,
50546     // {String} signatureTmp SVG mapping string (defaults to empty string)
50547     signatureTmp : '',
50548     
50549     
50550     defaultAutoCreate : { // modified by initCompnoent..
50551         tag: "input",
50552         type:"hidden"
50553     },
50554
50555     // private
50556     onRender : function(ct, position){
50557         
50558         Roo.form.Signature.superclass.onRender.call(this, ct, position);
50559         
50560         this.wrap = this.el.wrap({
50561             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
50562         });
50563         
50564         this.createToolbar(this);
50565         this.signPanel = this.wrap.createChild({
50566                 tag: 'div',
50567                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
50568             }, this.el
50569         );
50570             
50571         this.svgID = Roo.id();
50572         this.svgEl = this.signPanel.createChild({
50573               xmlns : 'http://www.w3.org/2000/svg',
50574               tag : 'svg',
50575               id : this.svgID + "-svg",
50576               width: this.width,
50577               height: this.height,
50578               viewBox: '0 0 '+this.width+' '+this.height,
50579               cn : [
50580                 {
50581                     tag: "rect",
50582                     id: this.svgID + "-svg-r",
50583                     width: this.width,
50584                     height: this.height,
50585                     fill: "#ffa"
50586                 },
50587                 {
50588                     tag: "line",
50589                     id: this.svgID + "-svg-l",
50590                     x1: "0", // start
50591                     y1: (this.height*0.8), // start set the line in 80% of height
50592                     x2: this.width, // end
50593                     y2: (this.height*0.8), // end set the line in 80% of height
50594                     'stroke': "#666",
50595                     'stroke-width': "1",
50596                     'stroke-dasharray': "3",
50597                     'shape-rendering': "crispEdges",
50598                     'pointer-events': "none"
50599                 },
50600                 {
50601                     tag: "path",
50602                     id: this.svgID + "-svg-p",
50603                     'stroke': "navy",
50604                     'stroke-width': "3",
50605                     'fill': "none",
50606                     'pointer-events': 'none'
50607                 }
50608               ]
50609         });
50610         this.createSVG();
50611         this.svgBox = this.svgEl.dom.getScreenCTM();
50612     },
50613     createSVG : function(){ 
50614         var svg = this.signPanel;
50615         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
50616         var t = this;
50617
50618         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
50619         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
50620         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
50621         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
50622         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
50623         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
50624         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
50625         
50626     },
50627     isTouchEvent : function(e){
50628         return e.type.match(/^touch/);
50629     },
50630     getCoords : function (e) {
50631         var pt    = this.svgEl.dom.createSVGPoint();
50632         pt.x = e.clientX; 
50633         pt.y = e.clientY;
50634         if (this.isTouchEvent(e)) {
50635             pt.x =  e.targetTouches[0].clientX;
50636             pt.y = e.targetTouches[0].clientY;
50637         }
50638         var a = this.svgEl.dom.getScreenCTM();
50639         var b = a.inverse();
50640         var mx = pt.matrixTransform(b);
50641         return mx.x + ',' + mx.y;
50642     },
50643     //mouse event headler 
50644     down : function (e) {
50645         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
50646         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
50647         
50648         this.isMouseDown = true;
50649         
50650         e.preventDefault();
50651     },
50652     move : function (e) {
50653         if (this.isMouseDown) {
50654             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
50655             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
50656         }
50657         
50658         e.preventDefault();
50659     },
50660     up : function (e) {
50661         this.isMouseDown = false;
50662         var sp = this.signatureTmp.split(' ');
50663         
50664         if(sp.length > 1){
50665             if(!sp[sp.length-2].match(/^L/)){
50666                 sp.pop();
50667                 sp.pop();
50668                 sp.push("");
50669                 this.signatureTmp = sp.join(" ");
50670             }
50671         }
50672         if(this.getValue() != this.signatureTmp){
50673             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50674             this.isConfirmed = false;
50675         }
50676         e.preventDefault();
50677     },
50678     
50679     /**
50680      * Protected method that will not generally be called directly. It
50681      * is called when the editor creates its toolbar. Override this method if you need to
50682      * add custom toolbar buttons.
50683      * @param {HtmlEditor} editor
50684      */
50685     createToolbar : function(editor){
50686          function btn(id, toggle, handler){
50687             var xid = fid + '-'+ id ;
50688             return {
50689                 id : xid,
50690                 cmd : id,
50691                 cls : 'x-btn-icon x-edit-'+id,
50692                 enableToggle:toggle !== false,
50693                 scope: editor, // was editor...
50694                 handler:handler||editor.relayBtnCmd,
50695                 clickEvent:'mousedown',
50696                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
50697                 tabIndex:-1
50698             };
50699         }
50700         
50701         
50702         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
50703         this.tb = tb;
50704         this.tb.add(
50705            {
50706                 cls : ' x-signature-btn x-signature-'+id,
50707                 scope: editor, // was editor...
50708                 handler: this.reset,
50709                 clickEvent:'mousedown',
50710                 text: this.labels.clear
50711             },
50712             {
50713                  xtype : 'Fill',
50714                  xns: Roo.Toolbar
50715             }, 
50716             {
50717                 cls : '  x-signature-btn x-signature-'+id,
50718                 scope: editor, // was editor...
50719                 handler: this.confirmHandler,
50720                 clickEvent:'mousedown',
50721                 text: this.labels.confirm
50722             }
50723         );
50724     
50725     },
50726     //public
50727     /**
50728      * when user is clicked confirm then show this image.....
50729      * 
50730      * @return {String} Image Data URI
50731      */
50732     getImageDataURI : function(){
50733         var svg = this.svgEl.dom.parentNode.innerHTML;
50734         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
50735         return src; 
50736     },
50737     /**
50738      * 
50739      * @return {Boolean} this.isConfirmed
50740      */
50741     getConfirmed : function(){
50742         return this.isConfirmed;
50743     },
50744     /**
50745      * 
50746      * @return {Number} this.width
50747      */
50748     getWidth : function(){
50749         return this.width;
50750     },
50751     /**
50752      * 
50753      * @return {Number} this.height
50754      */
50755     getHeight : function(){
50756         return this.height;
50757     },
50758     // private
50759     getSignature : function(){
50760         return this.signatureTmp;
50761     },
50762     // private
50763     reset : function(){
50764         this.signatureTmp = '';
50765         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50766         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
50767         this.isConfirmed = false;
50768         Roo.form.Signature.superclass.reset.call(this);
50769     },
50770     setSignature : function(s){
50771         this.signatureTmp = s;
50772         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50773         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
50774         this.setValue(s);
50775         this.isConfirmed = false;
50776         Roo.form.Signature.superclass.reset.call(this);
50777     }, 
50778     test : function(){
50779 //        Roo.log(this.signPanel.dom.contentWindow.up())
50780     },
50781     //private
50782     setConfirmed : function(){
50783         
50784         
50785         
50786 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
50787     },
50788     // private
50789     confirmHandler : function(){
50790         if(!this.getSignature()){
50791             return;
50792         }
50793         
50794         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
50795         this.setValue(this.getSignature());
50796         this.isConfirmed = true;
50797         
50798         this.fireEvent('confirm', this);
50799     },
50800     // private
50801     // Subclasses should provide the validation implementation by overriding this
50802     validateValue : function(value){
50803         if(this.allowBlank){
50804             return true;
50805         }
50806         
50807         if(this.isConfirmed){
50808             return true;
50809         }
50810         return false;
50811     }
50812 });/*
50813  * Based on:
50814  * Ext JS Library 1.1.1
50815  * Copyright(c) 2006-2007, Ext JS, LLC.
50816  *
50817  * Originally Released Under LGPL - original licence link has changed is not relivant.
50818  *
50819  * Fork - LGPL
50820  * <script type="text/javascript">
50821  */
50822  
50823
50824 /**
50825  * @class Roo.form.ComboBox
50826  * @extends Roo.form.TriggerField
50827  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
50828  * @constructor
50829  * Create a new ComboBox.
50830  * @param {Object} config Configuration options
50831  */
50832 Roo.form.Select = function(config){
50833     Roo.form.Select.superclass.constructor.call(this, config);
50834      
50835 };
50836
50837 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
50838     /**
50839      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
50840      */
50841     /**
50842      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
50843      * rendering into an Roo.Editor, defaults to false)
50844      */
50845     /**
50846      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
50847      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
50848      */
50849     /**
50850      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
50851      */
50852     /**
50853      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
50854      * the dropdown list (defaults to undefined, with no header element)
50855      */
50856
50857      /**
50858      * @cfg {String/Roo.Template} tpl The template to use to render the output
50859      */
50860      
50861     // private
50862     defaultAutoCreate : {tag: "select"  },
50863     /**
50864      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
50865      */
50866     listWidth: undefined,
50867     /**
50868      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
50869      * mode = 'remote' or 'text' if mode = 'local')
50870      */
50871     displayField: undefined,
50872     /**
50873      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
50874      * mode = 'remote' or 'value' if mode = 'local'). 
50875      * Note: use of a valueField requires the user make a selection
50876      * in order for a value to be mapped.
50877      */
50878     valueField: undefined,
50879     
50880     
50881     /**
50882      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
50883      * field's data value (defaults to the underlying DOM element's name)
50884      */
50885     hiddenName: undefined,
50886     /**
50887      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
50888      */
50889     listClass: '',
50890     /**
50891      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
50892      */
50893     selectedClass: 'x-combo-selected',
50894     /**
50895      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
50896      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
50897      * which displays a downward arrow icon).
50898      */
50899     triggerClass : 'x-form-arrow-trigger',
50900     /**
50901      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
50902      */
50903     shadow:'sides',
50904     /**
50905      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
50906      * anchor positions (defaults to 'tl-bl')
50907      */
50908     listAlign: 'tl-bl?',
50909     /**
50910      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
50911      */
50912     maxHeight: 300,
50913     /**
50914      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
50915      * query specified by the allQuery config option (defaults to 'query')
50916      */
50917     triggerAction: 'query',
50918     /**
50919      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
50920      * (defaults to 4, does not apply if editable = false)
50921      */
50922     minChars : 4,
50923     /**
50924      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
50925      * delay (typeAheadDelay) if it matches a known value (defaults to false)
50926      */
50927     typeAhead: false,
50928     /**
50929      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
50930      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
50931      */
50932     queryDelay: 500,
50933     /**
50934      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
50935      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
50936      */
50937     pageSize: 0,
50938     /**
50939      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
50940      * when editable = true (defaults to false)
50941      */
50942     selectOnFocus:false,
50943     /**
50944      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
50945      */
50946     queryParam: 'query',
50947     /**
50948      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
50949      * when mode = 'remote' (defaults to 'Loading...')
50950      */
50951     loadingText: 'Loading...',
50952     /**
50953      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
50954      */
50955     resizable: false,
50956     /**
50957      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
50958      */
50959     handleHeight : 8,
50960     /**
50961      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
50962      * traditional select (defaults to true)
50963      */
50964     editable: true,
50965     /**
50966      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
50967      */
50968     allQuery: '',
50969     /**
50970      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
50971      */
50972     mode: 'remote',
50973     /**
50974      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
50975      * listWidth has a higher value)
50976      */
50977     minListWidth : 70,
50978     /**
50979      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
50980      * allow the user to set arbitrary text into the field (defaults to false)
50981      */
50982     forceSelection:false,
50983     /**
50984      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
50985      * if typeAhead = true (defaults to 250)
50986      */
50987     typeAheadDelay : 250,
50988     /**
50989      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
50990      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
50991      */
50992     valueNotFoundText : undefined,
50993     
50994     /**
50995      * @cfg {String} defaultValue The value displayed after loading the store.
50996      */
50997     defaultValue: '',
50998     
50999     /**
51000      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
51001      */
51002     blockFocus : false,
51003     
51004     /**
51005      * @cfg {Boolean} disableClear Disable showing of clear button.
51006      */
51007     disableClear : false,
51008     /**
51009      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
51010      */
51011     alwaysQuery : false,
51012     
51013     //private
51014     addicon : false,
51015     editicon: false,
51016     
51017     // element that contains real text value.. (when hidden is used..)
51018      
51019     // private
51020     onRender : function(ct, position){
51021         Roo.form.Field.prototype.onRender.call(this, ct, position);
51022         
51023         if(this.store){
51024             this.store.on('beforeload', this.onBeforeLoad, this);
51025             this.store.on('load', this.onLoad, this);
51026             this.store.on('loadexception', this.onLoadException, this);
51027             this.store.load({});
51028         }
51029         
51030         
51031         
51032     },
51033
51034     // private
51035     initEvents : function(){
51036         //Roo.form.ComboBox.superclass.initEvents.call(this);
51037  
51038     },
51039
51040     onDestroy : function(){
51041        
51042         if(this.store){
51043             this.store.un('beforeload', this.onBeforeLoad, this);
51044             this.store.un('load', this.onLoad, this);
51045             this.store.un('loadexception', this.onLoadException, this);
51046         }
51047         //Roo.form.ComboBox.superclass.onDestroy.call(this);
51048     },
51049
51050     // private
51051     fireKey : function(e){
51052         if(e.isNavKeyPress() && !this.list.isVisible()){
51053             this.fireEvent("specialkey", this, e);
51054         }
51055     },
51056
51057     // private
51058     onResize: function(w, h){
51059         
51060         return; 
51061     
51062         
51063     },
51064
51065     /**
51066      * Allow or prevent the user from directly editing the field text.  If false is passed,
51067      * the user will only be able to select from the items defined in the dropdown list.  This method
51068      * is the runtime equivalent of setting the 'editable' config option at config time.
51069      * @param {Boolean} value True to allow the user to directly edit the field text
51070      */
51071     setEditable : function(value){
51072          
51073     },
51074
51075     // private
51076     onBeforeLoad : function(){
51077         
51078         Roo.log("Select before load");
51079         return;
51080     
51081         this.innerList.update(this.loadingText ?
51082                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
51083         //this.restrictHeight();
51084         this.selectedIndex = -1;
51085     },
51086
51087     // private
51088     onLoad : function(){
51089
51090     
51091         var dom = this.el.dom;
51092         dom.innerHTML = '';
51093          var od = dom.ownerDocument;
51094          
51095         if (this.emptyText) {
51096             var op = od.createElement('option');
51097             op.setAttribute('value', '');
51098             op.innerHTML = String.format('{0}', this.emptyText);
51099             dom.appendChild(op);
51100         }
51101         if(this.store.getCount() > 0){
51102            
51103             var vf = this.valueField;
51104             var df = this.displayField;
51105             this.store.data.each(function(r) {
51106                 // which colmsn to use... testing - cdoe / title..
51107                 var op = od.createElement('option');
51108                 op.setAttribute('value', r.data[vf]);
51109                 op.innerHTML = String.format('{0}', r.data[df]);
51110                 dom.appendChild(op);
51111             });
51112             if (typeof(this.defaultValue != 'undefined')) {
51113                 this.setValue(this.defaultValue);
51114             }
51115             
51116              
51117         }else{
51118             //this.onEmptyResults();
51119         }
51120         //this.el.focus();
51121     },
51122     // private
51123     onLoadException : function()
51124     {
51125         dom.innerHTML = '';
51126             
51127         Roo.log("Select on load exception");
51128         return;
51129     
51130         this.collapse();
51131         Roo.log(this.store.reader.jsonData);
51132         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
51133             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
51134         }
51135         
51136         
51137     },
51138     // private
51139     onTypeAhead : function(){
51140          
51141     },
51142
51143     // private
51144     onSelect : function(record, index){
51145         Roo.log('on select?');
51146         return;
51147         if(this.fireEvent('beforeselect', this, record, index) !== false){
51148             this.setFromData(index > -1 ? record.data : false);
51149             this.collapse();
51150             this.fireEvent('select', this, record, index);
51151         }
51152     },
51153
51154     /**
51155      * Returns the currently selected field value or empty string if no value is set.
51156      * @return {String} value The selected value
51157      */
51158     getValue : function(){
51159         var dom = this.el.dom;
51160         this.value = dom.options[dom.selectedIndex].value;
51161         return this.value;
51162         
51163     },
51164
51165     /**
51166      * Clears any text/value currently set in the field
51167      */
51168     clearValue : function(){
51169         this.value = '';
51170         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
51171         
51172     },
51173
51174     /**
51175      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
51176      * will be displayed in the field.  If the value does not match the data value of an existing item,
51177      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
51178      * Otherwise the field will be blank (although the value will still be set).
51179      * @param {String} value The value to match
51180      */
51181     setValue : function(v){
51182         var d = this.el.dom;
51183         for (var i =0; i < d.options.length;i++) {
51184             if (v == d.options[i].value) {
51185                 d.selectedIndex = i;
51186                 this.value = v;
51187                 return;
51188             }
51189         }
51190         this.clearValue();
51191     },
51192     /**
51193      * @property {Object} the last set data for the element
51194      */
51195     
51196     lastData : false,
51197     /**
51198      * Sets the value of the field based on a object which is related to the record format for the store.
51199      * @param {Object} value the value to set as. or false on reset?
51200      */
51201     setFromData : function(o){
51202         Roo.log('setfrom data?');
51203          
51204         
51205         
51206     },
51207     // private
51208     reset : function(){
51209         this.clearValue();
51210     },
51211     // private
51212     findRecord : function(prop, value){
51213         
51214         return false;
51215     
51216         var record;
51217         if(this.store.getCount() > 0){
51218             this.store.each(function(r){
51219                 if(r.data[prop] == value){
51220                     record = r;
51221                     return false;
51222                 }
51223                 return true;
51224             });
51225         }
51226         return record;
51227     },
51228     
51229     getName: function()
51230     {
51231         // returns hidden if it's set..
51232         if (!this.rendered) {return ''};
51233         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
51234         
51235     },
51236      
51237
51238     
51239
51240     // private
51241     onEmptyResults : function(){
51242         Roo.log('empty results');
51243         //this.collapse();
51244     },
51245
51246     /**
51247      * Returns true if the dropdown list is expanded, else false.
51248      */
51249     isExpanded : function(){
51250         return false;
51251     },
51252
51253     /**
51254      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
51255      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
51256      * @param {String} value The data value of the item to select
51257      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
51258      * selected item if it is not currently in view (defaults to true)
51259      * @return {Boolean} True if the value matched an item in the list, else false
51260      */
51261     selectByValue : function(v, scrollIntoView){
51262         Roo.log('select By Value');
51263         return false;
51264     
51265         if(v !== undefined && v !== null){
51266             var r = this.findRecord(this.valueField || this.displayField, v);
51267             if(r){
51268                 this.select(this.store.indexOf(r), scrollIntoView);
51269                 return true;
51270             }
51271         }
51272         return false;
51273     },
51274
51275     /**
51276      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
51277      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
51278      * @param {Number} index The zero-based index of the list item to select
51279      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
51280      * selected item if it is not currently in view (defaults to true)
51281      */
51282     select : function(index, scrollIntoView){
51283         Roo.log('select ');
51284         return  ;
51285         
51286         this.selectedIndex = index;
51287         this.view.select(index);
51288         if(scrollIntoView !== false){
51289             var el = this.view.getNode(index);
51290             if(el){
51291                 this.innerList.scrollChildIntoView(el, false);
51292             }
51293         }
51294     },
51295
51296       
51297
51298     // private
51299     validateBlur : function(){
51300         
51301         return;
51302         
51303     },
51304
51305     // private
51306     initQuery : function(){
51307         this.doQuery(this.getRawValue());
51308     },
51309
51310     // private
51311     doForce : function(){
51312         if(this.el.dom.value.length > 0){
51313             this.el.dom.value =
51314                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
51315              
51316         }
51317     },
51318
51319     /**
51320      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
51321      * query allowing the query action to be canceled if needed.
51322      * @param {String} query The SQL query to execute
51323      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
51324      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
51325      * saved in the current store (defaults to false)
51326      */
51327     doQuery : function(q, forceAll){
51328         
51329         Roo.log('doQuery?');
51330         if(q === undefined || q === null){
51331             q = '';
51332         }
51333         var qe = {
51334             query: q,
51335             forceAll: forceAll,
51336             combo: this,
51337             cancel:false
51338         };
51339         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
51340             return false;
51341         }
51342         q = qe.query;
51343         forceAll = qe.forceAll;
51344         if(forceAll === true || (q.length >= this.minChars)){
51345             if(this.lastQuery != q || this.alwaysQuery){
51346                 this.lastQuery = q;
51347                 if(this.mode == 'local'){
51348                     this.selectedIndex = -1;
51349                     if(forceAll){
51350                         this.store.clearFilter();
51351                     }else{
51352                         this.store.filter(this.displayField, q);
51353                     }
51354                     this.onLoad();
51355                 }else{
51356                     this.store.baseParams[this.queryParam] = q;
51357                     this.store.load({
51358                         params: this.getParams(q)
51359                     });
51360                     this.expand();
51361                 }
51362             }else{
51363                 this.selectedIndex = -1;
51364                 this.onLoad();   
51365             }
51366         }
51367     },
51368
51369     // private
51370     getParams : function(q){
51371         var p = {};
51372         //p[this.queryParam] = q;
51373         if(this.pageSize){
51374             p.start = 0;
51375             p.limit = this.pageSize;
51376         }
51377         return p;
51378     },
51379
51380     /**
51381      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
51382      */
51383     collapse : function(){
51384         
51385     },
51386
51387     // private
51388     collapseIf : function(e){
51389         
51390     },
51391
51392     /**
51393      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
51394      */
51395     expand : function(){
51396         
51397     } ,
51398
51399     // private
51400      
51401
51402     /** 
51403     * @cfg {Boolean} grow 
51404     * @hide 
51405     */
51406     /** 
51407     * @cfg {Number} growMin 
51408     * @hide 
51409     */
51410     /** 
51411     * @cfg {Number} growMax 
51412     * @hide 
51413     */
51414     /**
51415      * @hide
51416      * @method autoSize
51417      */
51418     
51419     setWidth : function()
51420     {
51421         
51422     },
51423     getResizeEl : function(){
51424         return this.el;
51425     }
51426 });//<script type="text/javasscript">
51427  
51428
51429 /**
51430  * @class Roo.DDView
51431  * A DnD enabled version of Roo.View.
51432  * @param {Element/String} container The Element in which to create the View.
51433  * @param {String} tpl The template string used to create the markup for each element of the View
51434  * @param {Object} config The configuration properties. These include all the config options of
51435  * {@link Roo.View} plus some specific to this class.<br>
51436  * <p>
51437  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
51438  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
51439  * <p>
51440  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
51441 .x-view-drag-insert-above {
51442         border-top:1px dotted #3366cc;
51443 }
51444 .x-view-drag-insert-below {
51445         border-bottom:1px dotted #3366cc;
51446 }
51447 </code></pre>
51448  * 
51449  */
51450  
51451 Roo.DDView = function(container, tpl, config) {
51452     Roo.DDView.superclass.constructor.apply(this, arguments);
51453     this.getEl().setStyle("outline", "0px none");
51454     this.getEl().unselectable();
51455     if (this.dragGroup) {
51456                 this.setDraggable(this.dragGroup.split(","));
51457     }
51458     if (this.dropGroup) {
51459                 this.setDroppable(this.dropGroup.split(","));
51460     }
51461     if (this.deletable) {
51462         this.setDeletable();
51463     }
51464     this.isDirtyFlag = false;
51465         this.addEvents({
51466                 "drop" : true
51467         });
51468 };
51469
51470 Roo.extend(Roo.DDView, Roo.View, {
51471 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
51472 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
51473 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
51474 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
51475
51476         isFormField: true,
51477
51478         reset: Roo.emptyFn,
51479         
51480         clearInvalid: Roo.form.Field.prototype.clearInvalid,
51481
51482         validate: function() {
51483                 return true;
51484         },
51485         
51486         destroy: function() {
51487                 this.purgeListeners();
51488                 this.getEl.removeAllListeners();
51489                 this.getEl().remove();
51490                 if (this.dragZone) {
51491                         if (this.dragZone.destroy) {
51492                                 this.dragZone.destroy();
51493                         }
51494                 }
51495                 if (this.dropZone) {
51496                         if (this.dropZone.destroy) {
51497                                 this.dropZone.destroy();
51498                         }
51499                 }
51500         },
51501
51502 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
51503         getName: function() {
51504                 return this.name;
51505         },
51506
51507 /**     Loads the View from a JSON string representing the Records to put into the Store. */
51508         setValue: function(v) {
51509                 if (!this.store) {
51510                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
51511                 }
51512                 var data = {};
51513                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
51514                 this.store.proxy = new Roo.data.MemoryProxy(data);
51515                 this.store.load();
51516         },
51517
51518 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
51519         getValue: function() {
51520                 var result = '(';
51521                 this.store.each(function(rec) {
51522                         result += rec.id + ',';
51523                 });
51524                 return result.substr(0, result.length - 1) + ')';
51525         },
51526         
51527         getIds: function() {
51528                 var i = 0, result = new Array(this.store.getCount());
51529                 this.store.each(function(rec) {
51530                         result[i++] = rec.id;
51531                 });
51532                 return result;
51533         },
51534         
51535         isDirty: function() {
51536                 return this.isDirtyFlag;
51537         },
51538
51539 /**
51540  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
51541  *      whole Element becomes the target, and this causes the drop gesture to append.
51542  */
51543     getTargetFromEvent : function(e) {
51544                 var target = e.getTarget();
51545                 while ((target !== null) && (target.parentNode != this.el.dom)) {
51546                 target = target.parentNode;
51547                 }
51548                 if (!target) {
51549                         target = this.el.dom.lastChild || this.el.dom;
51550                 }
51551                 return target;
51552     },
51553
51554 /**
51555  *      Create the drag data which consists of an object which has the property "ddel" as
51556  *      the drag proxy element. 
51557  */
51558     getDragData : function(e) {
51559         var target = this.findItemFromChild(e.getTarget());
51560                 if(target) {
51561                         this.handleSelection(e);
51562                         var selNodes = this.getSelectedNodes();
51563             var dragData = {
51564                 source: this,
51565                 copy: this.copy || (this.allowCopy && e.ctrlKey),
51566                 nodes: selNodes,
51567                 records: []
51568                         };
51569                         var selectedIndices = this.getSelectedIndexes();
51570                         for (var i = 0; i < selectedIndices.length; i++) {
51571                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
51572                         }
51573                         if (selNodes.length == 1) {
51574                                 dragData.ddel = target.cloneNode(true); // the div element
51575                         } else {
51576                                 var div = document.createElement('div'); // create the multi element drag "ghost"
51577                                 div.className = 'multi-proxy';
51578                                 for (var i = 0, len = selNodes.length; i < len; i++) {
51579                                         div.appendChild(selNodes[i].cloneNode(true));
51580                                 }
51581                                 dragData.ddel = div;
51582                         }
51583             //console.log(dragData)
51584             //console.log(dragData.ddel.innerHTML)
51585                         return dragData;
51586                 }
51587         //console.log('nodragData')
51588                 return false;
51589     },
51590     
51591 /**     Specify to which ddGroup items in this DDView may be dragged. */
51592     setDraggable: function(ddGroup) {
51593         if (ddGroup instanceof Array) {
51594                 Roo.each(ddGroup, this.setDraggable, this);
51595                 return;
51596         }
51597         if (this.dragZone) {
51598                 this.dragZone.addToGroup(ddGroup);
51599         } else {
51600                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
51601                                 containerScroll: true,
51602                                 ddGroup: ddGroup 
51603
51604                         });
51605 //                      Draggability implies selection. DragZone's mousedown selects the element.
51606                         if (!this.multiSelect) { this.singleSelect = true; }
51607
51608 //                      Wire the DragZone's handlers up to methods in *this*
51609                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
51610                 }
51611     },
51612
51613 /**     Specify from which ddGroup this DDView accepts drops. */
51614     setDroppable: function(ddGroup) {
51615         if (ddGroup instanceof Array) {
51616                 Roo.each(ddGroup, this.setDroppable, this);
51617                 return;
51618         }
51619         if (this.dropZone) {
51620                 this.dropZone.addToGroup(ddGroup);
51621         } else {
51622                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
51623                                 containerScroll: true,
51624                                 ddGroup: ddGroup
51625                         });
51626
51627 //                      Wire the DropZone's handlers up to methods in *this*
51628                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
51629                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
51630                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
51631                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
51632                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
51633                 }
51634     },
51635
51636 /**     Decide whether to drop above or below a View node. */
51637     getDropPoint : function(e, n, dd){
51638         if (n == this.el.dom) { return "above"; }
51639                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
51640                 var c = t + (b - t) / 2;
51641                 var y = Roo.lib.Event.getPageY(e);
51642                 if(y <= c) {
51643                         return "above";
51644                 }else{
51645                         return "below";
51646                 }
51647     },
51648
51649     onNodeEnter : function(n, dd, e, data){
51650                 return false;
51651     },
51652     
51653     onNodeOver : function(n, dd, e, data){
51654                 var pt = this.getDropPoint(e, n, dd);
51655                 // set the insert point style on the target node
51656                 var dragElClass = this.dropNotAllowed;
51657                 if (pt) {
51658                         var targetElClass;
51659                         if (pt == "above"){
51660                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
51661                                 targetElClass = "x-view-drag-insert-above";
51662                         } else {
51663                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
51664                                 targetElClass = "x-view-drag-insert-below";
51665                         }
51666                         if (this.lastInsertClass != targetElClass){
51667                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
51668                                 this.lastInsertClass = targetElClass;
51669                         }
51670                 }
51671                 return dragElClass;
51672         },
51673
51674     onNodeOut : function(n, dd, e, data){
51675                 this.removeDropIndicators(n);
51676     },
51677
51678     onNodeDrop : function(n, dd, e, data){
51679         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
51680                 return false;
51681         }
51682         var pt = this.getDropPoint(e, n, dd);
51683                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
51684                 if (pt == "below") { insertAt++; }
51685                 for (var i = 0; i < data.records.length; i++) {
51686                         var r = data.records[i];
51687                         var dup = this.store.getById(r.id);
51688                         if (dup && (dd != this.dragZone)) {
51689                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
51690                         } else {
51691                                 if (data.copy) {
51692                                         this.store.insert(insertAt++, r.copy());
51693                                 } else {
51694                                         data.source.isDirtyFlag = true;
51695                                         r.store.remove(r);
51696                                         this.store.insert(insertAt++, r);
51697                                 }
51698                                 this.isDirtyFlag = true;
51699                         }
51700                 }
51701                 this.dragZone.cachedTarget = null;
51702                 return true;
51703     },
51704
51705     removeDropIndicators : function(n){
51706                 if(n){
51707                         Roo.fly(n).removeClass([
51708                                 "x-view-drag-insert-above",
51709                                 "x-view-drag-insert-below"]);
51710                         this.lastInsertClass = "_noclass";
51711                 }
51712     },
51713
51714 /**
51715  *      Utility method. Add a delete option to the DDView's context menu.
51716  *      @param {String} imageUrl The URL of the "delete" icon image.
51717  */
51718         setDeletable: function(imageUrl) {
51719                 if (!this.singleSelect && !this.multiSelect) {
51720                         this.singleSelect = true;
51721                 }
51722                 var c = this.getContextMenu();
51723                 this.contextMenu.on("itemclick", function(item) {
51724                         switch (item.id) {
51725                                 case "delete":
51726                                         this.remove(this.getSelectedIndexes());
51727                                         break;
51728                         }
51729                 }, this);
51730                 this.contextMenu.add({
51731                         icon: imageUrl,
51732                         id: "delete",
51733                         text: 'Delete'
51734                 });
51735         },
51736         
51737 /**     Return the context menu for this DDView. */
51738         getContextMenu: function() {
51739                 if (!this.contextMenu) {
51740 //                      Create the View's context menu
51741                         this.contextMenu = new Roo.menu.Menu({
51742                                 id: this.id + "-contextmenu"
51743                         });
51744                         this.el.on("contextmenu", this.showContextMenu, this);
51745                 }
51746                 return this.contextMenu;
51747         },
51748         
51749         disableContextMenu: function() {
51750                 if (this.contextMenu) {
51751                         this.el.un("contextmenu", this.showContextMenu, this);
51752                 }
51753         },
51754
51755         showContextMenu: function(e, item) {
51756         item = this.findItemFromChild(e.getTarget());
51757                 if (item) {
51758                         e.stopEvent();
51759                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
51760                         this.contextMenu.showAt(e.getXY());
51761             }
51762     },
51763
51764 /**
51765  *      Remove {@link Roo.data.Record}s at the specified indices.
51766  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
51767  */
51768     remove: function(selectedIndices) {
51769                 selectedIndices = [].concat(selectedIndices);
51770                 for (var i = 0; i < selectedIndices.length; i++) {
51771                         var rec = this.store.getAt(selectedIndices[i]);
51772                         this.store.remove(rec);
51773                 }
51774     },
51775
51776 /**
51777  *      Double click fires the event, but also, if this is draggable, and there is only one other
51778  *      related DropZone, it transfers the selected node.
51779  */
51780     onDblClick : function(e){
51781         var item = this.findItemFromChild(e.getTarget());
51782         if(item){
51783             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
51784                 return false;
51785             }
51786             if (this.dragGroup) {
51787                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
51788                     while (targets.indexOf(this.dropZone) > -1) {
51789                             targets.remove(this.dropZone);
51790                                 }
51791                     if (targets.length == 1) {
51792                                         this.dragZone.cachedTarget = null;
51793                         var el = Roo.get(targets[0].getEl());
51794                         var box = el.getBox(true);
51795                         targets[0].onNodeDrop(el.dom, {
51796                                 target: el.dom,
51797                                 xy: [box.x, box.y + box.height - 1]
51798                         }, null, this.getDragData(e));
51799                     }
51800                 }
51801         }
51802     },
51803     
51804     handleSelection: function(e) {
51805                 this.dragZone.cachedTarget = null;
51806         var item = this.findItemFromChild(e.getTarget());
51807         if (!item) {
51808                 this.clearSelections(true);
51809                 return;
51810         }
51811                 if (item && (this.multiSelect || this.singleSelect)){
51812                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
51813                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
51814                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
51815                                 this.unselect(item);
51816                         } else {
51817                                 this.select(item, this.multiSelect && e.ctrlKey);
51818                                 this.lastSelection = item;
51819                         }
51820                 }
51821     },
51822
51823     onItemClick : function(item, index, e){
51824                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
51825                         return false;
51826                 }
51827                 return true;
51828     },
51829
51830     unselect : function(nodeInfo, suppressEvent){
51831                 var node = this.getNode(nodeInfo);
51832                 if(node && this.isSelected(node)){
51833                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
51834                                 Roo.fly(node).removeClass(this.selectedClass);
51835                                 this.selections.remove(node);
51836                                 if(!suppressEvent){
51837                                         this.fireEvent("selectionchange", this, this.selections);
51838                                 }
51839                         }
51840                 }
51841     }
51842 });
51843 /*
51844  * Based on:
51845  * Ext JS Library 1.1.1
51846  * Copyright(c) 2006-2007, Ext JS, LLC.
51847  *
51848  * Originally Released Under LGPL - original licence link has changed is not relivant.
51849  *
51850  * Fork - LGPL
51851  * <script type="text/javascript">
51852  */
51853  
51854 /**
51855  * @class Roo.LayoutManager
51856  * @extends Roo.util.Observable
51857  * Base class for layout managers.
51858  */
51859 Roo.LayoutManager = function(container, config){
51860     Roo.LayoutManager.superclass.constructor.call(this);
51861     this.el = Roo.get(container);
51862     // ie scrollbar fix
51863     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
51864         document.body.scroll = "no";
51865     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
51866         this.el.position('relative');
51867     }
51868     this.id = this.el.id;
51869     this.el.addClass("x-layout-container");
51870     /** false to disable window resize monitoring @type Boolean */
51871     this.monitorWindowResize = true;
51872     this.regions = {};
51873     this.addEvents({
51874         /**
51875          * @event layout
51876          * Fires when a layout is performed. 
51877          * @param {Roo.LayoutManager} this
51878          */
51879         "layout" : true,
51880         /**
51881          * @event regionresized
51882          * Fires when the user resizes a region. 
51883          * @param {Roo.LayoutRegion} region The resized region
51884          * @param {Number} newSize The new size (width for east/west, height for north/south)
51885          */
51886         "regionresized" : true,
51887         /**
51888          * @event regioncollapsed
51889          * Fires when a region is collapsed. 
51890          * @param {Roo.LayoutRegion} region The collapsed region
51891          */
51892         "regioncollapsed" : true,
51893         /**
51894          * @event regionexpanded
51895          * Fires when a region is expanded.  
51896          * @param {Roo.LayoutRegion} region The expanded region
51897          */
51898         "regionexpanded" : true
51899     });
51900     this.updating = false;
51901     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
51902 };
51903
51904 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
51905     /**
51906      * Returns true if this layout is currently being updated
51907      * @return {Boolean}
51908      */
51909     isUpdating : function(){
51910         return this.updating; 
51911     },
51912     
51913     /**
51914      * Suspend the LayoutManager from doing auto-layouts while
51915      * making multiple add or remove calls
51916      */
51917     beginUpdate : function(){
51918         this.updating = true;    
51919     },
51920     
51921     /**
51922      * Restore auto-layouts and optionally disable the manager from performing a layout
51923      * @param {Boolean} noLayout true to disable a layout update 
51924      */
51925     endUpdate : function(noLayout){
51926         this.updating = false;
51927         if(!noLayout){
51928             this.layout();
51929         }    
51930     },
51931     
51932     layout: function(){
51933         
51934     },
51935     
51936     onRegionResized : function(region, newSize){
51937         this.fireEvent("regionresized", region, newSize);
51938         this.layout();
51939     },
51940     
51941     onRegionCollapsed : function(region){
51942         this.fireEvent("regioncollapsed", region);
51943     },
51944     
51945     onRegionExpanded : function(region){
51946         this.fireEvent("regionexpanded", region);
51947     },
51948         
51949     /**
51950      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
51951      * performs box-model adjustments.
51952      * @return {Object} The size as an object {width: (the width), height: (the height)}
51953      */
51954     getViewSize : function(){
51955         var size;
51956         if(this.el.dom != document.body){
51957             size = this.el.getSize();
51958         }else{
51959             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
51960         }
51961         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
51962         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
51963         return size;
51964     },
51965     
51966     /**
51967      * Returns the Element this layout is bound to.
51968      * @return {Roo.Element}
51969      */
51970     getEl : function(){
51971         return this.el;
51972     },
51973     
51974     /**
51975      * Returns the specified region.
51976      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
51977      * @return {Roo.LayoutRegion}
51978      */
51979     getRegion : function(target){
51980         return this.regions[target.toLowerCase()];
51981     },
51982     
51983     onWindowResize : function(){
51984         if(this.monitorWindowResize){
51985             this.layout();
51986         }
51987     }
51988 });/*
51989  * Based on:
51990  * Ext JS Library 1.1.1
51991  * Copyright(c) 2006-2007, Ext JS, LLC.
51992  *
51993  * Originally Released Under LGPL - original licence link has changed is not relivant.
51994  *
51995  * Fork - LGPL
51996  * <script type="text/javascript">
51997  */
51998 /**
51999  * @class Roo.BorderLayout
52000  * @extends Roo.LayoutManager
52001  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
52002  * please see: <br><br>
52003  * <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>
52004  * <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>
52005  * Example:
52006  <pre><code>
52007  var layout = new Roo.BorderLayout(document.body, {
52008     north: {
52009         initialSize: 25,
52010         titlebar: false
52011     },
52012     west: {
52013         split:true,
52014         initialSize: 200,
52015         minSize: 175,
52016         maxSize: 400,
52017         titlebar: true,
52018         collapsible: true
52019     },
52020     east: {
52021         split:true,
52022         initialSize: 202,
52023         minSize: 175,
52024         maxSize: 400,
52025         titlebar: true,
52026         collapsible: true
52027     },
52028     south: {
52029         split:true,
52030         initialSize: 100,
52031         minSize: 100,
52032         maxSize: 200,
52033         titlebar: true,
52034         collapsible: true
52035     },
52036     center: {
52037         titlebar: true,
52038         autoScroll:true,
52039         resizeTabs: true,
52040         minTabWidth: 50,
52041         preferredTabWidth: 150
52042     }
52043 });
52044
52045 // shorthand
52046 var CP = Roo.ContentPanel;
52047
52048 layout.beginUpdate();
52049 layout.add("north", new CP("north", "North"));
52050 layout.add("south", new CP("south", {title: "South", closable: true}));
52051 layout.add("west", new CP("west", {title: "West"}));
52052 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
52053 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
52054 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
52055 layout.getRegion("center").showPanel("center1");
52056 layout.endUpdate();
52057 </code></pre>
52058
52059 <b>The container the layout is rendered into can be either the body element or any other element.
52060 If it is not the body element, the container needs to either be an absolute positioned element,
52061 or you will need to add "position:relative" to the css of the container.  You will also need to specify
52062 the container size if it is not the body element.</b>
52063
52064 * @constructor
52065 * Create a new BorderLayout
52066 * @param {String/HTMLElement/Element} container The container this layout is bound to
52067 * @param {Object} config Configuration options
52068  */
52069 Roo.BorderLayout = function(container, config){
52070     config = config || {};
52071     Roo.BorderLayout.superclass.constructor.call(this, container, config);
52072     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
52073     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
52074         var target = this.factory.validRegions[i];
52075         if(config[target]){
52076             this.addRegion(target, config[target]);
52077         }
52078     }
52079 };
52080
52081 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
52082     /**
52083      * Creates and adds a new region if it doesn't already exist.
52084      * @param {String} target The target region key (north, south, east, west or center).
52085      * @param {Object} config The regions config object
52086      * @return {BorderLayoutRegion} The new region
52087      */
52088     addRegion : function(target, config){
52089         if(!this.regions[target]){
52090             var r = this.factory.create(target, this, config);
52091             this.bindRegion(target, r);
52092         }
52093         return this.regions[target];
52094     },
52095
52096     // private (kinda)
52097     bindRegion : function(name, r){
52098         this.regions[name] = r;
52099         r.on("visibilitychange", this.layout, this);
52100         r.on("paneladded", this.layout, this);
52101         r.on("panelremoved", this.layout, this);
52102         r.on("invalidated", this.layout, this);
52103         r.on("resized", this.onRegionResized, this);
52104         r.on("collapsed", this.onRegionCollapsed, this);
52105         r.on("expanded", this.onRegionExpanded, this);
52106     },
52107
52108     /**
52109      * Performs a layout update.
52110      */
52111     layout : function(){
52112         if(this.updating) {
52113             return;
52114         }
52115         var size = this.getViewSize();
52116         var w = size.width;
52117         var h = size.height;
52118         var centerW = w;
52119         var centerH = h;
52120         var centerY = 0;
52121         var centerX = 0;
52122         //var x = 0, y = 0;
52123
52124         var rs = this.regions;
52125         var north = rs["north"];
52126         var south = rs["south"]; 
52127         var west = rs["west"];
52128         var east = rs["east"];
52129         var center = rs["center"];
52130         //if(this.hideOnLayout){ // not supported anymore
52131             //c.el.setStyle("display", "none");
52132         //}
52133         if(north && north.isVisible()){
52134             var b = north.getBox();
52135             var m = north.getMargins();
52136             b.width = w - (m.left+m.right);
52137             b.x = m.left;
52138             b.y = m.top;
52139             centerY = b.height + b.y + m.bottom;
52140             centerH -= centerY;
52141             north.updateBox(this.safeBox(b));
52142         }
52143         if(south && south.isVisible()){
52144             var b = south.getBox();
52145             var m = south.getMargins();
52146             b.width = w - (m.left+m.right);
52147             b.x = m.left;
52148             var totalHeight = (b.height + m.top + m.bottom);
52149             b.y = h - totalHeight + m.top;
52150             centerH -= totalHeight;
52151             south.updateBox(this.safeBox(b));
52152         }
52153         if(west && west.isVisible()){
52154             var b = west.getBox();
52155             var m = west.getMargins();
52156             b.height = centerH - (m.top+m.bottom);
52157             b.x = m.left;
52158             b.y = centerY + m.top;
52159             var totalWidth = (b.width + m.left + m.right);
52160             centerX += totalWidth;
52161             centerW -= totalWidth;
52162             west.updateBox(this.safeBox(b));
52163         }
52164         if(east && east.isVisible()){
52165             var b = east.getBox();
52166             var m = east.getMargins();
52167             b.height = centerH - (m.top+m.bottom);
52168             var totalWidth = (b.width + m.left + m.right);
52169             b.x = w - totalWidth + m.left;
52170             b.y = centerY + m.top;
52171             centerW -= totalWidth;
52172             east.updateBox(this.safeBox(b));
52173         }
52174         if(center){
52175             var m = center.getMargins();
52176             var centerBox = {
52177                 x: centerX + m.left,
52178                 y: centerY + m.top,
52179                 width: centerW - (m.left+m.right),
52180                 height: centerH - (m.top+m.bottom)
52181             };
52182             //if(this.hideOnLayout){
52183                 //center.el.setStyle("display", "block");
52184             //}
52185             center.updateBox(this.safeBox(centerBox));
52186         }
52187         this.el.repaint();
52188         this.fireEvent("layout", this);
52189     },
52190
52191     // private
52192     safeBox : function(box){
52193         box.width = Math.max(0, box.width);
52194         box.height = Math.max(0, box.height);
52195         return box;
52196     },
52197
52198     /**
52199      * Adds a ContentPanel (or subclass) to this layout.
52200      * @param {String} target The target region key (north, south, east, west or center).
52201      * @param {Roo.ContentPanel} panel The panel to add
52202      * @return {Roo.ContentPanel} The added panel
52203      */
52204     add : function(target, panel){
52205          
52206         target = target.toLowerCase();
52207         return this.regions[target].add(panel);
52208     },
52209
52210     /**
52211      * Remove a ContentPanel (or subclass) to this layout.
52212      * @param {String} target The target region key (north, south, east, west or center).
52213      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
52214      * @return {Roo.ContentPanel} The removed panel
52215      */
52216     remove : function(target, panel){
52217         target = target.toLowerCase();
52218         return this.regions[target].remove(panel);
52219     },
52220
52221     /**
52222      * Searches all regions for a panel with the specified id
52223      * @param {String} panelId
52224      * @return {Roo.ContentPanel} The panel or null if it wasn't found
52225      */
52226     findPanel : function(panelId){
52227         var rs = this.regions;
52228         for(var target in rs){
52229             if(typeof rs[target] != "function"){
52230                 var p = rs[target].getPanel(panelId);
52231                 if(p){
52232                     return p;
52233                 }
52234             }
52235         }
52236         return null;
52237     },
52238
52239     /**
52240      * Searches all regions for a panel with the specified id and activates (shows) it.
52241      * @param {String/ContentPanel} panelId The panels id or the panel itself
52242      * @return {Roo.ContentPanel} The shown panel or null
52243      */
52244     showPanel : function(panelId) {
52245       var rs = this.regions;
52246       for(var target in rs){
52247          var r = rs[target];
52248          if(typeof r != "function"){
52249             if(r.hasPanel(panelId)){
52250                return r.showPanel(panelId);
52251             }
52252          }
52253       }
52254       return null;
52255    },
52256
52257    /**
52258      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
52259      * @param {Roo.state.Provider} provider (optional) An alternate state provider
52260      */
52261     restoreState : function(provider){
52262         if(!provider){
52263             provider = Roo.state.Manager;
52264         }
52265         var sm = new Roo.LayoutStateManager();
52266         sm.init(this, provider);
52267     },
52268
52269     /**
52270      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
52271      * object should contain properties for each region to add ContentPanels to, and each property's value should be
52272      * a valid ContentPanel config object.  Example:
52273      * <pre><code>
52274 // Create the main layout
52275 var layout = new Roo.BorderLayout('main-ct', {
52276     west: {
52277         split:true,
52278         minSize: 175,
52279         titlebar: true
52280     },
52281     center: {
52282         title:'Components'
52283     }
52284 }, 'main-ct');
52285
52286 // Create and add multiple ContentPanels at once via configs
52287 layout.batchAdd({
52288    west: {
52289        id: 'source-files',
52290        autoCreate:true,
52291        title:'Ext Source Files',
52292        autoScroll:true,
52293        fitToFrame:true
52294    },
52295    center : {
52296        el: cview,
52297        autoScroll:true,
52298        fitToFrame:true,
52299        toolbar: tb,
52300        resizeEl:'cbody'
52301    }
52302 });
52303 </code></pre>
52304      * @param {Object} regions An object containing ContentPanel configs by region name
52305      */
52306     batchAdd : function(regions){
52307         this.beginUpdate();
52308         for(var rname in regions){
52309             var lr = this.regions[rname];
52310             if(lr){
52311                 this.addTypedPanels(lr, regions[rname]);
52312             }
52313         }
52314         this.endUpdate();
52315     },
52316
52317     // private
52318     addTypedPanels : function(lr, ps){
52319         if(typeof ps == 'string'){
52320             lr.add(new Roo.ContentPanel(ps));
52321         }
52322         else if(ps instanceof Array){
52323             for(var i =0, len = ps.length; i < len; i++){
52324                 this.addTypedPanels(lr, ps[i]);
52325             }
52326         }
52327         else if(!ps.events){ // raw config?
52328             var el = ps.el;
52329             delete ps.el; // prevent conflict
52330             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
52331         }
52332         else {  // panel object assumed!
52333             lr.add(ps);
52334         }
52335     },
52336     /**
52337      * Adds a xtype elements to the layout.
52338      * <pre><code>
52339
52340 layout.addxtype({
52341        xtype : 'ContentPanel',
52342        region: 'west',
52343        items: [ .... ]
52344    }
52345 );
52346
52347 layout.addxtype({
52348         xtype : 'NestedLayoutPanel',
52349         region: 'west',
52350         layout: {
52351            center: { },
52352            west: { }   
52353         },
52354         items : [ ... list of content panels or nested layout panels.. ]
52355    }
52356 );
52357 </code></pre>
52358      * @param {Object} cfg Xtype definition of item to add.
52359      */
52360     addxtype : function(cfg)
52361     {
52362         // basically accepts a pannel...
52363         // can accept a layout region..!?!?
52364         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
52365         
52366         if (!cfg.xtype.match(/Panel$/)) {
52367             return false;
52368         }
52369         var ret = false;
52370         
52371         if (typeof(cfg.region) == 'undefined') {
52372             Roo.log("Failed to add Panel, region was not set");
52373             Roo.log(cfg);
52374             return false;
52375         }
52376         var region = cfg.region;
52377         delete cfg.region;
52378         
52379           
52380         var xitems = [];
52381         if (cfg.items) {
52382             xitems = cfg.items;
52383             delete cfg.items;
52384         }
52385         var nb = false;
52386         
52387         switch(cfg.xtype) 
52388         {
52389             case 'ContentPanel':  // ContentPanel (el, cfg)
52390             case 'ScrollPanel':  // ContentPanel (el, cfg)
52391             case 'ViewPanel': 
52392                 if(cfg.autoCreate) {
52393                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52394                 } else {
52395                     var el = this.el.createChild();
52396                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
52397                 }
52398                 
52399                 this.add(region, ret);
52400                 break;
52401             
52402             
52403             case 'TreePanel': // our new panel!
52404                 cfg.el = this.el.createChild();
52405                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52406                 this.add(region, ret);
52407                 break;
52408             
52409             case 'NestedLayoutPanel': 
52410                 // create a new Layout (which is  a Border Layout...
52411                 var el = this.el.createChild();
52412                 var clayout = cfg.layout;
52413                 delete cfg.layout;
52414                 clayout.items   = clayout.items  || [];
52415                 // replace this exitems with the clayout ones..
52416                 xitems = clayout.items;
52417                  
52418                 
52419                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
52420                     cfg.background = false;
52421                 }
52422                 var layout = new Roo.BorderLayout(el, clayout);
52423                 
52424                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
52425                 //console.log('adding nested layout panel '  + cfg.toSource());
52426                 this.add(region, ret);
52427                 nb = {}; /// find first...
52428                 break;
52429                 
52430             case 'GridPanel': 
52431             
52432                 // needs grid and region
52433                 
52434                 //var el = this.getRegion(region).el.createChild();
52435                 var el = this.el.createChild();
52436                 // create the grid first...
52437                 
52438                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
52439                 delete cfg.grid;
52440                 if (region == 'center' && this.active ) {
52441                     cfg.background = false;
52442                 }
52443                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
52444                 
52445                 this.add(region, ret);
52446                 if (cfg.background) {
52447                     ret.on('activate', function(gp) {
52448                         if (!gp.grid.rendered) {
52449                             gp.grid.render();
52450                         }
52451                     });
52452                 } else {
52453                     grid.render();
52454                 }
52455                 break;
52456            
52457            
52458            
52459                 
52460                 
52461                 
52462             default:
52463                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
52464                     
52465                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52466                     this.add(region, ret);
52467                 } else {
52468                 
52469                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
52470                     return null;
52471                 }
52472                 
52473              // GridPanel (grid, cfg)
52474             
52475         }
52476         this.beginUpdate();
52477         // add children..
52478         var region = '';
52479         var abn = {};
52480         Roo.each(xitems, function(i)  {
52481             region = nb && i.region ? i.region : false;
52482             
52483             var add = ret.addxtype(i);
52484            
52485             if (region) {
52486                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
52487                 if (!i.background) {
52488                     abn[region] = nb[region] ;
52489                 }
52490             }
52491             
52492         });
52493         this.endUpdate();
52494
52495         // make the last non-background panel active..
52496         //if (nb) { Roo.log(abn); }
52497         if (nb) {
52498             
52499             for(var r in abn) {
52500                 region = this.getRegion(r);
52501                 if (region) {
52502                     // tried using nb[r], but it does not work..
52503                      
52504                     region.showPanel(abn[r]);
52505                    
52506                 }
52507             }
52508         }
52509         return ret;
52510         
52511     }
52512 });
52513
52514 /**
52515  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
52516  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
52517  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
52518  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
52519  * <pre><code>
52520 // shorthand
52521 var CP = Roo.ContentPanel;
52522
52523 var layout = Roo.BorderLayout.create({
52524     north: {
52525         initialSize: 25,
52526         titlebar: false,
52527         panels: [new CP("north", "North")]
52528     },
52529     west: {
52530         split:true,
52531         initialSize: 200,
52532         minSize: 175,
52533         maxSize: 400,
52534         titlebar: true,
52535         collapsible: true,
52536         panels: [new CP("west", {title: "West"})]
52537     },
52538     east: {
52539         split:true,
52540         initialSize: 202,
52541         minSize: 175,
52542         maxSize: 400,
52543         titlebar: true,
52544         collapsible: true,
52545         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
52546     },
52547     south: {
52548         split:true,
52549         initialSize: 100,
52550         minSize: 100,
52551         maxSize: 200,
52552         titlebar: true,
52553         collapsible: true,
52554         panels: [new CP("south", {title: "South", closable: true})]
52555     },
52556     center: {
52557         titlebar: true,
52558         autoScroll:true,
52559         resizeTabs: true,
52560         minTabWidth: 50,
52561         preferredTabWidth: 150,
52562         panels: [
52563             new CP("center1", {title: "Close Me", closable: true}),
52564             new CP("center2", {title: "Center Panel", closable: false})
52565         ]
52566     }
52567 }, document.body);
52568
52569 layout.getRegion("center").showPanel("center1");
52570 </code></pre>
52571  * @param config
52572  * @param targetEl
52573  */
52574 Roo.BorderLayout.create = function(config, targetEl){
52575     var layout = new Roo.BorderLayout(targetEl || document.body, config);
52576     layout.beginUpdate();
52577     var regions = Roo.BorderLayout.RegionFactory.validRegions;
52578     for(var j = 0, jlen = regions.length; j < jlen; j++){
52579         var lr = regions[j];
52580         if(layout.regions[lr] && config[lr].panels){
52581             var r = layout.regions[lr];
52582             var ps = config[lr].panels;
52583             layout.addTypedPanels(r, ps);
52584         }
52585     }
52586     layout.endUpdate();
52587     return layout;
52588 };
52589
52590 // private
52591 Roo.BorderLayout.RegionFactory = {
52592     // private
52593     validRegions : ["north","south","east","west","center"],
52594
52595     // private
52596     create : function(target, mgr, config){
52597         target = target.toLowerCase();
52598         if(config.lightweight || config.basic){
52599             return new Roo.BasicLayoutRegion(mgr, config, target);
52600         }
52601         switch(target){
52602             case "north":
52603                 return new Roo.NorthLayoutRegion(mgr, config);
52604             case "south":
52605                 return new Roo.SouthLayoutRegion(mgr, config);
52606             case "east":
52607                 return new Roo.EastLayoutRegion(mgr, config);
52608             case "west":
52609                 return new Roo.WestLayoutRegion(mgr, config);
52610             case "center":
52611                 return new Roo.CenterLayoutRegion(mgr, config);
52612         }
52613         throw 'Layout region "'+target+'" not supported.';
52614     }
52615 };/*
52616  * Based on:
52617  * Ext JS Library 1.1.1
52618  * Copyright(c) 2006-2007, Ext JS, LLC.
52619  *
52620  * Originally Released Under LGPL - original licence link has changed is not relivant.
52621  *
52622  * Fork - LGPL
52623  * <script type="text/javascript">
52624  */
52625  
52626 /**
52627  * @class Roo.BasicLayoutRegion
52628  * @extends Roo.util.Observable
52629  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
52630  * and does not have a titlebar, tabs or any other features. All it does is size and position 
52631  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
52632  */
52633 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
52634     this.mgr = mgr;
52635     this.position  = pos;
52636     this.events = {
52637         /**
52638          * @scope Roo.BasicLayoutRegion
52639          */
52640         
52641         /**
52642          * @event beforeremove
52643          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
52644          * @param {Roo.LayoutRegion} this
52645          * @param {Roo.ContentPanel} panel The panel
52646          * @param {Object} e The cancel event object
52647          */
52648         "beforeremove" : true,
52649         /**
52650          * @event invalidated
52651          * Fires when the layout for this region is changed.
52652          * @param {Roo.LayoutRegion} this
52653          */
52654         "invalidated" : true,
52655         /**
52656          * @event visibilitychange
52657          * Fires when this region is shown or hidden 
52658          * @param {Roo.LayoutRegion} this
52659          * @param {Boolean} visibility true or false
52660          */
52661         "visibilitychange" : true,
52662         /**
52663          * @event paneladded
52664          * Fires when a panel is added. 
52665          * @param {Roo.LayoutRegion} this
52666          * @param {Roo.ContentPanel} panel The panel
52667          */
52668         "paneladded" : true,
52669         /**
52670          * @event panelremoved
52671          * Fires when a panel is removed. 
52672          * @param {Roo.LayoutRegion} this
52673          * @param {Roo.ContentPanel} panel The panel
52674          */
52675         "panelremoved" : true,
52676         /**
52677          * @event beforecollapse
52678          * Fires when this region before collapse.
52679          * @param {Roo.LayoutRegion} this
52680          */
52681         "beforecollapse" : true,
52682         /**
52683          * @event collapsed
52684          * Fires when this region is collapsed.
52685          * @param {Roo.LayoutRegion} this
52686          */
52687         "collapsed" : true,
52688         /**
52689          * @event expanded
52690          * Fires when this region is expanded.
52691          * @param {Roo.LayoutRegion} this
52692          */
52693         "expanded" : true,
52694         /**
52695          * @event slideshow
52696          * Fires when this region is slid into view.
52697          * @param {Roo.LayoutRegion} this
52698          */
52699         "slideshow" : true,
52700         /**
52701          * @event slidehide
52702          * Fires when this region slides out of view. 
52703          * @param {Roo.LayoutRegion} this
52704          */
52705         "slidehide" : true,
52706         /**
52707          * @event panelactivated
52708          * Fires when a panel is activated. 
52709          * @param {Roo.LayoutRegion} this
52710          * @param {Roo.ContentPanel} panel The activated panel
52711          */
52712         "panelactivated" : true,
52713         /**
52714          * @event resized
52715          * Fires when the user resizes this region. 
52716          * @param {Roo.LayoutRegion} this
52717          * @param {Number} newSize The new size (width for east/west, height for north/south)
52718          */
52719         "resized" : true
52720     };
52721     /** A collection of panels in this region. @type Roo.util.MixedCollection */
52722     this.panels = new Roo.util.MixedCollection();
52723     this.panels.getKey = this.getPanelId.createDelegate(this);
52724     this.box = null;
52725     this.activePanel = null;
52726     // ensure listeners are added...
52727     
52728     if (config.listeners || config.events) {
52729         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
52730             listeners : config.listeners || {},
52731             events : config.events || {}
52732         });
52733     }
52734     
52735     if(skipConfig !== true){
52736         this.applyConfig(config);
52737     }
52738 };
52739
52740 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
52741     getPanelId : function(p){
52742         return p.getId();
52743     },
52744     
52745     applyConfig : function(config){
52746         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
52747         this.config = config;
52748         
52749     },
52750     
52751     /**
52752      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
52753      * the width, for horizontal (north, south) the height.
52754      * @param {Number} newSize The new width or height
52755      */
52756     resizeTo : function(newSize){
52757         var el = this.el ? this.el :
52758                  (this.activePanel ? this.activePanel.getEl() : null);
52759         if(el){
52760             switch(this.position){
52761                 case "east":
52762                 case "west":
52763                     el.setWidth(newSize);
52764                     this.fireEvent("resized", this, newSize);
52765                 break;
52766                 case "north":
52767                 case "south":
52768                     el.setHeight(newSize);
52769                     this.fireEvent("resized", this, newSize);
52770                 break;                
52771             }
52772         }
52773     },
52774     
52775     getBox : function(){
52776         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
52777     },
52778     
52779     getMargins : function(){
52780         return this.margins;
52781     },
52782     
52783     updateBox : function(box){
52784         this.box = box;
52785         var el = this.activePanel.getEl();
52786         el.dom.style.left = box.x + "px";
52787         el.dom.style.top = box.y + "px";
52788         this.activePanel.setSize(box.width, box.height);
52789     },
52790     
52791     /**
52792      * Returns the container element for this region.
52793      * @return {Roo.Element}
52794      */
52795     getEl : function(){
52796         return this.activePanel;
52797     },
52798     
52799     /**
52800      * Returns true if this region is currently visible.
52801      * @return {Boolean}
52802      */
52803     isVisible : function(){
52804         return this.activePanel ? true : false;
52805     },
52806     
52807     setActivePanel : function(panel){
52808         panel = this.getPanel(panel);
52809         if(this.activePanel && this.activePanel != panel){
52810             this.activePanel.setActiveState(false);
52811             this.activePanel.getEl().setLeftTop(-10000,-10000);
52812         }
52813         this.activePanel = panel;
52814         panel.setActiveState(true);
52815         if(this.box){
52816             panel.setSize(this.box.width, this.box.height);
52817         }
52818         this.fireEvent("panelactivated", this, panel);
52819         this.fireEvent("invalidated");
52820     },
52821     
52822     /**
52823      * Show the specified panel.
52824      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
52825      * @return {Roo.ContentPanel} The shown panel or null
52826      */
52827     showPanel : function(panel){
52828         if(panel = this.getPanel(panel)){
52829             this.setActivePanel(panel);
52830         }
52831         return panel;
52832     },
52833     
52834     /**
52835      * Get the active panel for this region.
52836      * @return {Roo.ContentPanel} The active panel or null
52837      */
52838     getActivePanel : function(){
52839         return this.activePanel;
52840     },
52841     
52842     /**
52843      * Add the passed ContentPanel(s)
52844      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
52845      * @return {Roo.ContentPanel} The panel added (if only one was added)
52846      */
52847     add : function(panel){
52848         if(arguments.length > 1){
52849             for(var i = 0, len = arguments.length; i < len; i++) {
52850                 this.add(arguments[i]);
52851             }
52852             return null;
52853         }
52854         if(this.hasPanel(panel)){
52855             this.showPanel(panel);
52856             return panel;
52857         }
52858         var el = panel.getEl();
52859         if(el.dom.parentNode != this.mgr.el.dom){
52860             this.mgr.el.dom.appendChild(el.dom);
52861         }
52862         if(panel.setRegion){
52863             panel.setRegion(this);
52864         }
52865         this.panels.add(panel);
52866         el.setStyle("position", "absolute");
52867         if(!panel.background){
52868             this.setActivePanel(panel);
52869             if(this.config.initialSize && this.panels.getCount()==1){
52870                 this.resizeTo(this.config.initialSize);
52871             }
52872         }
52873         this.fireEvent("paneladded", this, panel);
52874         return panel;
52875     },
52876     
52877     /**
52878      * Returns true if the panel is in this region.
52879      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
52880      * @return {Boolean}
52881      */
52882     hasPanel : function(panel){
52883         if(typeof panel == "object"){ // must be panel obj
52884             panel = panel.getId();
52885         }
52886         return this.getPanel(panel) ? true : false;
52887     },
52888     
52889     /**
52890      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
52891      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
52892      * @param {Boolean} preservePanel Overrides the config preservePanel option
52893      * @return {Roo.ContentPanel} The panel that was removed
52894      */
52895     remove : function(panel, preservePanel){
52896         panel = this.getPanel(panel);
52897         if(!panel){
52898             return null;
52899         }
52900         var e = {};
52901         this.fireEvent("beforeremove", this, panel, e);
52902         if(e.cancel === true){
52903             return null;
52904         }
52905         var panelId = panel.getId();
52906         this.panels.removeKey(panelId);
52907         return panel;
52908     },
52909     
52910     /**
52911      * Returns the panel specified or null if it's not in this region.
52912      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
52913      * @return {Roo.ContentPanel}
52914      */
52915     getPanel : function(id){
52916         if(typeof id == "object"){ // must be panel obj
52917             return id;
52918         }
52919         return this.panels.get(id);
52920     },
52921     
52922     /**
52923      * Returns this regions position (north/south/east/west/center).
52924      * @return {String} 
52925      */
52926     getPosition: function(){
52927         return this.position;    
52928     }
52929 });/*
52930  * Based on:
52931  * Ext JS Library 1.1.1
52932  * Copyright(c) 2006-2007, Ext JS, LLC.
52933  *
52934  * Originally Released Under LGPL - original licence link has changed is not relivant.
52935  *
52936  * Fork - LGPL
52937  * <script type="text/javascript">
52938  */
52939  
52940 /**
52941  * @class Roo.LayoutRegion
52942  * @extends Roo.BasicLayoutRegion
52943  * This class represents a region in a layout manager.
52944  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
52945  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
52946  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
52947  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
52948  * @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})
52949  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
52950  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
52951  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
52952  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
52953  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
52954  * @cfg {String}    title           The title for the region (overrides panel titles)
52955  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
52956  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
52957  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
52958  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
52959  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
52960  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
52961  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
52962  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
52963  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
52964  * @cfg {Boolean}   showPin         True to show a pin button
52965  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
52966  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
52967  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
52968  * @cfg {Number}    width           For East/West panels
52969  * @cfg {Number}    height          For North/South panels
52970  * @cfg {Boolean}   split           To show the splitter
52971  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
52972  */
52973 Roo.LayoutRegion = function(mgr, config, pos){
52974     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
52975     var dh = Roo.DomHelper;
52976     /** This region's container element 
52977     * @type Roo.Element */
52978     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
52979     /** This region's title element 
52980     * @type Roo.Element */
52981
52982     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
52983         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
52984         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
52985     ]}, true);
52986     this.titleEl.enableDisplayMode();
52987     /** This region's title text element 
52988     * @type HTMLElement */
52989     this.titleTextEl = this.titleEl.dom.firstChild;
52990     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
52991     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
52992     this.closeBtn.enableDisplayMode();
52993     this.closeBtn.on("click", this.closeClicked, this);
52994     this.closeBtn.hide();
52995
52996     this.createBody(config);
52997     this.visible = true;
52998     this.collapsed = false;
52999
53000     if(config.hideWhenEmpty){
53001         this.hide();
53002         this.on("paneladded", this.validateVisibility, this);
53003         this.on("panelremoved", this.validateVisibility, this);
53004     }
53005     this.applyConfig(config);
53006 };
53007
53008 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
53009
53010     createBody : function(){
53011         /** This region's body element 
53012         * @type Roo.Element */
53013         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
53014     },
53015
53016     applyConfig : function(c){
53017         if(c.collapsible && this.position != "center" && !this.collapsedEl){
53018             var dh = Roo.DomHelper;
53019             if(c.titlebar !== false){
53020                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
53021                 this.collapseBtn.on("click", this.collapse, this);
53022                 this.collapseBtn.enableDisplayMode();
53023
53024                 if(c.showPin === true || this.showPin){
53025                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
53026                     this.stickBtn.enableDisplayMode();
53027                     this.stickBtn.on("click", this.expand, this);
53028                     this.stickBtn.hide();
53029                 }
53030             }
53031             /** This region's collapsed element
53032             * @type Roo.Element */
53033             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
53034                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
53035             ]}, true);
53036             if(c.floatable !== false){
53037                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
53038                this.collapsedEl.on("click", this.collapseClick, this);
53039             }
53040
53041             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
53042                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
53043                    id: "message", unselectable: "on", style:{"float":"left"}});
53044                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
53045              }
53046             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
53047             this.expandBtn.on("click", this.expand, this);
53048         }
53049         if(this.collapseBtn){
53050             this.collapseBtn.setVisible(c.collapsible == true);
53051         }
53052         this.cmargins = c.cmargins || this.cmargins ||
53053                          (this.position == "west" || this.position == "east" ?
53054                              {top: 0, left: 2, right:2, bottom: 0} :
53055                              {top: 2, left: 0, right:0, bottom: 2});
53056         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
53057         this.bottomTabs = c.tabPosition != "top";
53058         this.autoScroll = c.autoScroll || false;
53059         if(this.autoScroll){
53060             this.bodyEl.setStyle("overflow", "auto");
53061         }else{
53062             this.bodyEl.setStyle("overflow", "hidden");
53063         }
53064         //if(c.titlebar !== false){
53065             if((!c.titlebar && !c.title) || c.titlebar === false){
53066                 this.titleEl.hide();
53067             }else{
53068                 this.titleEl.show();
53069                 if(c.title){
53070                     this.titleTextEl.innerHTML = c.title;
53071                 }
53072             }
53073         //}
53074         this.duration = c.duration || .30;
53075         this.slideDuration = c.slideDuration || .45;
53076         this.config = c;
53077         if(c.collapsed){
53078             this.collapse(true);
53079         }
53080         if(c.hidden){
53081             this.hide();
53082         }
53083     },
53084     /**
53085      * Returns true if this region is currently visible.
53086      * @return {Boolean}
53087      */
53088     isVisible : function(){
53089         return this.visible;
53090     },
53091
53092     /**
53093      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
53094      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
53095      */
53096     setCollapsedTitle : function(title){
53097         title = title || "&#160;";
53098         if(this.collapsedTitleTextEl){
53099             this.collapsedTitleTextEl.innerHTML = title;
53100         }
53101     },
53102
53103     getBox : function(){
53104         var b;
53105         if(!this.collapsed){
53106             b = this.el.getBox(false, true);
53107         }else{
53108             b = this.collapsedEl.getBox(false, true);
53109         }
53110         return b;
53111     },
53112
53113     getMargins : function(){
53114         return this.collapsed ? this.cmargins : this.margins;
53115     },
53116
53117     highlight : function(){
53118         this.el.addClass("x-layout-panel-dragover");
53119     },
53120
53121     unhighlight : function(){
53122         this.el.removeClass("x-layout-panel-dragover");
53123     },
53124
53125     updateBox : function(box){
53126         this.box = box;
53127         if(!this.collapsed){
53128             this.el.dom.style.left = box.x + "px";
53129             this.el.dom.style.top = box.y + "px";
53130             this.updateBody(box.width, box.height);
53131         }else{
53132             this.collapsedEl.dom.style.left = box.x + "px";
53133             this.collapsedEl.dom.style.top = box.y + "px";
53134             this.collapsedEl.setSize(box.width, box.height);
53135         }
53136         if(this.tabs){
53137             this.tabs.autoSizeTabs();
53138         }
53139     },
53140
53141     updateBody : function(w, h){
53142         if(w !== null){
53143             this.el.setWidth(w);
53144             w -= this.el.getBorderWidth("rl");
53145             if(this.config.adjustments){
53146                 w += this.config.adjustments[0];
53147             }
53148         }
53149         if(h !== null){
53150             this.el.setHeight(h);
53151             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
53152             h -= this.el.getBorderWidth("tb");
53153             if(this.config.adjustments){
53154                 h += this.config.adjustments[1];
53155             }
53156             this.bodyEl.setHeight(h);
53157             if(this.tabs){
53158                 h = this.tabs.syncHeight(h);
53159             }
53160         }
53161         if(this.panelSize){
53162             w = w !== null ? w : this.panelSize.width;
53163             h = h !== null ? h : this.panelSize.height;
53164         }
53165         if(this.activePanel){
53166             var el = this.activePanel.getEl();
53167             w = w !== null ? w : el.getWidth();
53168             h = h !== null ? h : el.getHeight();
53169             this.panelSize = {width: w, height: h};
53170             this.activePanel.setSize(w, h);
53171         }
53172         if(Roo.isIE && this.tabs){
53173             this.tabs.el.repaint();
53174         }
53175     },
53176
53177     /**
53178      * Returns the container element for this region.
53179      * @return {Roo.Element}
53180      */
53181     getEl : function(){
53182         return this.el;
53183     },
53184
53185     /**
53186      * Hides this region.
53187      */
53188     hide : function(){
53189         if(!this.collapsed){
53190             this.el.dom.style.left = "-2000px";
53191             this.el.hide();
53192         }else{
53193             this.collapsedEl.dom.style.left = "-2000px";
53194             this.collapsedEl.hide();
53195         }
53196         this.visible = false;
53197         this.fireEvent("visibilitychange", this, false);
53198     },
53199
53200     /**
53201      * Shows this region if it was previously hidden.
53202      */
53203     show : function(){
53204         if(!this.collapsed){
53205             this.el.show();
53206         }else{
53207             this.collapsedEl.show();
53208         }
53209         this.visible = true;
53210         this.fireEvent("visibilitychange", this, true);
53211     },
53212
53213     closeClicked : function(){
53214         if(this.activePanel){
53215             this.remove(this.activePanel);
53216         }
53217     },
53218
53219     collapseClick : function(e){
53220         if(this.isSlid){
53221            e.stopPropagation();
53222            this.slideIn();
53223         }else{
53224            e.stopPropagation();
53225            this.slideOut();
53226         }
53227     },
53228
53229     /**
53230      * Collapses this region.
53231      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
53232      */
53233     collapse : function(skipAnim, skipCheck){
53234         if(this.collapsed) {
53235             return;
53236         }
53237         
53238         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
53239             
53240             this.collapsed = true;
53241             if(this.split){
53242                 this.split.el.hide();
53243             }
53244             if(this.config.animate && skipAnim !== true){
53245                 this.fireEvent("invalidated", this);
53246                 this.animateCollapse();
53247             }else{
53248                 this.el.setLocation(-20000,-20000);
53249                 this.el.hide();
53250                 this.collapsedEl.show();
53251                 this.fireEvent("collapsed", this);
53252                 this.fireEvent("invalidated", this);
53253             }
53254         }
53255         
53256     },
53257
53258     animateCollapse : function(){
53259         // overridden
53260     },
53261
53262     /**
53263      * Expands this region if it was previously collapsed.
53264      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
53265      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
53266      */
53267     expand : function(e, skipAnim){
53268         if(e) {
53269             e.stopPropagation();
53270         }
53271         if(!this.collapsed || this.el.hasActiveFx()) {
53272             return;
53273         }
53274         if(this.isSlid){
53275             this.afterSlideIn();
53276             skipAnim = true;
53277         }
53278         this.collapsed = false;
53279         if(this.config.animate && skipAnim !== true){
53280             this.animateExpand();
53281         }else{
53282             this.el.show();
53283             if(this.split){
53284                 this.split.el.show();
53285             }
53286             this.collapsedEl.setLocation(-2000,-2000);
53287             this.collapsedEl.hide();
53288             this.fireEvent("invalidated", this);
53289             this.fireEvent("expanded", this);
53290         }
53291     },
53292
53293     animateExpand : function(){
53294         // overridden
53295     },
53296
53297     initTabs : function()
53298     {
53299         this.bodyEl.setStyle("overflow", "hidden");
53300         var ts = new Roo.TabPanel(
53301                 this.bodyEl.dom,
53302                 {
53303                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
53304                     disableTooltips: this.config.disableTabTips,
53305                     toolbar : this.config.toolbar
53306                 }
53307         );
53308         if(this.config.hideTabs){
53309             ts.stripWrap.setDisplayed(false);
53310         }
53311         this.tabs = ts;
53312         ts.resizeTabs = this.config.resizeTabs === true;
53313         ts.minTabWidth = this.config.minTabWidth || 40;
53314         ts.maxTabWidth = this.config.maxTabWidth || 250;
53315         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
53316         ts.monitorResize = false;
53317         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
53318         ts.bodyEl.addClass('x-layout-tabs-body');
53319         this.panels.each(this.initPanelAsTab, this);
53320     },
53321
53322     initPanelAsTab : function(panel){
53323         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
53324                     this.config.closeOnTab && panel.isClosable());
53325         if(panel.tabTip !== undefined){
53326             ti.setTooltip(panel.tabTip);
53327         }
53328         ti.on("activate", function(){
53329               this.setActivePanel(panel);
53330         }, this);
53331         if(this.config.closeOnTab){
53332             ti.on("beforeclose", function(t, e){
53333                 e.cancel = true;
53334                 this.remove(panel);
53335             }, this);
53336         }
53337         return ti;
53338     },
53339
53340     updatePanelTitle : function(panel, title){
53341         if(this.activePanel == panel){
53342             this.updateTitle(title);
53343         }
53344         if(this.tabs){
53345             var ti = this.tabs.getTab(panel.getEl().id);
53346             ti.setText(title);
53347             if(panel.tabTip !== undefined){
53348                 ti.setTooltip(panel.tabTip);
53349             }
53350         }
53351     },
53352
53353     updateTitle : function(title){
53354         if(this.titleTextEl && !this.config.title){
53355             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
53356         }
53357     },
53358
53359     setActivePanel : function(panel){
53360         panel = this.getPanel(panel);
53361         if(this.activePanel && this.activePanel != panel){
53362             this.activePanel.setActiveState(false);
53363         }
53364         this.activePanel = panel;
53365         panel.setActiveState(true);
53366         if(this.panelSize){
53367             panel.setSize(this.panelSize.width, this.panelSize.height);
53368         }
53369         if(this.closeBtn){
53370             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
53371         }
53372         this.updateTitle(panel.getTitle());
53373         if(this.tabs){
53374             this.fireEvent("invalidated", this);
53375         }
53376         this.fireEvent("panelactivated", this, panel);
53377     },
53378
53379     /**
53380      * Shows the specified panel.
53381      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
53382      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
53383      */
53384     showPanel : function(panel)
53385     {
53386         panel = this.getPanel(panel);
53387         if(panel){
53388             if(this.tabs){
53389                 var tab = this.tabs.getTab(panel.getEl().id);
53390                 if(tab.isHidden()){
53391                     this.tabs.unhideTab(tab.id);
53392                 }
53393                 tab.activate();
53394             }else{
53395                 this.setActivePanel(panel);
53396             }
53397         }
53398         return panel;
53399     },
53400
53401     /**
53402      * Get the active panel for this region.
53403      * @return {Roo.ContentPanel} The active panel or null
53404      */
53405     getActivePanel : function(){
53406         return this.activePanel;
53407     },
53408
53409     validateVisibility : function(){
53410         if(this.panels.getCount() < 1){
53411             this.updateTitle("&#160;");
53412             this.closeBtn.hide();
53413             this.hide();
53414         }else{
53415             if(!this.isVisible()){
53416                 this.show();
53417             }
53418         }
53419     },
53420
53421     /**
53422      * Adds the passed ContentPanel(s) to this region.
53423      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
53424      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
53425      */
53426     add : function(panel){
53427         if(arguments.length > 1){
53428             for(var i = 0, len = arguments.length; i < len; i++) {
53429                 this.add(arguments[i]);
53430             }
53431             return null;
53432         }
53433         if(this.hasPanel(panel)){
53434             this.showPanel(panel);
53435             return panel;
53436         }
53437         panel.setRegion(this);
53438         this.panels.add(panel);
53439         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
53440             this.bodyEl.dom.appendChild(panel.getEl().dom);
53441             if(panel.background !== true){
53442                 this.setActivePanel(panel);
53443             }
53444             this.fireEvent("paneladded", this, panel);
53445             return panel;
53446         }
53447         if(!this.tabs){
53448             this.initTabs();
53449         }else{
53450             this.initPanelAsTab(panel);
53451         }
53452         if(panel.background !== true){
53453             this.tabs.activate(panel.getEl().id);
53454         }
53455         this.fireEvent("paneladded", this, panel);
53456         return panel;
53457     },
53458
53459     /**
53460      * Hides the tab for the specified panel.
53461      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53462      */
53463     hidePanel : function(panel){
53464         if(this.tabs && (panel = this.getPanel(panel))){
53465             this.tabs.hideTab(panel.getEl().id);
53466         }
53467     },
53468
53469     /**
53470      * Unhides the tab for a previously hidden panel.
53471      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53472      */
53473     unhidePanel : function(panel){
53474         if(this.tabs && (panel = this.getPanel(panel))){
53475             this.tabs.unhideTab(panel.getEl().id);
53476         }
53477     },
53478
53479     clearPanels : function(){
53480         while(this.panels.getCount() > 0){
53481              this.remove(this.panels.first());
53482         }
53483     },
53484
53485     /**
53486      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
53487      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53488      * @param {Boolean} preservePanel Overrides the config preservePanel option
53489      * @return {Roo.ContentPanel} The panel that was removed
53490      */
53491     remove : function(panel, preservePanel){
53492         panel = this.getPanel(panel);
53493         if(!panel){
53494             return null;
53495         }
53496         var e = {};
53497         this.fireEvent("beforeremove", this, panel, e);
53498         if(e.cancel === true){
53499             return null;
53500         }
53501         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
53502         var panelId = panel.getId();
53503         this.panels.removeKey(panelId);
53504         if(preservePanel){
53505             document.body.appendChild(panel.getEl().dom);
53506         }
53507         if(this.tabs){
53508             this.tabs.removeTab(panel.getEl().id);
53509         }else if (!preservePanel){
53510             this.bodyEl.dom.removeChild(panel.getEl().dom);
53511         }
53512         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
53513             var p = this.panels.first();
53514             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
53515             tempEl.appendChild(p.getEl().dom);
53516             this.bodyEl.update("");
53517             this.bodyEl.dom.appendChild(p.getEl().dom);
53518             tempEl = null;
53519             this.updateTitle(p.getTitle());
53520             this.tabs = null;
53521             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
53522             this.setActivePanel(p);
53523         }
53524         panel.setRegion(null);
53525         if(this.activePanel == panel){
53526             this.activePanel = null;
53527         }
53528         if(this.config.autoDestroy !== false && preservePanel !== true){
53529             try{panel.destroy();}catch(e){}
53530         }
53531         this.fireEvent("panelremoved", this, panel);
53532         return panel;
53533     },
53534
53535     /**
53536      * Returns the TabPanel component used by this region
53537      * @return {Roo.TabPanel}
53538      */
53539     getTabs : function(){
53540         return this.tabs;
53541     },
53542
53543     createTool : function(parentEl, className){
53544         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
53545             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
53546         btn.addClassOnOver("x-layout-tools-button-over");
53547         return btn;
53548     }
53549 });/*
53550  * Based on:
53551  * Ext JS Library 1.1.1
53552  * Copyright(c) 2006-2007, Ext JS, LLC.
53553  *
53554  * Originally Released Under LGPL - original licence link has changed is not relivant.
53555  *
53556  * Fork - LGPL
53557  * <script type="text/javascript">
53558  */
53559  
53560
53561
53562 /**
53563  * @class Roo.SplitLayoutRegion
53564  * @extends Roo.LayoutRegion
53565  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
53566  */
53567 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
53568     this.cursor = cursor;
53569     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
53570 };
53571
53572 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
53573     splitTip : "Drag to resize.",
53574     collapsibleSplitTip : "Drag to resize. Double click to hide.",
53575     useSplitTips : false,
53576
53577     applyConfig : function(config){
53578         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
53579         if(config.split){
53580             if(!this.split){
53581                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
53582                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
53583                 /** The SplitBar for this region 
53584                 * @type Roo.SplitBar */
53585                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
53586                 this.split.on("moved", this.onSplitMove, this);
53587                 this.split.useShim = config.useShim === true;
53588                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
53589                 if(this.useSplitTips){
53590                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
53591                 }
53592                 if(config.collapsible){
53593                     this.split.el.on("dblclick", this.collapse,  this);
53594                 }
53595             }
53596             if(typeof config.minSize != "undefined"){
53597                 this.split.minSize = config.minSize;
53598             }
53599             if(typeof config.maxSize != "undefined"){
53600                 this.split.maxSize = config.maxSize;
53601             }
53602             if(config.hideWhenEmpty || config.hidden || config.collapsed){
53603                 this.hideSplitter();
53604             }
53605         }
53606     },
53607
53608     getHMaxSize : function(){
53609          var cmax = this.config.maxSize || 10000;
53610          var center = this.mgr.getRegion("center");
53611          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
53612     },
53613
53614     getVMaxSize : function(){
53615          var cmax = this.config.maxSize || 10000;
53616          var center = this.mgr.getRegion("center");
53617          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
53618     },
53619
53620     onSplitMove : function(split, newSize){
53621         this.fireEvent("resized", this, newSize);
53622     },
53623     
53624     /** 
53625      * Returns the {@link Roo.SplitBar} for this region.
53626      * @return {Roo.SplitBar}
53627      */
53628     getSplitBar : function(){
53629         return this.split;
53630     },
53631     
53632     hide : function(){
53633         this.hideSplitter();
53634         Roo.SplitLayoutRegion.superclass.hide.call(this);
53635     },
53636
53637     hideSplitter : function(){
53638         if(this.split){
53639             this.split.el.setLocation(-2000,-2000);
53640             this.split.el.hide();
53641         }
53642     },
53643
53644     show : function(){
53645         if(this.split){
53646             this.split.el.show();
53647         }
53648         Roo.SplitLayoutRegion.superclass.show.call(this);
53649     },
53650     
53651     beforeSlide: function(){
53652         if(Roo.isGecko){// firefox overflow auto bug workaround
53653             this.bodyEl.clip();
53654             if(this.tabs) {
53655                 this.tabs.bodyEl.clip();
53656             }
53657             if(this.activePanel){
53658                 this.activePanel.getEl().clip();
53659                 
53660                 if(this.activePanel.beforeSlide){
53661                     this.activePanel.beforeSlide();
53662                 }
53663             }
53664         }
53665     },
53666     
53667     afterSlide : function(){
53668         if(Roo.isGecko){// firefox overflow auto bug workaround
53669             this.bodyEl.unclip();
53670             if(this.tabs) {
53671                 this.tabs.bodyEl.unclip();
53672             }
53673             if(this.activePanel){
53674                 this.activePanel.getEl().unclip();
53675                 if(this.activePanel.afterSlide){
53676                     this.activePanel.afterSlide();
53677                 }
53678             }
53679         }
53680     },
53681
53682     initAutoHide : function(){
53683         if(this.autoHide !== false){
53684             if(!this.autoHideHd){
53685                 var st = new Roo.util.DelayedTask(this.slideIn, this);
53686                 this.autoHideHd = {
53687                     "mouseout": function(e){
53688                         if(!e.within(this.el, true)){
53689                             st.delay(500);
53690                         }
53691                     },
53692                     "mouseover" : function(e){
53693                         st.cancel();
53694                     },
53695                     scope : this
53696                 };
53697             }
53698             this.el.on(this.autoHideHd);
53699         }
53700     },
53701
53702     clearAutoHide : function(){
53703         if(this.autoHide !== false){
53704             this.el.un("mouseout", this.autoHideHd.mouseout);
53705             this.el.un("mouseover", this.autoHideHd.mouseover);
53706         }
53707     },
53708
53709     clearMonitor : function(){
53710         Roo.get(document).un("click", this.slideInIf, this);
53711     },
53712
53713     // these names are backwards but not changed for compat
53714     slideOut : function(){
53715         if(this.isSlid || this.el.hasActiveFx()){
53716             return;
53717         }
53718         this.isSlid = true;
53719         if(this.collapseBtn){
53720             this.collapseBtn.hide();
53721         }
53722         this.closeBtnState = this.closeBtn.getStyle('display');
53723         this.closeBtn.hide();
53724         if(this.stickBtn){
53725             this.stickBtn.show();
53726         }
53727         this.el.show();
53728         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
53729         this.beforeSlide();
53730         this.el.setStyle("z-index", 10001);
53731         this.el.slideIn(this.getSlideAnchor(), {
53732             callback: function(){
53733                 this.afterSlide();
53734                 this.initAutoHide();
53735                 Roo.get(document).on("click", this.slideInIf, this);
53736                 this.fireEvent("slideshow", this);
53737             },
53738             scope: this,
53739             block: true
53740         });
53741     },
53742
53743     afterSlideIn : function(){
53744         this.clearAutoHide();
53745         this.isSlid = false;
53746         this.clearMonitor();
53747         this.el.setStyle("z-index", "");
53748         if(this.collapseBtn){
53749             this.collapseBtn.show();
53750         }
53751         this.closeBtn.setStyle('display', this.closeBtnState);
53752         if(this.stickBtn){
53753             this.stickBtn.hide();
53754         }
53755         this.fireEvent("slidehide", this);
53756     },
53757
53758     slideIn : function(cb){
53759         if(!this.isSlid || this.el.hasActiveFx()){
53760             Roo.callback(cb);
53761             return;
53762         }
53763         this.isSlid = false;
53764         this.beforeSlide();
53765         this.el.slideOut(this.getSlideAnchor(), {
53766             callback: function(){
53767                 this.el.setLeftTop(-10000, -10000);
53768                 this.afterSlide();
53769                 this.afterSlideIn();
53770                 Roo.callback(cb);
53771             },
53772             scope: this,
53773             block: true
53774         });
53775     },
53776     
53777     slideInIf : function(e){
53778         if(!e.within(this.el)){
53779             this.slideIn();
53780         }
53781     },
53782
53783     animateCollapse : function(){
53784         this.beforeSlide();
53785         this.el.setStyle("z-index", 20000);
53786         var anchor = this.getSlideAnchor();
53787         this.el.slideOut(anchor, {
53788             callback : function(){
53789                 this.el.setStyle("z-index", "");
53790                 this.collapsedEl.slideIn(anchor, {duration:.3});
53791                 this.afterSlide();
53792                 this.el.setLocation(-10000,-10000);
53793                 this.el.hide();
53794                 this.fireEvent("collapsed", this);
53795             },
53796             scope: this,
53797             block: true
53798         });
53799     },
53800
53801     animateExpand : function(){
53802         this.beforeSlide();
53803         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
53804         this.el.setStyle("z-index", 20000);
53805         this.collapsedEl.hide({
53806             duration:.1
53807         });
53808         this.el.slideIn(this.getSlideAnchor(), {
53809             callback : function(){
53810                 this.el.setStyle("z-index", "");
53811                 this.afterSlide();
53812                 if(this.split){
53813                     this.split.el.show();
53814                 }
53815                 this.fireEvent("invalidated", this);
53816                 this.fireEvent("expanded", this);
53817             },
53818             scope: this,
53819             block: true
53820         });
53821     },
53822
53823     anchors : {
53824         "west" : "left",
53825         "east" : "right",
53826         "north" : "top",
53827         "south" : "bottom"
53828     },
53829
53830     sanchors : {
53831         "west" : "l",
53832         "east" : "r",
53833         "north" : "t",
53834         "south" : "b"
53835     },
53836
53837     canchors : {
53838         "west" : "tl-tr",
53839         "east" : "tr-tl",
53840         "north" : "tl-bl",
53841         "south" : "bl-tl"
53842     },
53843
53844     getAnchor : function(){
53845         return this.anchors[this.position];
53846     },
53847
53848     getCollapseAnchor : function(){
53849         return this.canchors[this.position];
53850     },
53851
53852     getSlideAnchor : function(){
53853         return this.sanchors[this.position];
53854     },
53855
53856     getAlignAdj : function(){
53857         var cm = this.cmargins;
53858         switch(this.position){
53859             case "west":
53860                 return [0, 0];
53861             break;
53862             case "east":
53863                 return [0, 0];
53864             break;
53865             case "north":
53866                 return [0, 0];
53867             break;
53868             case "south":
53869                 return [0, 0];
53870             break;
53871         }
53872     },
53873
53874     getExpandAdj : function(){
53875         var c = this.collapsedEl, cm = this.cmargins;
53876         switch(this.position){
53877             case "west":
53878                 return [-(cm.right+c.getWidth()+cm.left), 0];
53879             break;
53880             case "east":
53881                 return [cm.right+c.getWidth()+cm.left, 0];
53882             break;
53883             case "north":
53884                 return [0, -(cm.top+cm.bottom+c.getHeight())];
53885             break;
53886             case "south":
53887                 return [0, cm.top+cm.bottom+c.getHeight()];
53888             break;
53889         }
53890     }
53891 });/*
53892  * Based on:
53893  * Ext JS Library 1.1.1
53894  * Copyright(c) 2006-2007, Ext JS, LLC.
53895  *
53896  * Originally Released Under LGPL - original licence link has changed is not relivant.
53897  *
53898  * Fork - LGPL
53899  * <script type="text/javascript">
53900  */
53901 /*
53902  * These classes are private internal classes
53903  */
53904 Roo.CenterLayoutRegion = function(mgr, config){
53905     Roo.LayoutRegion.call(this, mgr, config, "center");
53906     this.visible = true;
53907     this.minWidth = config.minWidth || 20;
53908     this.minHeight = config.minHeight || 20;
53909 };
53910
53911 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
53912     hide : function(){
53913         // center panel can't be hidden
53914     },
53915     
53916     show : function(){
53917         // center panel can't be hidden
53918     },
53919     
53920     getMinWidth: function(){
53921         return this.minWidth;
53922     },
53923     
53924     getMinHeight: function(){
53925         return this.minHeight;
53926     }
53927 });
53928
53929
53930 Roo.NorthLayoutRegion = function(mgr, config){
53931     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
53932     if(this.split){
53933         this.split.placement = Roo.SplitBar.TOP;
53934         this.split.orientation = Roo.SplitBar.VERTICAL;
53935         this.split.el.addClass("x-layout-split-v");
53936     }
53937     var size = config.initialSize || config.height;
53938     if(typeof size != "undefined"){
53939         this.el.setHeight(size);
53940     }
53941 };
53942 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
53943     orientation: Roo.SplitBar.VERTICAL,
53944     getBox : function(){
53945         if(this.collapsed){
53946             return this.collapsedEl.getBox();
53947         }
53948         var box = this.el.getBox();
53949         if(this.split){
53950             box.height += this.split.el.getHeight();
53951         }
53952         return box;
53953     },
53954     
53955     updateBox : function(box){
53956         if(this.split && !this.collapsed){
53957             box.height -= this.split.el.getHeight();
53958             this.split.el.setLeft(box.x);
53959             this.split.el.setTop(box.y+box.height);
53960             this.split.el.setWidth(box.width);
53961         }
53962         if(this.collapsed){
53963             this.updateBody(box.width, null);
53964         }
53965         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53966     }
53967 });
53968
53969 Roo.SouthLayoutRegion = function(mgr, config){
53970     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
53971     if(this.split){
53972         this.split.placement = Roo.SplitBar.BOTTOM;
53973         this.split.orientation = Roo.SplitBar.VERTICAL;
53974         this.split.el.addClass("x-layout-split-v");
53975     }
53976     var size = config.initialSize || config.height;
53977     if(typeof size != "undefined"){
53978         this.el.setHeight(size);
53979     }
53980 };
53981 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
53982     orientation: Roo.SplitBar.VERTICAL,
53983     getBox : function(){
53984         if(this.collapsed){
53985             return this.collapsedEl.getBox();
53986         }
53987         var box = this.el.getBox();
53988         if(this.split){
53989             var sh = this.split.el.getHeight();
53990             box.height += sh;
53991             box.y -= sh;
53992         }
53993         return box;
53994     },
53995     
53996     updateBox : function(box){
53997         if(this.split && !this.collapsed){
53998             var sh = this.split.el.getHeight();
53999             box.height -= sh;
54000             box.y += sh;
54001             this.split.el.setLeft(box.x);
54002             this.split.el.setTop(box.y-sh);
54003             this.split.el.setWidth(box.width);
54004         }
54005         if(this.collapsed){
54006             this.updateBody(box.width, null);
54007         }
54008         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54009     }
54010 });
54011
54012 Roo.EastLayoutRegion = function(mgr, config){
54013     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
54014     if(this.split){
54015         this.split.placement = Roo.SplitBar.RIGHT;
54016         this.split.orientation = Roo.SplitBar.HORIZONTAL;
54017         this.split.el.addClass("x-layout-split-h");
54018     }
54019     var size = config.initialSize || config.width;
54020     if(typeof size != "undefined"){
54021         this.el.setWidth(size);
54022     }
54023 };
54024 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
54025     orientation: Roo.SplitBar.HORIZONTAL,
54026     getBox : function(){
54027         if(this.collapsed){
54028             return this.collapsedEl.getBox();
54029         }
54030         var box = this.el.getBox();
54031         if(this.split){
54032             var sw = this.split.el.getWidth();
54033             box.width += sw;
54034             box.x -= sw;
54035         }
54036         return box;
54037     },
54038
54039     updateBox : function(box){
54040         if(this.split && !this.collapsed){
54041             var sw = this.split.el.getWidth();
54042             box.width -= sw;
54043             this.split.el.setLeft(box.x);
54044             this.split.el.setTop(box.y);
54045             this.split.el.setHeight(box.height);
54046             box.x += sw;
54047         }
54048         if(this.collapsed){
54049             this.updateBody(null, box.height);
54050         }
54051         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54052     }
54053 });
54054
54055 Roo.WestLayoutRegion = function(mgr, config){
54056     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
54057     if(this.split){
54058         this.split.placement = Roo.SplitBar.LEFT;
54059         this.split.orientation = Roo.SplitBar.HORIZONTAL;
54060         this.split.el.addClass("x-layout-split-h");
54061     }
54062     var size = config.initialSize || config.width;
54063     if(typeof size != "undefined"){
54064         this.el.setWidth(size);
54065     }
54066 };
54067 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
54068     orientation: Roo.SplitBar.HORIZONTAL,
54069     getBox : function(){
54070         if(this.collapsed){
54071             return this.collapsedEl.getBox();
54072         }
54073         var box = this.el.getBox();
54074         if(this.split){
54075             box.width += this.split.el.getWidth();
54076         }
54077         return box;
54078     },
54079     
54080     updateBox : function(box){
54081         if(this.split && !this.collapsed){
54082             var sw = this.split.el.getWidth();
54083             box.width -= sw;
54084             this.split.el.setLeft(box.x+box.width);
54085             this.split.el.setTop(box.y);
54086             this.split.el.setHeight(box.height);
54087         }
54088         if(this.collapsed){
54089             this.updateBody(null, box.height);
54090         }
54091         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54092     }
54093 });
54094 /*
54095  * Based on:
54096  * Ext JS Library 1.1.1
54097  * Copyright(c) 2006-2007, Ext JS, LLC.
54098  *
54099  * Originally Released Under LGPL - original licence link has changed is not relivant.
54100  *
54101  * Fork - LGPL
54102  * <script type="text/javascript">
54103  */
54104  
54105  
54106 /*
54107  * Private internal class for reading and applying state
54108  */
54109 Roo.LayoutStateManager = function(layout){
54110      // default empty state
54111      this.state = {
54112         north: {},
54113         south: {},
54114         east: {},
54115         west: {}       
54116     };
54117 };
54118
54119 Roo.LayoutStateManager.prototype = {
54120     init : function(layout, provider){
54121         this.provider = provider;
54122         var state = provider.get(layout.id+"-layout-state");
54123         if(state){
54124             var wasUpdating = layout.isUpdating();
54125             if(!wasUpdating){
54126                 layout.beginUpdate();
54127             }
54128             for(var key in state){
54129                 if(typeof state[key] != "function"){
54130                     var rstate = state[key];
54131                     var r = layout.getRegion(key);
54132                     if(r && rstate){
54133                         if(rstate.size){
54134                             r.resizeTo(rstate.size);
54135                         }
54136                         if(rstate.collapsed == true){
54137                             r.collapse(true);
54138                         }else{
54139                             r.expand(null, true);
54140                         }
54141                     }
54142                 }
54143             }
54144             if(!wasUpdating){
54145                 layout.endUpdate();
54146             }
54147             this.state = state; 
54148         }
54149         this.layout = layout;
54150         layout.on("regionresized", this.onRegionResized, this);
54151         layout.on("regioncollapsed", this.onRegionCollapsed, this);
54152         layout.on("regionexpanded", this.onRegionExpanded, this);
54153     },
54154     
54155     storeState : function(){
54156         this.provider.set(this.layout.id+"-layout-state", this.state);
54157     },
54158     
54159     onRegionResized : function(region, newSize){
54160         this.state[region.getPosition()].size = newSize;
54161         this.storeState();
54162     },
54163     
54164     onRegionCollapsed : function(region){
54165         this.state[region.getPosition()].collapsed = true;
54166         this.storeState();
54167     },
54168     
54169     onRegionExpanded : function(region){
54170         this.state[region.getPosition()].collapsed = false;
54171         this.storeState();
54172     }
54173 };/*
54174  * Based on:
54175  * Ext JS Library 1.1.1
54176  * Copyright(c) 2006-2007, Ext JS, LLC.
54177  *
54178  * Originally Released Under LGPL - original licence link has changed is not relivant.
54179  *
54180  * Fork - LGPL
54181  * <script type="text/javascript">
54182  */
54183 /**
54184  * @class Roo.ContentPanel
54185  * @extends Roo.util.Observable
54186  * A basic ContentPanel element.
54187  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
54188  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
54189  * @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
54190  * @cfg {Boolean}   closable      True if the panel can be closed/removed
54191  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
54192  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
54193  * @cfg {Toolbar}   toolbar       A toolbar for this panel
54194  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
54195  * @cfg {String} title          The title for this panel
54196  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
54197  * @cfg {String} url            Calls {@link #setUrl} with this value
54198  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
54199  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
54200  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
54201  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
54202  * @cfg {String}    style  Extra style to add to the content panel 
54203
54204  * @constructor
54205  * Create a new ContentPanel.
54206  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
54207  * @param {String/Object} config A string to set only the title or a config object
54208  * @param {String} content (optional) Set the HTML content for this panel
54209  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
54210  */
54211 Roo.ContentPanel = function(el, config, content){
54212     
54213      
54214     /*
54215     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
54216         config = el;
54217         el = Roo.id();
54218     }
54219     if (config && config.parentLayout) { 
54220         el = config.parentLayout.el.createChild(); 
54221     }
54222     */
54223     if(el.autoCreate){ // xtype is available if this is called from factory
54224         config = el;
54225         el = Roo.id();
54226     }
54227     this.el = Roo.get(el);
54228     if(!this.el && config && config.autoCreate){
54229         if(typeof config.autoCreate == "object"){
54230             if(!config.autoCreate.id){
54231                 config.autoCreate.id = config.id||el;
54232             }
54233             this.el = Roo.DomHelper.append(document.body,
54234                         config.autoCreate, true);
54235         }else{
54236             this.el = Roo.DomHelper.append(document.body,
54237                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
54238         }
54239     }
54240     
54241     
54242     this.closable = false;
54243     this.loaded = false;
54244     this.active = false;
54245     if(typeof config == "string"){
54246         this.title = config;
54247     }else{
54248         Roo.apply(this, config);
54249     }
54250     
54251     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
54252         this.wrapEl = this.el.wrap();
54253         this.toolbar.container = this.el.insertSibling(false, 'before');
54254         this.toolbar = new Roo.Toolbar(this.toolbar);
54255     }
54256     
54257     // xtype created footer. - not sure if will work as we normally have to render first..
54258     if (this.footer && !this.footer.el && this.footer.xtype) {
54259         if (!this.wrapEl) {
54260             this.wrapEl = this.el.wrap();
54261         }
54262     
54263         this.footer.container = this.wrapEl.createChild();
54264          
54265         this.footer = Roo.factory(this.footer, Roo);
54266         
54267     }
54268     
54269     if(this.resizeEl){
54270         this.resizeEl = Roo.get(this.resizeEl, true);
54271     }else{
54272         this.resizeEl = this.el;
54273     }
54274     // handle view.xtype
54275     
54276  
54277     
54278     
54279     this.addEvents({
54280         /**
54281          * @event activate
54282          * Fires when this panel is activated. 
54283          * @param {Roo.ContentPanel} this
54284          */
54285         "activate" : true,
54286         /**
54287          * @event deactivate
54288          * Fires when this panel is activated. 
54289          * @param {Roo.ContentPanel} this
54290          */
54291         "deactivate" : true,
54292
54293         /**
54294          * @event resize
54295          * Fires when this panel is resized if fitToFrame is true.
54296          * @param {Roo.ContentPanel} this
54297          * @param {Number} width The width after any component adjustments
54298          * @param {Number} height The height after any component adjustments
54299          */
54300         "resize" : true,
54301         
54302          /**
54303          * @event render
54304          * Fires when this tab is created
54305          * @param {Roo.ContentPanel} this
54306          */
54307         "render" : true
54308          
54309         
54310     });
54311     
54312
54313     
54314     
54315     if(this.autoScroll){
54316         this.resizeEl.setStyle("overflow", "auto");
54317     } else {
54318         // fix randome scrolling
54319         this.el.on('scroll', function() {
54320             Roo.log('fix random scolling');
54321             this.scrollTo('top',0); 
54322         });
54323     }
54324     content = content || this.content;
54325     if(content){
54326         this.setContent(content);
54327     }
54328     if(config && config.url){
54329         this.setUrl(this.url, this.params, this.loadOnce);
54330     }
54331     
54332     
54333     
54334     Roo.ContentPanel.superclass.constructor.call(this);
54335     
54336     if (this.view && typeof(this.view.xtype) != 'undefined') {
54337         this.view.el = this.el.appendChild(document.createElement("div"));
54338         this.view = Roo.factory(this.view); 
54339         this.view.render  &&  this.view.render(false, '');  
54340     }
54341     
54342     
54343     this.fireEvent('render', this);
54344 };
54345
54346 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
54347     tabTip:'',
54348     setRegion : function(region){
54349         this.region = region;
54350         if(region){
54351            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
54352         }else{
54353            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
54354         } 
54355     },
54356     
54357     /**
54358      * Returns the toolbar for this Panel if one was configured. 
54359      * @return {Roo.Toolbar} 
54360      */
54361     getToolbar : function(){
54362         return this.toolbar;
54363     },
54364     
54365     setActiveState : function(active){
54366         this.active = active;
54367         if(!active){
54368             this.fireEvent("deactivate", this);
54369         }else{
54370             this.fireEvent("activate", this);
54371         }
54372     },
54373     /**
54374      * Updates this panel's element
54375      * @param {String} content The new content
54376      * @param {Boolean} loadScripts (optional) true to look for and process scripts
54377     */
54378     setContent : function(content, loadScripts){
54379         this.el.update(content, loadScripts);
54380     },
54381
54382     ignoreResize : function(w, h){
54383         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
54384             return true;
54385         }else{
54386             this.lastSize = {width: w, height: h};
54387             return false;
54388         }
54389     },
54390     /**
54391      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
54392      * @return {Roo.UpdateManager} The UpdateManager
54393      */
54394     getUpdateManager : function(){
54395         return this.el.getUpdateManager();
54396     },
54397      /**
54398      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
54399      * @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:
54400 <pre><code>
54401 panel.load({
54402     url: "your-url.php",
54403     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
54404     callback: yourFunction,
54405     scope: yourObject, //(optional scope)
54406     discardUrl: false,
54407     nocache: false,
54408     text: "Loading...",
54409     timeout: 30,
54410     scripts: false
54411 });
54412 </code></pre>
54413      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
54414      * 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.
54415      * @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}
54416      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
54417      * @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.
54418      * @return {Roo.ContentPanel} this
54419      */
54420     load : function(){
54421         var um = this.el.getUpdateManager();
54422         um.update.apply(um, arguments);
54423         return this;
54424     },
54425
54426
54427     /**
54428      * 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.
54429      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
54430      * @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)
54431      * @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)
54432      * @return {Roo.UpdateManager} The UpdateManager
54433      */
54434     setUrl : function(url, params, loadOnce){
54435         if(this.refreshDelegate){
54436             this.removeListener("activate", this.refreshDelegate);
54437         }
54438         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
54439         this.on("activate", this.refreshDelegate);
54440         return this.el.getUpdateManager();
54441     },
54442     
54443     _handleRefresh : function(url, params, loadOnce){
54444         if(!loadOnce || !this.loaded){
54445             var updater = this.el.getUpdateManager();
54446             updater.update(url, params, this._setLoaded.createDelegate(this));
54447         }
54448     },
54449     
54450     _setLoaded : function(){
54451         this.loaded = true;
54452     }, 
54453     
54454     /**
54455      * Returns this panel's id
54456      * @return {String} 
54457      */
54458     getId : function(){
54459         return this.el.id;
54460     },
54461     
54462     /** 
54463      * Returns this panel's element - used by regiosn to add.
54464      * @return {Roo.Element} 
54465      */
54466     getEl : function(){
54467         return this.wrapEl || this.el;
54468     },
54469     
54470     adjustForComponents : function(width, height)
54471     {
54472         //Roo.log('adjustForComponents ');
54473         if(this.resizeEl != this.el){
54474             width -= this.el.getFrameWidth('lr');
54475             height -= this.el.getFrameWidth('tb');
54476         }
54477         if(this.toolbar){
54478             var te = this.toolbar.getEl();
54479             height -= te.getHeight();
54480             te.setWidth(width);
54481         }
54482         if(this.footer){
54483             var te = this.footer.getEl();
54484             //Roo.log("footer:" + te.getHeight());
54485             
54486             height -= te.getHeight();
54487             te.setWidth(width);
54488         }
54489         
54490         
54491         if(this.adjustments){
54492             width += this.adjustments[0];
54493             height += this.adjustments[1];
54494         }
54495         return {"width": width, "height": height};
54496     },
54497     
54498     setSize : function(width, height){
54499         if(this.fitToFrame && !this.ignoreResize(width, height)){
54500             if(this.fitContainer && this.resizeEl != this.el){
54501                 this.el.setSize(width, height);
54502             }
54503             var size = this.adjustForComponents(width, height);
54504             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
54505             this.fireEvent('resize', this, size.width, size.height);
54506         }
54507     },
54508     
54509     /**
54510      * Returns this panel's title
54511      * @return {String} 
54512      */
54513     getTitle : function(){
54514         return this.title;
54515     },
54516     
54517     /**
54518      * Set this panel's title
54519      * @param {String} title
54520      */
54521     setTitle : function(title){
54522         this.title = title;
54523         if(this.region){
54524             this.region.updatePanelTitle(this, title);
54525         }
54526     },
54527     
54528     /**
54529      * Returns true is this panel was configured to be closable
54530      * @return {Boolean} 
54531      */
54532     isClosable : function(){
54533         return this.closable;
54534     },
54535     
54536     beforeSlide : function(){
54537         this.el.clip();
54538         this.resizeEl.clip();
54539     },
54540     
54541     afterSlide : function(){
54542         this.el.unclip();
54543         this.resizeEl.unclip();
54544     },
54545     
54546     /**
54547      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
54548      *   Will fail silently if the {@link #setUrl} method has not been called.
54549      *   This does not activate the panel, just updates its content.
54550      */
54551     refresh : function(){
54552         if(this.refreshDelegate){
54553            this.loaded = false;
54554            this.refreshDelegate();
54555         }
54556     },
54557     
54558     /**
54559      * Destroys this panel
54560      */
54561     destroy : function(){
54562         this.el.removeAllListeners();
54563         var tempEl = document.createElement("span");
54564         tempEl.appendChild(this.el.dom);
54565         tempEl.innerHTML = "";
54566         this.el.remove();
54567         this.el = null;
54568     },
54569     
54570     /**
54571      * form - if the content panel contains a form - this is a reference to it.
54572      * @type {Roo.form.Form}
54573      */
54574     form : false,
54575     /**
54576      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
54577      *    This contains a reference to it.
54578      * @type {Roo.View}
54579      */
54580     view : false,
54581     
54582       /**
54583      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
54584      * <pre><code>
54585
54586 layout.addxtype({
54587        xtype : 'Form',
54588        items: [ .... ]
54589    }
54590 );
54591
54592 </code></pre>
54593      * @param {Object} cfg Xtype definition of item to add.
54594      */
54595     
54596     addxtype : function(cfg) {
54597         // add form..
54598         if (cfg.xtype.match(/^Form$/)) {
54599             
54600             var el;
54601             //if (this.footer) {
54602             //    el = this.footer.container.insertSibling(false, 'before');
54603             //} else {
54604                 el = this.el.createChild();
54605             //}
54606
54607             this.form = new  Roo.form.Form(cfg);
54608             
54609             
54610             if ( this.form.allItems.length) {
54611                 this.form.render(el.dom);
54612             }
54613             return this.form;
54614         }
54615         // should only have one of theses..
54616         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
54617             // views.. should not be just added - used named prop 'view''
54618             
54619             cfg.el = this.el.appendChild(document.createElement("div"));
54620             // factory?
54621             
54622             var ret = new Roo.factory(cfg);
54623              
54624              ret.render && ret.render(false, ''); // render blank..
54625             this.view = ret;
54626             return ret;
54627         }
54628         return false;
54629     }
54630 });
54631
54632 /**
54633  * @class Roo.GridPanel
54634  * @extends Roo.ContentPanel
54635  * @constructor
54636  * Create a new GridPanel.
54637  * @param {Roo.grid.Grid} grid The grid for this panel
54638  * @param {String/Object} config A string to set only the panel's title, or a config object
54639  */
54640 Roo.GridPanel = function(grid, config){
54641     
54642   
54643     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
54644         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
54645         
54646     this.wrapper.dom.appendChild(grid.getGridEl().dom);
54647     
54648     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
54649     
54650     if(this.toolbar){
54651         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
54652     }
54653     // xtype created footer. - not sure if will work as we normally have to render first..
54654     if (this.footer && !this.footer.el && this.footer.xtype) {
54655         
54656         this.footer.container = this.grid.getView().getFooterPanel(true);
54657         this.footer.dataSource = this.grid.dataSource;
54658         this.footer = Roo.factory(this.footer, Roo);
54659         
54660     }
54661     
54662     grid.monitorWindowResize = false; // turn off autosizing
54663     grid.autoHeight = false;
54664     grid.autoWidth = false;
54665     this.grid = grid;
54666     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
54667 };
54668
54669 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
54670     getId : function(){
54671         return this.grid.id;
54672     },
54673     
54674     /**
54675      * Returns the grid for this panel
54676      * @return {Roo.grid.Grid} 
54677      */
54678     getGrid : function(){
54679         return this.grid;    
54680     },
54681     
54682     setSize : function(width, height){
54683         if(!this.ignoreResize(width, height)){
54684             var grid = this.grid;
54685             var size = this.adjustForComponents(width, height);
54686             grid.getGridEl().setSize(size.width, size.height);
54687             grid.autoSize();
54688         }
54689     },
54690     
54691     beforeSlide : function(){
54692         this.grid.getView().scroller.clip();
54693     },
54694     
54695     afterSlide : function(){
54696         this.grid.getView().scroller.unclip();
54697     },
54698     
54699     destroy : function(){
54700         this.grid.destroy();
54701         delete this.grid;
54702         Roo.GridPanel.superclass.destroy.call(this); 
54703     }
54704 });
54705
54706
54707 /**
54708  * @class Roo.NestedLayoutPanel
54709  * @extends Roo.ContentPanel
54710  * @constructor
54711  * Create a new NestedLayoutPanel.
54712  * 
54713  * 
54714  * @param {Roo.BorderLayout} layout The layout for this panel
54715  * @param {String/Object} config A string to set only the title or a config object
54716  */
54717 Roo.NestedLayoutPanel = function(layout, config)
54718 {
54719     // construct with only one argument..
54720     /* FIXME - implement nicer consturctors
54721     if (layout.layout) {
54722         config = layout;
54723         layout = config.layout;
54724         delete config.layout;
54725     }
54726     if (layout.xtype && !layout.getEl) {
54727         // then layout needs constructing..
54728         layout = Roo.factory(layout, Roo);
54729     }
54730     */
54731     
54732     
54733     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
54734     
54735     layout.monitorWindowResize = false; // turn off autosizing
54736     this.layout = layout;
54737     this.layout.getEl().addClass("x-layout-nested-layout");
54738     
54739     
54740     
54741     
54742 };
54743
54744 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
54745
54746     setSize : function(width, height){
54747         if(!this.ignoreResize(width, height)){
54748             var size = this.adjustForComponents(width, height);
54749             var el = this.layout.getEl();
54750             el.setSize(size.width, size.height);
54751             var touch = el.dom.offsetWidth;
54752             this.layout.layout();
54753             // ie requires a double layout on the first pass
54754             if(Roo.isIE && !this.initialized){
54755                 this.initialized = true;
54756                 this.layout.layout();
54757             }
54758         }
54759     },
54760     
54761     // activate all subpanels if not currently active..
54762     
54763     setActiveState : function(active){
54764         this.active = active;
54765         if(!active){
54766             this.fireEvent("deactivate", this);
54767             return;
54768         }
54769         
54770         this.fireEvent("activate", this);
54771         // not sure if this should happen before or after..
54772         if (!this.layout) {
54773             return; // should not happen..
54774         }
54775         var reg = false;
54776         for (var r in this.layout.regions) {
54777             reg = this.layout.getRegion(r);
54778             if (reg.getActivePanel()) {
54779                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
54780                 reg.setActivePanel(reg.getActivePanel());
54781                 continue;
54782             }
54783             if (!reg.panels.length) {
54784                 continue;
54785             }
54786             reg.showPanel(reg.getPanel(0));
54787         }
54788         
54789         
54790         
54791         
54792     },
54793     
54794     /**
54795      * Returns the nested BorderLayout for this panel
54796      * @return {Roo.BorderLayout} 
54797      */
54798     getLayout : function(){
54799         return this.layout;
54800     },
54801     
54802      /**
54803      * Adds a xtype elements to the layout of the nested panel
54804      * <pre><code>
54805
54806 panel.addxtype({
54807        xtype : 'ContentPanel',
54808        region: 'west',
54809        items: [ .... ]
54810    }
54811 );
54812
54813 panel.addxtype({
54814         xtype : 'NestedLayoutPanel',
54815         region: 'west',
54816         layout: {
54817            center: { },
54818            west: { }   
54819         },
54820         items : [ ... list of content panels or nested layout panels.. ]
54821    }
54822 );
54823 </code></pre>
54824      * @param {Object} cfg Xtype definition of item to add.
54825      */
54826     addxtype : function(cfg) {
54827         return this.layout.addxtype(cfg);
54828     
54829     }
54830 });
54831
54832 Roo.ScrollPanel = function(el, config, content){
54833     config = config || {};
54834     config.fitToFrame = true;
54835     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
54836     
54837     this.el.dom.style.overflow = "hidden";
54838     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
54839     this.el.removeClass("x-layout-inactive-content");
54840     this.el.on("mousewheel", this.onWheel, this);
54841
54842     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
54843     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
54844     up.unselectable(); down.unselectable();
54845     up.on("click", this.scrollUp, this);
54846     down.on("click", this.scrollDown, this);
54847     up.addClassOnOver("x-scroller-btn-over");
54848     down.addClassOnOver("x-scroller-btn-over");
54849     up.addClassOnClick("x-scroller-btn-click");
54850     down.addClassOnClick("x-scroller-btn-click");
54851     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
54852
54853     this.resizeEl = this.el;
54854     this.el = wrap; this.up = up; this.down = down;
54855 };
54856
54857 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
54858     increment : 100,
54859     wheelIncrement : 5,
54860     scrollUp : function(){
54861         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
54862     },
54863
54864     scrollDown : function(){
54865         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
54866     },
54867
54868     afterScroll : function(){
54869         var el = this.resizeEl;
54870         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
54871         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
54872         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
54873     },
54874
54875     setSize : function(){
54876         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
54877         this.afterScroll();
54878     },
54879
54880     onWheel : function(e){
54881         var d = e.getWheelDelta();
54882         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
54883         this.afterScroll();
54884         e.stopEvent();
54885     },
54886
54887     setContent : function(content, loadScripts){
54888         this.resizeEl.update(content, loadScripts);
54889     }
54890
54891 });
54892
54893
54894
54895
54896
54897
54898
54899
54900
54901 /**
54902  * @class Roo.TreePanel
54903  * @extends Roo.ContentPanel
54904  * @constructor
54905  * Create a new TreePanel. - defaults to fit/scoll contents.
54906  * @param {String/Object} config A string to set only the panel's title, or a config object
54907  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
54908  */
54909 Roo.TreePanel = function(config){
54910     var el = config.el;
54911     var tree = config.tree;
54912     delete config.tree; 
54913     delete config.el; // hopefull!
54914     
54915     // wrapper for IE7 strict & safari scroll issue
54916     
54917     var treeEl = el.createChild();
54918     config.resizeEl = treeEl;
54919     
54920     
54921     
54922     Roo.TreePanel.superclass.constructor.call(this, el, config);
54923  
54924  
54925     this.tree = new Roo.tree.TreePanel(treeEl , tree);
54926     //console.log(tree);
54927     this.on('activate', function()
54928     {
54929         if (this.tree.rendered) {
54930             return;
54931         }
54932         //console.log('render tree');
54933         this.tree.render();
54934     });
54935     // this should not be needed.. - it's actually the 'el' that resizes?
54936     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
54937     
54938     //this.on('resize',  function (cp, w, h) {
54939     //        this.tree.innerCt.setWidth(w);
54940     //        this.tree.innerCt.setHeight(h);
54941     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
54942     //});
54943
54944         
54945     
54946 };
54947
54948 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
54949     fitToFrame : true,
54950     autoScroll : true
54951 });
54952
54953
54954
54955
54956
54957
54958
54959
54960
54961
54962
54963 /*
54964  * Based on:
54965  * Ext JS Library 1.1.1
54966  * Copyright(c) 2006-2007, Ext JS, LLC.
54967  *
54968  * Originally Released Under LGPL - original licence link has changed is not relivant.
54969  *
54970  * Fork - LGPL
54971  * <script type="text/javascript">
54972  */
54973  
54974
54975 /**
54976  * @class Roo.ReaderLayout
54977  * @extends Roo.BorderLayout
54978  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
54979  * center region containing two nested regions (a top one for a list view and one for item preview below),
54980  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
54981  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
54982  * expedites the setup of the overall layout and regions for this common application style.
54983  * Example:
54984  <pre><code>
54985 var reader = new Roo.ReaderLayout();
54986 var CP = Roo.ContentPanel;  // shortcut for adding
54987
54988 reader.beginUpdate();
54989 reader.add("north", new CP("north", "North"));
54990 reader.add("west", new CP("west", {title: "West"}));
54991 reader.add("east", new CP("east", {title: "East"}));
54992
54993 reader.regions.listView.add(new CP("listView", "List"));
54994 reader.regions.preview.add(new CP("preview", "Preview"));
54995 reader.endUpdate();
54996 </code></pre>
54997 * @constructor
54998 * Create a new ReaderLayout
54999 * @param {Object} config Configuration options
55000 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
55001 * document.body if omitted)
55002 */
55003 Roo.ReaderLayout = function(config, renderTo){
55004     var c = config || {size:{}};
55005     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
55006         north: c.north !== false ? Roo.apply({
55007             split:false,
55008             initialSize: 32,
55009             titlebar: false
55010         }, c.north) : false,
55011         west: c.west !== false ? Roo.apply({
55012             split:true,
55013             initialSize: 200,
55014             minSize: 175,
55015             maxSize: 400,
55016             titlebar: true,
55017             collapsible: true,
55018             animate: true,
55019             margins:{left:5,right:0,bottom:5,top:5},
55020             cmargins:{left:5,right:5,bottom:5,top:5}
55021         }, c.west) : false,
55022         east: c.east !== false ? Roo.apply({
55023             split:true,
55024             initialSize: 200,
55025             minSize: 175,
55026             maxSize: 400,
55027             titlebar: true,
55028             collapsible: true,
55029             animate: true,
55030             margins:{left:0,right:5,bottom:5,top:5},
55031             cmargins:{left:5,right:5,bottom:5,top:5}
55032         }, c.east) : false,
55033         center: Roo.apply({
55034             tabPosition: 'top',
55035             autoScroll:false,
55036             closeOnTab: true,
55037             titlebar:false,
55038             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
55039         }, c.center)
55040     });
55041
55042     this.el.addClass('x-reader');
55043
55044     this.beginUpdate();
55045
55046     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
55047         south: c.preview !== false ? Roo.apply({
55048             split:true,
55049             initialSize: 200,
55050             minSize: 100,
55051             autoScroll:true,
55052             collapsible:true,
55053             titlebar: true,
55054             cmargins:{top:5,left:0, right:0, bottom:0}
55055         }, c.preview) : false,
55056         center: Roo.apply({
55057             autoScroll:false,
55058             titlebar:false,
55059             minHeight:200
55060         }, c.listView)
55061     });
55062     this.add('center', new Roo.NestedLayoutPanel(inner,
55063             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
55064
55065     this.endUpdate();
55066
55067     this.regions.preview = inner.getRegion('south');
55068     this.regions.listView = inner.getRegion('center');
55069 };
55070
55071 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
55072  * Based on:
55073  * Ext JS Library 1.1.1
55074  * Copyright(c) 2006-2007, Ext JS, LLC.
55075  *
55076  * Originally Released Under LGPL - original licence link has changed is not relivant.
55077  *
55078  * Fork - LGPL
55079  * <script type="text/javascript">
55080  */
55081  
55082 /**
55083  * @class Roo.grid.Grid
55084  * @extends Roo.util.Observable
55085  * This class represents the primary interface of a component based grid control.
55086  * <br><br>Usage:<pre><code>
55087  var grid = new Roo.grid.Grid("my-container-id", {
55088      ds: myDataStore,
55089      cm: myColModel,
55090      selModel: mySelectionModel,
55091      autoSizeColumns: true,
55092      monitorWindowResize: false,
55093      trackMouseOver: true
55094  });
55095  // set any options
55096  grid.render();
55097  * </code></pre>
55098  * <b>Common Problems:</b><br/>
55099  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
55100  * element will correct this<br/>
55101  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
55102  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
55103  * are unpredictable.<br/>
55104  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
55105  * grid to calculate dimensions/offsets.<br/>
55106   * @constructor
55107  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
55108  * The container MUST have some type of size defined for the grid to fill. The container will be
55109  * automatically set to position relative if it isn't already.
55110  * @param {Object} config A config object that sets properties on this grid.
55111  */
55112 Roo.grid.Grid = function(container, config){
55113         // initialize the container
55114         this.container = Roo.get(container);
55115         this.container.update("");
55116         this.container.setStyle("overflow", "hidden");
55117     this.container.addClass('x-grid-container');
55118
55119     this.id = this.container.id;
55120
55121     Roo.apply(this, config);
55122     // check and correct shorthanded configs
55123     if(this.ds){
55124         this.dataSource = this.ds;
55125         delete this.ds;
55126     }
55127     if(this.cm){
55128         this.colModel = this.cm;
55129         delete this.cm;
55130     }
55131     if(this.sm){
55132         this.selModel = this.sm;
55133         delete this.sm;
55134     }
55135
55136     if (this.selModel) {
55137         this.selModel = Roo.factory(this.selModel, Roo.grid);
55138         this.sm = this.selModel;
55139         this.sm.xmodule = this.xmodule || false;
55140     }
55141     if (typeof(this.colModel.config) == 'undefined') {
55142         this.colModel = new Roo.grid.ColumnModel(this.colModel);
55143         this.cm = this.colModel;
55144         this.cm.xmodule = this.xmodule || false;
55145     }
55146     if (this.dataSource) {
55147         this.dataSource= Roo.factory(this.dataSource, Roo.data);
55148         this.ds = this.dataSource;
55149         this.ds.xmodule = this.xmodule || false;
55150          
55151     }
55152     
55153     
55154     
55155     if(this.width){
55156         this.container.setWidth(this.width);
55157     }
55158
55159     if(this.height){
55160         this.container.setHeight(this.height);
55161     }
55162     /** @private */
55163         this.addEvents({
55164         // raw events
55165         /**
55166          * @event click
55167          * The raw click event for the entire grid.
55168          * @param {Roo.EventObject} e
55169          */
55170         "click" : true,
55171         /**
55172          * @event dblclick
55173          * The raw dblclick event for the entire grid.
55174          * @param {Roo.EventObject} e
55175          */
55176         "dblclick" : true,
55177         /**
55178          * @event contextmenu
55179          * The raw contextmenu event for the entire grid.
55180          * @param {Roo.EventObject} e
55181          */
55182         "contextmenu" : true,
55183         /**
55184          * @event mousedown
55185          * The raw mousedown event for the entire grid.
55186          * @param {Roo.EventObject} e
55187          */
55188         "mousedown" : true,
55189         /**
55190          * @event mouseup
55191          * The raw mouseup event for the entire grid.
55192          * @param {Roo.EventObject} e
55193          */
55194         "mouseup" : true,
55195         /**
55196          * @event mouseover
55197          * The raw mouseover event for the entire grid.
55198          * @param {Roo.EventObject} e
55199          */
55200         "mouseover" : true,
55201         /**
55202          * @event mouseout
55203          * The raw mouseout event for the entire grid.
55204          * @param {Roo.EventObject} e
55205          */
55206         "mouseout" : true,
55207         /**
55208          * @event keypress
55209          * The raw keypress event for the entire grid.
55210          * @param {Roo.EventObject} e
55211          */
55212         "keypress" : true,
55213         /**
55214          * @event keydown
55215          * The raw keydown event for the entire grid.
55216          * @param {Roo.EventObject} e
55217          */
55218         "keydown" : true,
55219
55220         // custom events
55221
55222         /**
55223          * @event cellclick
55224          * Fires when a cell is clicked
55225          * @param {Grid} this
55226          * @param {Number} rowIndex
55227          * @param {Number} columnIndex
55228          * @param {Roo.EventObject} e
55229          */
55230         "cellclick" : true,
55231         /**
55232          * @event celldblclick
55233          * Fires when a cell is double clicked
55234          * @param {Grid} this
55235          * @param {Number} rowIndex
55236          * @param {Number} columnIndex
55237          * @param {Roo.EventObject} e
55238          */
55239         "celldblclick" : true,
55240         /**
55241          * @event rowclick
55242          * Fires when a row is clicked
55243          * @param {Grid} this
55244          * @param {Number} rowIndex
55245          * @param {Roo.EventObject} e
55246          */
55247         "rowclick" : true,
55248         /**
55249          * @event rowdblclick
55250          * Fires when a row is double clicked
55251          * @param {Grid} this
55252          * @param {Number} rowIndex
55253          * @param {Roo.EventObject} e
55254          */
55255         "rowdblclick" : true,
55256         /**
55257          * @event headerclick
55258          * Fires when a header is clicked
55259          * @param {Grid} this
55260          * @param {Number} columnIndex
55261          * @param {Roo.EventObject} e
55262          */
55263         "headerclick" : true,
55264         /**
55265          * @event headerdblclick
55266          * Fires when a header cell is double clicked
55267          * @param {Grid} this
55268          * @param {Number} columnIndex
55269          * @param {Roo.EventObject} e
55270          */
55271         "headerdblclick" : true,
55272         /**
55273          * @event rowcontextmenu
55274          * Fires when a row is right clicked
55275          * @param {Grid} this
55276          * @param {Number} rowIndex
55277          * @param {Roo.EventObject} e
55278          */
55279         "rowcontextmenu" : true,
55280         /**
55281          * @event cellcontextmenu
55282          * Fires when a cell is right clicked
55283          * @param {Grid} this
55284          * @param {Number} rowIndex
55285          * @param {Number} cellIndex
55286          * @param {Roo.EventObject} e
55287          */
55288          "cellcontextmenu" : true,
55289         /**
55290          * @event headercontextmenu
55291          * Fires when a header is right clicked
55292          * @param {Grid} this
55293          * @param {Number} columnIndex
55294          * @param {Roo.EventObject} e
55295          */
55296         "headercontextmenu" : true,
55297         /**
55298          * @event bodyscroll
55299          * Fires when the body element is scrolled
55300          * @param {Number} scrollLeft
55301          * @param {Number} scrollTop
55302          */
55303         "bodyscroll" : true,
55304         /**
55305          * @event columnresize
55306          * Fires when the user resizes a column
55307          * @param {Number} columnIndex
55308          * @param {Number} newSize
55309          */
55310         "columnresize" : true,
55311         /**
55312          * @event columnmove
55313          * Fires when the user moves a column
55314          * @param {Number} oldIndex
55315          * @param {Number} newIndex
55316          */
55317         "columnmove" : true,
55318         /**
55319          * @event startdrag
55320          * Fires when row(s) start being dragged
55321          * @param {Grid} this
55322          * @param {Roo.GridDD} dd The drag drop object
55323          * @param {event} e The raw browser event
55324          */
55325         "startdrag" : true,
55326         /**
55327          * @event enddrag
55328          * Fires when a drag operation is complete
55329          * @param {Grid} this
55330          * @param {Roo.GridDD} dd The drag drop object
55331          * @param {event} e The raw browser event
55332          */
55333         "enddrag" : true,
55334         /**
55335          * @event dragdrop
55336          * Fires when dragged row(s) are dropped on a valid DD target
55337          * @param {Grid} this
55338          * @param {Roo.GridDD} dd The drag drop object
55339          * @param {String} targetId The target drag drop object
55340          * @param {event} e The raw browser event
55341          */
55342         "dragdrop" : true,
55343         /**
55344          * @event dragover
55345          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
55346          * @param {Grid} this
55347          * @param {Roo.GridDD} dd The drag drop object
55348          * @param {String} targetId The target drag drop object
55349          * @param {event} e The raw browser event
55350          */
55351         "dragover" : true,
55352         /**
55353          * @event dragenter
55354          *  Fires when the dragged row(s) first cross another DD target while being dragged
55355          * @param {Grid} this
55356          * @param {Roo.GridDD} dd The drag drop object
55357          * @param {String} targetId The target drag drop object
55358          * @param {event} e The raw browser event
55359          */
55360         "dragenter" : true,
55361         /**
55362          * @event dragout
55363          * Fires when the dragged row(s) leave another DD target while being dragged
55364          * @param {Grid} this
55365          * @param {Roo.GridDD} dd The drag drop object
55366          * @param {String} targetId The target drag drop object
55367          * @param {event} e The raw browser event
55368          */
55369         "dragout" : true,
55370         /**
55371          * @event rowclass
55372          * Fires when a row is rendered, so you can change add a style to it.
55373          * @param {GridView} gridview   The grid view
55374          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
55375          */
55376         'rowclass' : true,
55377
55378         /**
55379          * @event render
55380          * Fires when the grid is rendered
55381          * @param {Grid} grid
55382          */
55383         'render' : true
55384     });
55385
55386     Roo.grid.Grid.superclass.constructor.call(this);
55387 };
55388 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
55389     
55390     /**
55391      * @cfg {String} ddGroup - drag drop group.
55392      */
55393
55394     /**
55395      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
55396      */
55397     minColumnWidth : 25,
55398
55399     /**
55400      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
55401      * <b>on initial render.</b> It is more efficient to explicitly size the columns
55402      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
55403      */
55404     autoSizeColumns : false,
55405
55406     /**
55407      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
55408      */
55409     autoSizeHeaders : true,
55410
55411     /**
55412      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
55413      */
55414     monitorWindowResize : true,
55415
55416     /**
55417      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
55418      * rows measured to get a columns size. Default is 0 (all rows).
55419      */
55420     maxRowsToMeasure : 0,
55421
55422     /**
55423      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
55424      */
55425     trackMouseOver : true,
55426
55427     /**
55428     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
55429     */
55430     
55431     /**
55432     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
55433     */
55434     enableDragDrop : false,
55435     
55436     /**
55437     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
55438     */
55439     enableColumnMove : true,
55440     
55441     /**
55442     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
55443     */
55444     enableColumnHide : true,
55445     
55446     /**
55447     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
55448     */
55449     enableRowHeightSync : false,
55450     
55451     /**
55452     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
55453     */
55454     stripeRows : true,
55455     
55456     /**
55457     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
55458     */
55459     autoHeight : false,
55460
55461     /**
55462      * @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.
55463      */
55464     autoExpandColumn : false,
55465
55466     /**
55467     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
55468     * Default is 50.
55469     */
55470     autoExpandMin : 50,
55471
55472     /**
55473     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
55474     */
55475     autoExpandMax : 1000,
55476
55477     /**
55478     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
55479     */
55480     view : null,
55481
55482     /**
55483     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
55484     */
55485     loadMask : false,
55486     /**
55487     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
55488     */
55489     dropTarget: false,
55490     
55491    
55492     
55493     // private
55494     rendered : false,
55495
55496     /**
55497     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
55498     * of a fixed width. Default is false.
55499     */
55500     /**
55501     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
55502     */
55503     /**
55504      * Called once after all setup has been completed and the grid is ready to be rendered.
55505      * @return {Roo.grid.Grid} this
55506      */
55507     render : function()
55508     {
55509         var c = this.container;
55510         // try to detect autoHeight/width mode
55511         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
55512             this.autoHeight = true;
55513         }
55514         var view = this.getView();
55515         view.init(this);
55516
55517         c.on("click", this.onClick, this);
55518         c.on("dblclick", this.onDblClick, this);
55519         c.on("contextmenu", this.onContextMenu, this);
55520         c.on("keydown", this.onKeyDown, this);
55521         if (Roo.isTouch) {
55522             c.on("touchstart", this.onTouchStart, this);
55523         }
55524
55525         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
55526
55527         this.getSelectionModel().init(this);
55528
55529         view.render();
55530
55531         if(this.loadMask){
55532             this.loadMask = new Roo.LoadMask(this.container,
55533                     Roo.apply({store:this.dataSource}, this.loadMask));
55534         }
55535         
55536         
55537         if (this.toolbar && this.toolbar.xtype) {
55538             this.toolbar.container = this.getView().getHeaderPanel(true);
55539             this.toolbar = new Roo.Toolbar(this.toolbar);
55540         }
55541         if (this.footer && this.footer.xtype) {
55542             this.footer.dataSource = this.getDataSource();
55543             this.footer.container = this.getView().getFooterPanel(true);
55544             this.footer = Roo.factory(this.footer, Roo);
55545         }
55546         if (this.dropTarget && this.dropTarget.xtype) {
55547             delete this.dropTarget.xtype;
55548             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
55549         }
55550         
55551         
55552         this.rendered = true;
55553         this.fireEvent('render', this);
55554         return this;
55555     },
55556
55557     /**
55558      * Reconfigures the grid to use a different Store and Column Model.
55559      * The View will be bound to the new objects and refreshed.
55560      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
55561      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
55562      */
55563     reconfigure : function(dataSource, colModel){
55564         if(this.loadMask){
55565             this.loadMask.destroy();
55566             this.loadMask = new Roo.LoadMask(this.container,
55567                     Roo.apply({store:dataSource}, this.loadMask));
55568         }
55569         this.view.bind(dataSource, colModel);
55570         this.dataSource = dataSource;
55571         this.colModel = colModel;
55572         this.view.refresh(true);
55573     },
55574     /**
55575      * addColumns
55576      * Add's a column, default at the end..
55577      
55578      * @param {int} position to add (default end)
55579      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
55580      */
55581     addColumns : function(pos, ar)
55582     {
55583         
55584         for (var i =0;i< ar.length;i++) {
55585             var cfg = ar[i];
55586             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
55587             this.cm.lookup[cfg.id] = cfg;
55588         }
55589         
55590         
55591         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
55592             pos = this.cm.config.length; //this.cm.config.push(cfg);
55593         } 
55594         pos = Math.max(0,pos);
55595         ar.unshift(0);
55596         ar.unshift(pos);
55597         this.cm.config.splice.apply(this.cm.config, ar);
55598         
55599         
55600         
55601         this.view.generateRules(this.cm);
55602         this.view.refresh(true);
55603         
55604     },
55605     
55606     
55607     
55608     
55609     // private
55610     onKeyDown : function(e){
55611         this.fireEvent("keydown", e);
55612     },
55613
55614     /**
55615      * Destroy this grid.
55616      * @param {Boolean} removeEl True to remove the element
55617      */
55618     destroy : function(removeEl, keepListeners){
55619         if(this.loadMask){
55620             this.loadMask.destroy();
55621         }
55622         var c = this.container;
55623         c.removeAllListeners();
55624         this.view.destroy();
55625         this.colModel.purgeListeners();
55626         if(!keepListeners){
55627             this.purgeListeners();
55628         }
55629         c.update("");
55630         if(removeEl === true){
55631             c.remove();
55632         }
55633     },
55634
55635     // private
55636     processEvent : function(name, e){
55637         // does this fire select???
55638         //Roo.log('grid:processEvent '  + name);
55639         
55640         if (name != 'touchstart' ) {
55641             this.fireEvent(name, e);    
55642         }
55643         
55644         var t = e.getTarget();
55645         var v = this.view;
55646         var header = v.findHeaderIndex(t);
55647         if(header !== false){
55648             var ename = name == 'touchstart' ? 'click' : name;
55649              
55650             this.fireEvent("header" + ename, this, header, e);
55651         }else{
55652             var row = v.findRowIndex(t);
55653             var cell = v.findCellIndex(t);
55654             if (name == 'touchstart') {
55655                 // first touch is always a click.
55656                 // hopefull this happens after selection is updated.?
55657                 name = false;
55658                 
55659                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
55660                     var cs = this.selModel.getSelectedCell();
55661                     if (row == cs[0] && cell == cs[1]){
55662                         name = 'dblclick';
55663                     }
55664                 }
55665                 if (typeof(this.selModel.getSelections) != 'undefined') {
55666                     var cs = this.selModel.getSelections();
55667                     var ds = this.dataSource;
55668                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
55669                         name = 'dblclick';
55670                     }
55671                 }
55672                 if (!name) {
55673                     return;
55674                 }
55675             }
55676             
55677             
55678             if(row !== false){
55679                 this.fireEvent("row" + name, this, row, e);
55680                 if(cell !== false){
55681                     this.fireEvent("cell" + name, this, row, cell, e);
55682                 }
55683             }
55684         }
55685     },
55686
55687     // private
55688     onClick : function(e){
55689         this.processEvent("click", e);
55690     },
55691    // private
55692     onTouchStart : function(e){
55693         this.processEvent("touchstart", e);
55694     },
55695
55696     // private
55697     onContextMenu : function(e, t){
55698         this.processEvent("contextmenu", e);
55699     },
55700
55701     // private
55702     onDblClick : function(e){
55703         this.processEvent("dblclick", e);
55704     },
55705
55706     // private
55707     walkCells : function(row, col, step, fn, scope){
55708         var cm = this.colModel, clen = cm.getColumnCount();
55709         var ds = this.dataSource, rlen = ds.getCount(), first = true;
55710         if(step < 0){
55711             if(col < 0){
55712                 row--;
55713                 first = false;
55714             }
55715             while(row >= 0){
55716                 if(!first){
55717                     col = clen-1;
55718                 }
55719                 first = false;
55720                 while(col >= 0){
55721                     if(fn.call(scope || this, row, col, cm) === true){
55722                         return [row, col];
55723                     }
55724                     col--;
55725                 }
55726                 row--;
55727             }
55728         } else {
55729             if(col >= clen){
55730                 row++;
55731                 first = false;
55732             }
55733             while(row < rlen){
55734                 if(!first){
55735                     col = 0;
55736                 }
55737                 first = false;
55738                 while(col < clen){
55739                     if(fn.call(scope || this, row, col, cm) === true){
55740                         return [row, col];
55741                     }
55742                     col++;
55743                 }
55744                 row++;
55745             }
55746         }
55747         return null;
55748     },
55749
55750     // private
55751     getSelections : function(){
55752         return this.selModel.getSelections();
55753     },
55754
55755     /**
55756      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
55757      * but if manual update is required this method will initiate it.
55758      */
55759     autoSize : function(){
55760         if(this.rendered){
55761             this.view.layout();
55762             if(this.view.adjustForScroll){
55763                 this.view.adjustForScroll();
55764             }
55765         }
55766     },
55767
55768     /**
55769      * Returns the grid's underlying element.
55770      * @return {Element} The element
55771      */
55772     getGridEl : function(){
55773         return this.container;
55774     },
55775
55776     // private for compatibility, overridden by editor grid
55777     stopEditing : function(){},
55778
55779     /**
55780      * Returns the grid's SelectionModel.
55781      * @return {SelectionModel}
55782      */
55783     getSelectionModel : function(){
55784         if(!this.selModel){
55785             this.selModel = new Roo.grid.RowSelectionModel();
55786         }
55787         return this.selModel;
55788     },
55789
55790     /**
55791      * Returns the grid's DataSource.
55792      * @return {DataSource}
55793      */
55794     getDataSource : function(){
55795         return this.dataSource;
55796     },
55797
55798     /**
55799      * Returns the grid's ColumnModel.
55800      * @return {ColumnModel}
55801      */
55802     getColumnModel : function(){
55803         return this.colModel;
55804     },
55805
55806     /**
55807      * Returns the grid's GridView object.
55808      * @return {GridView}
55809      */
55810     getView : function(){
55811         if(!this.view){
55812             this.view = new Roo.grid.GridView(this.viewConfig);
55813         }
55814         return this.view;
55815     },
55816     /**
55817      * Called to get grid's drag proxy text, by default returns this.ddText.
55818      * @return {String}
55819      */
55820     getDragDropText : function(){
55821         var count = this.selModel.getCount();
55822         return String.format(this.ddText, count, count == 1 ? '' : 's');
55823     }
55824 });
55825 /**
55826  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
55827  * %0 is replaced with the number of selected rows.
55828  * @type String
55829  */
55830 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
55831  * Based on:
55832  * Ext JS Library 1.1.1
55833  * Copyright(c) 2006-2007, Ext JS, LLC.
55834  *
55835  * Originally Released Under LGPL - original licence link has changed is not relivant.
55836  *
55837  * Fork - LGPL
55838  * <script type="text/javascript">
55839  */
55840  
55841 Roo.grid.AbstractGridView = function(){
55842         this.grid = null;
55843         
55844         this.events = {
55845             "beforerowremoved" : true,
55846             "beforerowsinserted" : true,
55847             "beforerefresh" : true,
55848             "rowremoved" : true,
55849             "rowsinserted" : true,
55850             "rowupdated" : true,
55851             "refresh" : true
55852         };
55853     Roo.grid.AbstractGridView.superclass.constructor.call(this);
55854 };
55855
55856 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
55857     rowClass : "x-grid-row",
55858     cellClass : "x-grid-cell",
55859     tdClass : "x-grid-td",
55860     hdClass : "x-grid-hd",
55861     splitClass : "x-grid-hd-split",
55862     
55863     init: function(grid){
55864         this.grid = grid;
55865                 var cid = this.grid.getGridEl().id;
55866         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
55867         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
55868         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
55869         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
55870         },
55871         
55872     getColumnRenderers : function(){
55873         var renderers = [];
55874         var cm = this.grid.colModel;
55875         var colCount = cm.getColumnCount();
55876         for(var i = 0; i < colCount; i++){
55877             renderers[i] = cm.getRenderer(i);
55878         }
55879         return renderers;
55880     },
55881     
55882     getColumnIds : function(){
55883         var ids = [];
55884         var cm = this.grid.colModel;
55885         var colCount = cm.getColumnCount();
55886         for(var i = 0; i < colCount; i++){
55887             ids[i] = cm.getColumnId(i);
55888         }
55889         return ids;
55890     },
55891     
55892     getDataIndexes : function(){
55893         if(!this.indexMap){
55894             this.indexMap = this.buildIndexMap();
55895         }
55896         return this.indexMap.colToData;
55897     },
55898     
55899     getColumnIndexByDataIndex : function(dataIndex){
55900         if(!this.indexMap){
55901             this.indexMap = this.buildIndexMap();
55902         }
55903         return this.indexMap.dataToCol[dataIndex];
55904     },
55905     
55906     /**
55907      * Set a css style for a column dynamically. 
55908      * @param {Number} colIndex The index of the column
55909      * @param {String} name The css property name
55910      * @param {String} value The css value
55911      */
55912     setCSSStyle : function(colIndex, name, value){
55913         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
55914         Roo.util.CSS.updateRule(selector, name, value);
55915     },
55916     
55917     generateRules : function(cm){
55918         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
55919         Roo.util.CSS.removeStyleSheet(rulesId);
55920         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55921             var cid = cm.getColumnId(i);
55922             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
55923                          this.tdSelector, cid, " {\n}\n",
55924                          this.hdSelector, cid, " {\n}\n",
55925                          this.splitSelector, cid, " {\n}\n");
55926         }
55927         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
55928     }
55929 });/*
55930  * Based on:
55931  * Ext JS Library 1.1.1
55932  * Copyright(c) 2006-2007, Ext JS, LLC.
55933  *
55934  * Originally Released Under LGPL - original licence link has changed is not relivant.
55935  *
55936  * Fork - LGPL
55937  * <script type="text/javascript">
55938  */
55939
55940 // private
55941 // This is a support class used internally by the Grid components
55942 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
55943     this.grid = grid;
55944     this.view = grid.getView();
55945     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
55946     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
55947     if(hd2){
55948         this.setHandleElId(Roo.id(hd));
55949         this.setOuterHandleElId(Roo.id(hd2));
55950     }
55951     this.scroll = false;
55952 };
55953 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
55954     maxDragWidth: 120,
55955     getDragData : function(e){
55956         var t = Roo.lib.Event.getTarget(e);
55957         var h = this.view.findHeaderCell(t);
55958         if(h){
55959             return {ddel: h.firstChild, header:h};
55960         }
55961         return false;
55962     },
55963
55964     onInitDrag : function(e){
55965         this.view.headersDisabled = true;
55966         var clone = this.dragData.ddel.cloneNode(true);
55967         clone.id = Roo.id();
55968         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
55969         this.proxy.update(clone);
55970         return true;
55971     },
55972
55973     afterValidDrop : function(){
55974         var v = this.view;
55975         setTimeout(function(){
55976             v.headersDisabled = false;
55977         }, 50);
55978     },
55979
55980     afterInvalidDrop : function(){
55981         var v = this.view;
55982         setTimeout(function(){
55983             v.headersDisabled = false;
55984         }, 50);
55985     }
55986 });
55987 /*
55988  * Based on:
55989  * Ext JS Library 1.1.1
55990  * Copyright(c) 2006-2007, Ext JS, LLC.
55991  *
55992  * Originally Released Under LGPL - original licence link has changed is not relivant.
55993  *
55994  * Fork - LGPL
55995  * <script type="text/javascript">
55996  */
55997 // private
55998 // This is a support class used internally by the Grid components
55999 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
56000     this.grid = grid;
56001     this.view = grid.getView();
56002     // split the proxies so they don't interfere with mouse events
56003     this.proxyTop = Roo.DomHelper.append(document.body, {
56004         cls:"col-move-top", html:"&#160;"
56005     }, true);
56006     this.proxyBottom = Roo.DomHelper.append(document.body, {
56007         cls:"col-move-bottom", html:"&#160;"
56008     }, true);
56009     this.proxyTop.hide = this.proxyBottom.hide = function(){
56010         this.setLeftTop(-100,-100);
56011         this.setStyle("visibility", "hidden");
56012     };
56013     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
56014     // temporarily disabled
56015     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
56016     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
56017 };
56018 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
56019     proxyOffsets : [-4, -9],
56020     fly: Roo.Element.fly,
56021
56022     getTargetFromEvent : function(e){
56023         var t = Roo.lib.Event.getTarget(e);
56024         var cindex = this.view.findCellIndex(t);
56025         if(cindex !== false){
56026             return this.view.getHeaderCell(cindex);
56027         }
56028         return null;
56029     },
56030
56031     nextVisible : function(h){
56032         var v = this.view, cm = this.grid.colModel;
56033         h = h.nextSibling;
56034         while(h){
56035             if(!cm.isHidden(v.getCellIndex(h))){
56036                 return h;
56037             }
56038             h = h.nextSibling;
56039         }
56040         return null;
56041     },
56042
56043     prevVisible : function(h){
56044         var v = this.view, cm = this.grid.colModel;
56045         h = h.prevSibling;
56046         while(h){
56047             if(!cm.isHidden(v.getCellIndex(h))){
56048                 return h;
56049             }
56050             h = h.prevSibling;
56051         }
56052         return null;
56053     },
56054
56055     positionIndicator : function(h, n, e){
56056         var x = Roo.lib.Event.getPageX(e);
56057         var r = Roo.lib.Dom.getRegion(n.firstChild);
56058         var px, pt, py = r.top + this.proxyOffsets[1];
56059         if((r.right - x) <= (r.right-r.left)/2){
56060             px = r.right+this.view.borderWidth;
56061             pt = "after";
56062         }else{
56063             px = r.left;
56064             pt = "before";
56065         }
56066         var oldIndex = this.view.getCellIndex(h);
56067         var newIndex = this.view.getCellIndex(n);
56068
56069         if(this.grid.colModel.isFixed(newIndex)){
56070             return false;
56071         }
56072
56073         var locked = this.grid.colModel.isLocked(newIndex);
56074
56075         if(pt == "after"){
56076             newIndex++;
56077         }
56078         if(oldIndex < newIndex){
56079             newIndex--;
56080         }
56081         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
56082             return false;
56083         }
56084         px +=  this.proxyOffsets[0];
56085         this.proxyTop.setLeftTop(px, py);
56086         this.proxyTop.show();
56087         if(!this.bottomOffset){
56088             this.bottomOffset = this.view.mainHd.getHeight();
56089         }
56090         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
56091         this.proxyBottom.show();
56092         return pt;
56093     },
56094
56095     onNodeEnter : function(n, dd, e, data){
56096         if(data.header != n){
56097             this.positionIndicator(data.header, n, e);
56098         }
56099     },
56100
56101     onNodeOver : function(n, dd, e, data){
56102         var result = false;
56103         if(data.header != n){
56104             result = this.positionIndicator(data.header, n, e);
56105         }
56106         if(!result){
56107             this.proxyTop.hide();
56108             this.proxyBottom.hide();
56109         }
56110         return result ? this.dropAllowed : this.dropNotAllowed;
56111     },
56112
56113     onNodeOut : function(n, dd, e, data){
56114         this.proxyTop.hide();
56115         this.proxyBottom.hide();
56116     },
56117
56118     onNodeDrop : function(n, dd, e, data){
56119         var h = data.header;
56120         if(h != n){
56121             var cm = this.grid.colModel;
56122             var x = Roo.lib.Event.getPageX(e);
56123             var r = Roo.lib.Dom.getRegion(n.firstChild);
56124             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
56125             var oldIndex = this.view.getCellIndex(h);
56126             var newIndex = this.view.getCellIndex(n);
56127             var locked = cm.isLocked(newIndex);
56128             if(pt == "after"){
56129                 newIndex++;
56130             }
56131             if(oldIndex < newIndex){
56132                 newIndex--;
56133             }
56134             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
56135                 return false;
56136             }
56137             cm.setLocked(oldIndex, locked, true);
56138             cm.moveColumn(oldIndex, newIndex);
56139             this.grid.fireEvent("columnmove", oldIndex, newIndex);
56140             return true;
56141         }
56142         return false;
56143     }
56144 });
56145 /*
56146  * Based on:
56147  * Ext JS Library 1.1.1
56148  * Copyright(c) 2006-2007, Ext JS, LLC.
56149  *
56150  * Originally Released Under LGPL - original licence link has changed is not relivant.
56151  *
56152  * Fork - LGPL
56153  * <script type="text/javascript">
56154  */
56155   
56156 /**
56157  * @class Roo.grid.GridView
56158  * @extends Roo.util.Observable
56159  *
56160  * @constructor
56161  * @param {Object} config
56162  */
56163 Roo.grid.GridView = function(config){
56164     Roo.grid.GridView.superclass.constructor.call(this);
56165     this.el = null;
56166
56167     Roo.apply(this, config);
56168 };
56169
56170 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
56171
56172     unselectable :  'unselectable="on"',
56173     unselectableCls :  'x-unselectable',
56174     
56175     
56176     rowClass : "x-grid-row",
56177
56178     cellClass : "x-grid-col",
56179
56180     tdClass : "x-grid-td",
56181
56182     hdClass : "x-grid-hd",
56183
56184     splitClass : "x-grid-split",
56185
56186     sortClasses : ["sort-asc", "sort-desc"],
56187
56188     enableMoveAnim : false,
56189
56190     hlColor: "C3DAF9",
56191
56192     dh : Roo.DomHelper,
56193
56194     fly : Roo.Element.fly,
56195
56196     css : Roo.util.CSS,
56197
56198     borderWidth: 1,
56199
56200     splitOffset: 3,
56201
56202     scrollIncrement : 22,
56203
56204     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
56205
56206     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
56207
56208     bind : function(ds, cm){
56209         if(this.ds){
56210             this.ds.un("load", this.onLoad, this);
56211             this.ds.un("datachanged", this.onDataChange, this);
56212             this.ds.un("add", this.onAdd, this);
56213             this.ds.un("remove", this.onRemove, this);
56214             this.ds.un("update", this.onUpdate, this);
56215             this.ds.un("clear", this.onClear, this);
56216         }
56217         if(ds){
56218             ds.on("load", this.onLoad, this);
56219             ds.on("datachanged", this.onDataChange, this);
56220             ds.on("add", this.onAdd, this);
56221             ds.on("remove", this.onRemove, this);
56222             ds.on("update", this.onUpdate, this);
56223             ds.on("clear", this.onClear, this);
56224         }
56225         this.ds = ds;
56226
56227         if(this.cm){
56228             this.cm.un("widthchange", this.onColWidthChange, this);
56229             this.cm.un("headerchange", this.onHeaderChange, this);
56230             this.cm.un("hiddenchange", this.onHiddenChange, this);
56231             this.cm.un("columnmoved", this.onColumnMove, this);
56232             this.cm.un("columnlockchange", this.onColumnLock, this);
56233         }
56234         if(cm){
56235             this.generateRules(cm);
56236             cm.on("widthchange", this.onColWidthChange, this);
56237             cm.on("headerchange", this.onHeaderChange, this);
56238             cm.on("hiddenchange", this.onHiddenChange, this);
56239             cm.on("columnmoved", this.onColumnMove, this);
56240             cm.on("columnlockchange", this.onColumnLock, this);
56241         }
56242         this.cm = cm;
56243     },
56244
56245     init: function(grid){
56246         Roo.grid.GridView.superclass.init.call(this, grid);
56247
56248         this.bind(grid.dataSource, grid.colModel);
56249
56250         grid.on("headerclick", this.handleHeaderClick, this);
56251
56252         if(grid.trackMouseOver){
56253             grid.on("mouseover", this.onRowOver, this);
56254             grid.on("mouseout", this.onRowOut, this);
56255         }
56256         grid.cancelTextSelection = function(){};
56257         this.gridId = grid.id;
56258
56259         var tpls = this.templates || {};
56260
56261         if(!tpls.master){
56262             tpls.master = new Roo.Template(
56263                '<div class="x-grid" hidefocus="true">',
56264                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
56265                   '<div class="x-grid-topbar"></div>',
56266                   '<div class="x-grid-scroller"><div></div></div>',
56267                   '<div class="x-grid-locked">',
56268                       '<div class="x-grid-header">{lockedHeader}</div>',
56269                       '<div class="x-grid-body">{lockedBody}</div>',
56270                   "</div>",
56271                   '<div class="x-grid-viewport">',
56272                       '<div class="x-grid-header">{header}</div>',
56273                       '<div class="x-grid-body">{body}</div>',
56274                   "</div>",
56275                   '<div class="x-grid-bottombar"></div>',
56276                  
56277                   '<div class="x-grid-resize-proxy">&#160;</div>',
56278                "</div>"
56279             );
56280             tpls.master.disableformats = true;
56281         }
56282
56283         if(!tpls.header){
56284             tpls.header = new Roo.Template(
56285                '<table border="0" cellspacing="0" cellpadding="0">',
56286                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
56287                "</table>{splits}"
56288             );
56289             tpls.header.disableformats = true;
56290         }
56291         tpls.header.compile();
56292
56293         if(!tpls.hcell){
56294             tpls.hcell = new Roo.Template(
56295                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
56296                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
56297                 "</div></td>"
56298              );
56299              tpls.hcell.disableFormats = true;
56300         }
56301         tpls.hcell.compile();
56302
56303         if(!tpls.hsplit){
56304             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
56305                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
56306             tpls.hsplit.disableFormats = true;
56307         }
56308         tpls.hsplit.compile();
56309
56310         if(!tpls.body){
56311             tpls.body = new Roo.Template(
56312                '<table border="0" cellspacing="0" cellpadding="0">',
56313                "<tbody>{rows}</tbody>",
56314                "</table>"
56315             );
56316             tpls.body.disableFormats = true;
56317         }
56318         tpls.body.compile();
56319
56320         if(!tpls.row){
56321             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
56322             tpls.row.disableFormats = true;
56323         }
56324         tpls.row.compile();
56325
56326         if(!tpls.cell){
56327             tpls.cell = new Roo.Template(
56328                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
56329                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
56330                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
56331                 "</td>"
56332             );
56333             tpls.cell.disableFormats = true;
56334         }
56335         tpls.cell.compile();
56336
56337         this.templates = tpls;
56338     },
56339
56340     // remap these for backwards compat
56341     onColWidthChange : function(){
56342         this.updateColumns.apply(this, arguments);
56343     },
56344     onHeaderChange : function(){
56345         this.updateHeaders.apply(this, arguments);
56346     }, 
56347     onHiddenChange : function(){
56348         this.handleHiddenChange.apply(this, arguments);
56349     },
56350     onColumnMove : function(){
56351         this.handleColumnMove.apply(this, arguments);
56352     },
56353     onColumnLock : function(){
56354         this.handleLockChange.apply(this, arguments);
56355     },
56356
56357     onDataChange : function(){
56358         this.refresh();
56359         this.updateHeaderSortState();
56360     },
56361
56362     onClear : function(){
56363         this.refresh();
56364     },
56365
56366     onUpdate : function(ds, record){
56367         this.refreshRow(record);
56368     },
56369
56370     refreshRow : function(record){
56371         var ds = this.ds, index;
56372         if(typeof record == 'number'){
56373             index = record;
56374             record = ds.getAt(index);
56375         }else{
56376             index = ds.indexOf(record);
56377         }
56378         this.insertRows(ds, index, index, true);
56379         this.onRemove(ds, record, index+1, true);
56380         this.syncRowHeights(index, index);
56381         this.layout();
56382         this.fireEvent("rowupdated", this, index, record);
56383     },
56384
56385     onAdd : function(ds, records, index){
56386         this.insertRows(ds, index, index + (records.length-1));
56387     },
56388
56389     onRemove : function(ds, record, index, isUpdate){
56390         if(isUpdate !== true){
56391             this.fireEvent("beforerowremoved", this, index, record);
56392         }
56393         var bt = this.getBodyTable(), lt = this.getLockedTable();
56394         if(bt.rows[index]){
56395             bt.firstChild.removeChild(bt.rows[index]);
56396         }
56397         if(lt.rows[index]){
56398             lt.firstChild.removeChild(lt.rows[index]);
56399         }
56400         if(isUpdate !== true){
56401             this.stripeRows(index);
56402             this.syncRowHeights(index, index);
56403             this.layout();
56404             this.fireEvent("rowremoved", this, index, record);
56405         }
56406     },
56407
56408     onLoad : function(){
56409         this.scrollToTop();
56410     },
56411
56412     /**
56413      * Scrolls the grid to the top
56414      */
56415     scrollToTop : function(){
56416         if(this.scroller){
56417             this.scroller.dom.scrollTop = 0;
56418             this.syncScroll();
56419         }
56420     },
56421
56422     /**
56423      * Gets a panel in the header of the grid that can be used for toolbars etc.
56424      * After modifying the contents of this panel a call to grid.autoSize() may be
56425      * required to register any changes in size.
56426      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
56427      * @return Roo.Element
56428      */
56429     getHeaderPanel : function(doShow){
56430         if(doShow){
56431             this.headerPanel.show();
56432         }
56433         return this.headerPanel;
56434     },
56435
56436     /**
56437      * Gets a panel in the footer of the grid that can be used for toolbars etc.
56438      * After modifying the contents of this panel a call to grid.autoSize() may be
56439      * required to register any changes in size.
56440      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
56441      * @return Roo.Element
56442      */
56443     getFooterPanel : function(doShow){
56444         if(doShow){
56445             this.footerPanel.show();
56446         }
56447         return this.footerPanel;
56448     },
56449
56450     initElements : function(){
56451         var E = Roo.Element;
56452         var el = this.grid.getGridEl().dom.firstChild;
56453         var cs = el.childNodes;
56454
56455         this.el = new E(el);
56456         
56457          this.focusEl = new E(el.firstChild);
56458         this.focusEl.swallowEvent("click", true);
56459         
56460         this.headerPanel = new E(cs[1]);
56461         this.headerPanel.enableDisplayMode("block");
56462
56463         this.scroller = new E(cs[2]);
56464         this.scrollSizer = new E(this.scroller.dom.firstChild);
56465
56466         this.lockedWrap = new E(cs[3]);
56467         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
56468         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
56469
56470         this.mainWrap = new E(cs[4]);
56471         this.mainHd = new E(this.mainWrap.dom.firstChild);
56472         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
56473
56474         this.footerPanel = new E(cs[5]);
56475         this.footerPanel.enableDisplayMode("block");
56476
56477         this.resizeProxy = new E(cs[6]);
56478
56479         this.headerSelector = String.format(
56480            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
56481            this.lockedHd.id, this.mainHd.id
56482         );
56483
56484         this.splitterSelector = String.format(
56485            '#{0} div.x-grid-split, #{1} div.x-grid-split',
56486            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
56487         );
56488     },
56489     idToCssName : function(s)
56490     {
56491         return s.replace(/[^a-z0-9]+/ig, '-');
56492     },
56493
56494     getHeaderCell : function(index){
56495         return Roo.DomQuery.select(this.headerSelector)[index];
56496     },
56497
56498     getHeaderCellMeasure : function(index){
56499         return this.getHeaderCell(index).firstChild;
56500     },
56501
56502     getHeaderCellText : function(index){
56503         return this.getHeaderCell(index).firstChild.firstChild;
56504     },
56505
56506     getLockedTable : function(){
56507         return this.lockedBody.dom.firstChild;
56508     },
56509
56510     getBodyTable : function(){
56511         return this.mainBody.dom.firstChild;
56512     },
56513
56514     getLockedRow : function(index){
56515         return this.getLockedTable().rows[index];
56516     },
56517
56518     getRow : function(index){
56519         return this.getBodyTable().rows[index];
56520     },
56521
56522     getRowComposite : function(index){
56523         if(!this.rowEl){
56524             this.rowEl = new Roo.CompositeElementLite();
56525         }
56526         var els = [], lrow, mrow;
56527         if(lrow = this.getLockedRow(index)){
56528             els.push(lrow);
56529         }
56530         if(mrow = this.getRow(index)){
56531             els.push(mrow);
56532         }
56533         this.rowEl.elements = els;
56534         return this.rowEl;
56535     },
56536     /**
56537      * Gets the 'td' of the cell
56538      * 
56539      * @param {Integer} rowIndex row to select
56540      * @param {Integer} colIndex column to select
56541      * 
56542      * @return {Object} 
56543      */
56544     getCell : function(rowIndex, colIndex){
56545         var locked = this.cm.getLockedCount();
56546         var source;
56547         if(colIndex < locked){
56548             source = this.lockedBody.dom.firstChild;
56549         }else{
56550             source = this.mainBody.dom.firstChild;
56551             colIndex -= locked;
56552         }
56553         return source.rows[rowIndex].childNodes[colIndex];
56554     },
56555
56556     getCellText : function(rowIndex, colIndex){
56557         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
56558     },
56559
56560     getCellBox : function(cell){
56561         var b = this.fly(cell).getBox();
56562         if(Roo.isOpera){ // opera fails to report the Y
56563             b.y = cell.offsetTop + this.mainBody.getY();
56564         }
56565         return b;
56566     },
56567
56568     getCellIndex : function(cell){
56569         var id = String(cell.className).match(this.cellRE);
56570         if(id){
56571             return parseInt(id[1], 10);
56572         }
56573         return 0;
56574     },
56575
56576     findHeaderIndex : function(n){
56577         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
56578         return r ? this.getCellIndex(r) : false;
56579     },
56580
56581     findHeaderCell : function(n){
56582         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
56583         return r ? r : false;
56584     },
56585
56586     findRowIndex : function(n){
56587         if(!n){
56588             return false;
56589         }
56590         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
56591         return r ? r.rowIndex : false;
56592     },
56593
56594     findCellIndex : function(node){
56595         var stop = this.el.dom;
56596         while(node && node != stop){
56597             if(this.findRE.test(node.className)){
56598                 return this.getCellIndex(node);
56599             }
56600             node = node.parentNode;
56601         }
56602         return false;
56603     },
56604
56605     getColumnId : function(index){
56606         return this.cm.getColumnId(index);
56607     },
56608
56609     getSplitters : function()
56610     {
56611         if(this.splitterSelector){
56612            return Roo.DomQuery.select(this.splitterSelector);
56613         }else{
56614             return null;
56615       }
56616     },
56617
56618     getSplitter : function(index){
56619         return this.getSplitters()[index];
56620     },
56621
56622     onRowOver : function(e, t){
56623         var row;
56624         if((row = this.findRowIndex(t)) !== false){
56625             this.getRowComposite(row).addClass("x-grid-row-over");
56626         }
56627     },
56628
56629     onRowOut : function(e, t){
56630         var row;
56631         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
56632             this.getRowComposite(row).removeClass("x-grid-row-over");
56633         }
56634     },
56635
56636     renderHeaders : function(){
56637         var cm = this.cm;
56638         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
56639         var cb = [], lb = [], sb = [], lsb = [], p = {};
56640         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56641             p.cellId = "x-grid-hd-0-" + i;
56642             p.splitId = "x-grid-csplit-0-" + i;
56643             p.id = cm.getColumnId(i);
56644             p.value = cm.getColumnHeader(i) || "";
56645             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
56646             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
56647             if(!cm.isLocked(i)){
56648                 cb[cb.length] = ct.apply(p);
56649                 sb[sb.length] = st.apply(p);
56650             }else{
56651                 lb[lb.length] = ct.apply(p);
56652                 lsb[lsb.length] = st.apply(p);
56653             }
56654         }
56655         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
56656                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
56657     },
56658
56659     updateHeaders : function(){
56660         var html = this.renderHeaders();
56661         this.lockedHd.update(html[0]);
56662         this.mainHd.update(html[1]);
56663     },
56664
56665     /**
56666      * Focuses the specified row.
56667      * @param {Number} row The row index
56668      */
56669     focusRow : function(row)
56670     {
56671         //Roo.log('GridView.focusRow');
56672         var x = this.scroller.dom.scrollLeft;
56673         this.focusCell(row, 0, false);
56674         this.scroller.dom.scrollLeft = x;
56675     },
56676
56677     /**
56678      * Focuses the specified cell.
56679      * @param {Number} row The row index
56680      * @param {Number} col The column index
56681      * @param {Boolean} hscroll false to disable horizontal scrolling
56682      */
56683     focusCell : function(row, col, hscroll)
56684     {
56685         //Roo.log('GridView.focusCell');
56686         var el = this.ensureVisible(row, col, hscroll);
56687         this.focusEl.alignTo(el, "tl-tl");
56688         if(Roo.isGecko){
56689             this.focusEl.focus();
56690         }else{
56691             this.focusEl.focus.defer(1, this.focusEl);
56692         }
56693     },
56694
56695     /**
56696      * Scrolls the specified cell into view
56697      * @param {Number} row The row index
56698      * @param {Number} col The column index
56699      * @param {Boolean} hscroll false to disable horizontal scrolling
56700      */
56701     ensureVisible : function(row, col, hscroll)
56702     {
56703         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
56704         //return null; //disable for testing.
56705         if(typeof row != "number"){
56706             row = row.rowIndex;
56707         }
56708         if(row < 0 && row >= this.ds.getCount()){
56709             return  null;
56710         }
56711         col = (col !== undefined ? col : 0);
56712         var cm = this.grid.colModel;
56713         while(cm.isHidden(col)){
56714             col++;
56715         }
56716
56717         var el = this.getCell(row, col);
56718         if(!el){
56719             return null;
56720         }
56721         var c = this.scroller.dom;
56722
56723         var ctop = parseInt(el.offsetTop, 10);
56724         var cleft = parseInt(el.offsetLeft, 10);
56725         var cbot = ctop + el.offsetHeight;
56726         var cright = cleft + el.offsetWidth;
56727         
56728         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
56729         var stop = parseInt(c.scrollTop, 10);
56730         var sleft = parseInt(c.scrollLeft, 10);
56731         var sbot = stop + ch;
56732         var sright = sleft + c.clientWidth;
56733         /*
56734         Roo.log('GridView.ensureVisible:' +
56735                 ' ctop:' + ctop +
56736                 ' c.clientHeight:' + c.clientHeight +
56737                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
56738                 ' stop:' + stop +
56739                 ' cbot:' + cbot +
56740                 ' sbot:' + sbot +
56741                 ' ch:' + ch  
56742                 );
56743         */
56744         if(ctop < stop){
56745              c.scrollTop = ctop;
56746             //Roo.log("set scrolltop to ctop DISABLE?");
56747         }else if(cbot > sbot){
56748             //Roo.log("set scrolltop to cbot-ch");
56749             c.scrollTop = cbot-ch;
56750         }
56751         
56752         if(hscroll !== false){
56753             if(cleft < sleft){
56754                 c.scrollLeft = cleft;
56755             }else if(cright > sright){
56756                 c.scrollLeft = cright-c.clientWidth;
56757             }
56758         }
56759          
56760         return el;
56761     },
56762
56763     updateColumns : function(){
56764         this.grid.stopEditing();
56765         var cm = this.grid.colModel, colIds = this.getColumnIds();
56766         //var totalWidth = cm.getTotalWidth();
56767         var pos = 0;
56768         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56769             //if(cm.isHidden(i)) continue;
56770             var w = cm.getColumnWidth(i);
56771             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
56772             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
56773         }
56774         this.updateSplitters();
56775     },
56776
56777     generateRules : function(cm){
56778         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
56779         Roo.util.CSS.removeStyleSheet(rulesId);
56780         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56781             var cid = cm.getColumnId(i);
56782             var align = '';
56783             if(cm.config[i].align){
56784                 align = 'text-align:'+cm.config[i].align+';';
56785             }
56786             var hidden = '';
56787             if(cm.isHidden(i)){
56788                 hidden = 'display:none;';
56789             }
56790             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
56791             ruleBuf.push(
56792                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
56793                     this.hdSelector, cid, " {\n", align, width, "}\n",
56794                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
56795                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
56796         }
56797         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
56798     },
56799
56800     updateSplitters : function(){
56801         var cm = this.cm, s = this.getSplitters();
56802         if(s){ // splitters not created yet
56803             var pos = 0, locked = true;
56804             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56805                 if(cm.isHidden(i)) {
56806                     continue;
56807                 }
56808                 var w = cm.getColumnWidth(i); // make sure it's a number
56809                 if(!cm.isLocked(i) && locked){
56810                     pos = 0;
56811                     locked = false;
56812                 }
56813                 pos += w;
56814                 s[i].style.left = (pos-this.splitOffset) + "px";
56815             }
56816         }
56817     },
56818
56819     handleHiddenChange : function(colModel, colIndex, hidden){
56820         if(hidden){
56821             this.hideColumn(colIndex);
56822         }else{
56823             this.unhideColumn(colIndex);
56824         }
56825     },
56826
56827     hideColumn : function(colIndex){
56828         var cid = this.getColumnId(colIndex);
56829         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
56830         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
56831         if(Roo.isSafari){
56832             this.updateHeaders();
56833         }
56834         this.updateSplitters();
56835         this.layout();
56836     },
56837
56838     unhideColumn : function(colIndex){
56839         var cid = this.getColumnId(colIndex);
56840         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
56841         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
56842
56843         if(Roo.isSafari){
56844             this.updateHeaders();
56845         }
56846         this.updateSplitters();
56847         this.layout();
56848     },
56849
56850     insertRows : function(dm, firstRow, lastRow, isUpdate){
56851         if(firstRow == 0 && lastRow == dm.getCount()-1){
56852             this.refresh();
56853         }else{
56854             if(!isUpdate){
56855                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
56856             }
56857             var s = this.getScrollState();
56858             var markup = this.renderRows(firstRow, lastRow);
56859             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
56860             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
56861             this.restoreScroll(s);
56862             if(!isUpdate){
56863                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
56864                 this.syncRowHeights(firstRow, lastRow);
56865                 this.stripeRows(firstRow);
56866                 this.layout();
56867             }
56868         }
56869     },
56870
56871     bufferRows : function(markup, target, index){
56872         var before = null, trows = target.rows, tbody = target.tBodies[0];
56873         if(index < trows.length){
56874             before = trows[index];
56875         }
56876         var b = document.createElement("div");
56877         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
56878         var rows = b.firstChild.rows;
56879         for(var i = 0, len = rows.length; i < len; i++){
56880             if(before){
56881                 tbody.insertBefore(rows[0], before);
56882             }else{
56883                 tbody.appendChild(rows[0]);
56884             }
56885         }
56886         b.innerHTML = "";
56887         b = null;
56888     },
56889
56890     deleteRows : function(dm, firstRow, lastRow){
56891         if(dm.getRowCount()<1){
56892             this.fireEvent("beforerefresh", this);
56893             this.mainBody.update("");
56894             this.lockedBody.update("");
56895             this.fireEvent("refresh", this);
56896         }else{
56897             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
56898             var bt = this.getBodyTable();
56899             var tbody = bt.firstChild;
56900             var rows = bt.rows;
56901             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
56902                 tbody.removeChild(rows[firstRow]);
56903             }
56904             this.stripeRows(firstRow);
56905             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
56906         }
56907     },
56908
56909     updateRows : function(dataSource, firstRow, lastRow){
56910         var s = this.getScrollState();
56911         this.refresh();
56912         this.restoreScroll(s);
56913     },
56914
56915     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
56916         if(!noRefresh){
56917            this.refresh();
56918         }
56919         this.updateHeaderSortState();
56920     },
56921
56922     getScrollState : function(){
56923         
56924         var sb = this.scroller.dom;
56925         return {left: sb.scrollLeft, top: sb.scrollTop};
56926     },
56927
56928     stripeRows : function(startRow){
56929         if(!this.grid.stripeRows || this.ds.getCount() < 1){
56930             return;
56931         }
56932         startRow = startRow || 0;
56933         var rows = this.getBodyTable().rows;
56934         var lrows = this.getLockedTable().rows;
56935         var cls = ' x-grid-row-alt ';
56936         for(var i = startRow, len = rows.length; i < len; i++){
56937             var row = rows[i], lrow = lrows[i];
56938             var isAlt = ((i+1) % 2 == 0);
56939             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
56940             if(isAlt == hasAlt){
56941                 continue;
56942             }
56943             if(isAlt){
56944                 row.className += " x-grid-row-alt";
56945             }else{
56946                 row.className = row.className.replace("x-grid-row-alt", "");
56947             }
56948             if(lrow){
56949                 lrow.className = row.className;
56950             }
56951         }
56952     },
56953
56954     restoreScroll : function(state){
56955         //Roo.log('GridView.restoreScroll');
56956         var sb = this.scroller.dom;
56957         sb.scrollLeft = state.left;
56958         sb.scrollTop = state.top;
56959         this.syncScroll();
56960     },
56961
56962     syncScroll : function(){
56963         //Roo.log('GridView.syncScroll');
56964         var sb = this.scroller.dom;
56965         var sh = this.mainHd.dom;
56966         var bs = this.mainBody.dom;
56967         var lv = this.lockedBody.dom;
56968         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
56969         lv.scrollTop = bs.scrollTop = sb.scrollTop;
56970     },
56971
56972     handleScroll : function(e){
56973         this.syncScroll();
56974         var sb = this.scroller.dom;
56975         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
56976         e.stopEvent();
56977     },
56978
56979     handleWheel : function(e){
56980         var d = e.getWheelDelta();
56981         this.scroller.dom.scrollTop -= d*22;
56982         // set this here to prevent jumpy scrolling on large tables
56983         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
56984         e.stopEvent();
56985     },
56986
56987     renderRows : function(startRow, endRow){
56988         // pull in all the crap needed to render rows
56989         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
56990         var colCount = cm.getColumnCount();
56991
56992         if(ds.getCount() < 1){
56993             return ["", ""];
56994         }
56995
56996         // build a map for all the columns
56997         var cs = [];
56998         for(var i = 0; i < colCount; i++){
56999             var name = cm.getDataIndex(i);
57000             cs[i] = {
57001                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
57002                 renderer : cm.getRenderer(i),
57003                 id : cm.getColumnId(i),
57004                 locked : cm.isLocked(i),
57005                 has_editor : cm.isCellEditable(i)
57006             };
57007         }
57008
57009         startRow = startRow || 0;
57010         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
57011
57012         // records to render
57013         var rs = ds.getRange(startRow, endRow);
57014
57015         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
57016     },
57017
57018     // As much as I hate to duplicate code, this was branched because FireFox really hates
57019     // [].join("") on strings. The performance difference was substantial enough to
57020     // branch this function
57021     doRender : Roo.isGecko ?
57022             function(cs, rs, ds, startRow, colCount, stripe){
57023                 var ts = this.templates, ct = ts.cell, rt = ts.row;
57024                 // buffers
57025                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
57026                 
57027                 var hasListener = this.grid.hasListener('rowclass');
57028                 var rowcfg = {};
57029                 for(var j = 0, len = rs.length; j < len; j++){
57030                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
57031                     for(var i = 0; i < colCount; i++){
57032                         c = cs[i];
57033                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
57034                         p.id = c.id;
57035                         p.css = p.attr = "";
57036                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
57037                         if(p.value == undefined || p.value === "") {
57038                             p.value = "&#160;";
57039                         }
57040                         if(c.has_editor){
57041                             p.css += ' x-grid-editable-cell';
57042                         }
57043                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
57044                             p.css +=  ' x-grid-dirty-cell';
57045                         }
57046                         var markup = ct.apply(p);
57047                         if(!c.locked){
57048                             cb+= markup;
57049                         }else{
57050                             lcb+= markup;
57051                         }
57052                     }
57053                     var alt = [];
57054                     if(stripe && ((rowIndex+1) % 2 == 0)){
57055                         alt.push("x-grid-row-alt")
57056                     }
57057                     if(r.dirty){
57058                         alt.push(  " x-grid-dirty-row");
57059                     }
57060                     rp.cells = lcb;
57061                     if(this.getRowClass){
57062                         alt.push(this.getRowClass(r, rowIndex));
57063                     }
57064                     if (hasListener) {
57065                         rowcfg = {
57066                              
57067                             record: r,
57068                             rowIndex : rowIndex,
57069                             rowClass : ''
57070                         };
57071                         this.grid.fireEvent('rowclass', this, rowcfg);
57072                         alt.push(rowcfg.rowClass);
57073                     }
57074                     rp.alt = alt.join(" ");
57075                     lbuf+= rt.apply(rp);
57076                     rp.cells = cb;
57077                     buf+=  rt.apply(rp);
57078                 }
57079                 return [lbuf, buf];
57080             } :
57081             function(cs, rs, ds, startRow, colCount, stripe){
57082                 var ts = this.templates, ct = ts.cell, rt = ts.row;
57083                 // buffers
57084                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
57085                 var hasListener = this.grid.hasListener('rowclass');
57086  
57087                 var rowcfg = {};
57088                 for(var j = 0, len = rs.length; j < len; j++){
57089                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
57090                     for(var i = 0; i < colCount; i++){
57091                         c = cs[i];
57092                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
57093                         p.id = c.id;
57094                         p.css = p.attr = "";
57095                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
57096                         if(p.value == undefined || p.value === "") {
57097                             p.value = "&#160;";
57098                         }
57099                         //Roo.log(c);
57100                          if(c.has_editor){
57101                             p.css += ' x-grid-editable-cell';
57102                         }
57103                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
57104                             p.css += ' x-grid-dirty-cell' 
57105                         }
57106                         
57107                         var markup = ct.apply(p);
57108                         if(!c.locked){
57109                             cb[cb.length] = markup;
57110                         }else{
57111                             lcb[lcb.length] = markup;
57112                         }
57113                     }
57114                     var alt = [];
57115                     if(stripe && ((rowIndex+1) % 2 == 0)){
57116                         alt.push( "x-grid-row-alt");
57117                     }
57118                     if(r.dirty){
57119                         alt.push(" x-grid-dirty-row");
57120                     }
57121                     rp.cells = lcb;
57122                     if(this.getRowClass){
57123                         alt.push( this.getRowClass(r, rowIndex));
57124                     }
57125                     if (hasListener) {
57126                         rowcfg = {
57127                              
57128                             record: r,
57129                             rowIndex : rowIndex,
57130                             rowClass : ''
57131                         };
57132                         this.grid.fireEvent('rowclass', this, rowcfg);
57133                         alt.push(rowcfg.rowClass);
57134                     }
57135                     
57136                     rp.alt = alt.join(" ");
57137                     rp.cells = lcb.join("");
57138                     lbuf[lbuf.length] = rt.apply(rp);
57139                     rp.cells = cb.join("");
57140                     buf[buf.length] =  rt.apply(rp);
57141                 }
57142                 return [lbuf.join(""), buf.join("")];
57143             },
57144
57145     renderBody : function(){
57146         var markup = this.renderRows();
57147         var bt = this.templates.body;
57148         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
57149     },
57150
57151     /**
57152      * Refreshes the grid
57153      * @param {Boolean} headersToo
57154      */
57155     refresh : function(headersToo){
57156         this.fireEvent("beforerefresh", this);
57157         this.grid.stopEditing();
57158         var result = this.renderBody();
57159         this.lockedBody.update(result[0]);
57160         this.mainBody.update(result[1]);
57161         if(headersToo === true){
57162             this.updateHeaders();
57163             this.updateColumns();
57164             this.updateSplitters();
57165             this.updateHeaderSortState();
57166         }
57167         this.syncRowHeights();
57168         this.layout();
57169         this.fireEvent("refresh", this);
57170     },
57171
57172     handleColumnMove : function(cm, oldIndex, newIndex){
57173         this.indexMap = null;
57174         var s = this.getScrollState();
57175         this.refresh(true);
57176         this.restoreScroll(s);
57177         this.afterMove(newIndex);
57178     },
57179
57180     afterMove : function(colIndex){
57181         if(this.enableMoveAnim && Roo.enableFx){
57182             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
57183         }
57184         // if multisort - fix sortOrder, and reload..
57185         if (this.grid.dataSource.multiSort) {
57186             // the we can call sort again..
57187             var dm = this.grid.dataSource;
57188             var cm = this.grid.colModel;
57189             var so = [];
57190             for(var i = 0; i < cm.config.length; i++ ) {
57191                 
57192                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
57193                     continue; // dont' bother, it's not in sort list or being set.
57194                 }
57195                 
57196                 so.push(cm.config[i].dataIndex);
57197             };
57198             dm.sortOrder = so;
57199             dm.load(dm.lastOptions);
57200             
57201             
57202         }
57203         
57204     },
57205
57206     updateCell : function(dm, rowIndex, dataIndex){
57207         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
57208         if(typeof colIndex == "undefined"){ // not present in grid
57209             return;
57210         }
57211         var cm = this.grid.colModel;
57212         var cell = this.getCell(rowIndex, colIndex);
57213         var cellText = this.getCellText(rowIndex, colIndex);
57214
57215         var p = {
57216             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
57217             id : cm.getColumnId(colIndex),
57218             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
57219         };
57220         var renderer = cm.getRenderer(colIndex);
57221         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
57222         if(typeof val == "undefined" || val === "") {
57223             val = "&#160;";
57224         }
57225         cellText.innerHTML = val;
57226         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
57227         this.syncRowHeights(rowIndex, rowIndex);
57228     },
57229
57230     calcColumnWidth : function(colIndex, maxRowsToMeasure){
57231         var maxWidth = 0;
57232         if(this.grid.autoSizeHeaders){
57233             var h = this.getHeaderCellMeasure(colIndex);
57234             maxWidth = Math.max(maxWidth, h.scrollWidth);
57235         }
57236         var tb, index;
57237         if(this.cm.isLocked(colIndex)){
57238             tb = this.getLockedTable();
57239             index = colIndex;
57240         }else{
57241             tb = this.getBodyTable();
57242             index = colIndex - this.cm.getLockedCount();
57243         }
57244         if(tb && tb.rows){
57245             var rows = tb.rows;
57246             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
57247             for(var i = 0; i < stopIndex; i++){
57248                 var cell = rows[i].childNodes[index].firstChild;
57249                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
57250             }
57251         }
57252         return maxWidth + /*margin for error in IE*/ 5;
57253     },
57254     /**
57255      * Autofit a column to its content.
57256      * @param {Number} colIndex
57257      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
57258      */
57259      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
57260          if(this.cm.isHidden(colIndex)){
57261              return; // can't calc a hidden column
57262          }
57263         if(forceMinSize){
57264             var cid = this.cm.getColumnId(colIndex);
57265             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
57266            if(this.grid.autoSizeHeaders){
57267                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
57268            }
57269         }
57270         var newWidth = this.calcColumnWidth(colIndex);
57271         this.cm.setColumnWidth(colIndex,
57272             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
57273         if(!suppressEvent){
57274             this.grid.fireEvent("columnresize", colIndex, newWidth);
57275         }
57276     },
57277
57278     /**
57279      * Autofits all columns to their content and then expands to fit any extra space in the grid
57280      */
57281      autoSizeColumns : function(){
57282         var cm = this.grid.colModel;
57283         var colCount = cm.getColumnCount();
57284         for(var i = 0; i < colCount; i++){
57285             this.autoSizeColumn(i, true, true);
57286         }
57287         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
57288             this.fitColumns();
57289         }else{
57290             this.updateColumns();
57291             this.layout();
57292         }
57293     },
57294
57295     /**
57296      * Autofits all columns to the grid's width proportionate with their current size
57297      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
57298      */
57299     fitColumns : function(reserveScrollSpace){
57300         var cm = this.grid.colModel;
57301         var colCount = cm.getColumnCount();
57302         var cols = [];
57303         var width = 0;
57304         var i, w;
57305         for (i = 0; i < colCount; i++){
57306             if(!cm.isHidden(i) && !cm.isFixed(i)){
57307                 w = cm.getColumnWidth(i);
57308                 cols.push(i);
57309                 cols.push(w);
57310                 width += w;
57311             }
57312         }
57313         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
57314         if(reserveScrollSpace){
57315             avail -= 17;
57316         }
57317         var frac = (avail - cm.getTotalWidth())/width;
57318         while (cols.length){
57319             w = cols.pop();
57320             i = cols.pop();
57321             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
57322         }
57323         this.updateColumns();
57324         this.layout();
57325     },
57326
57327     onRowSelect : function(rowIndex){
57328         var row = this.getRowComposite(rowIndex);
57329         row.addClass("x-grid-row-selected");
57330     },
57331
57332     onRowDeselect : function(rowIndex){
57333         var row = this.getRowComposite(rowIndex);
57334         row.removeClass("x-grid-row-selected");
57335     },
57336
57337     onCellSelect : function(row, col){
57338         var cell = this.getCell(row, col);
57339         if(cell){
57340             Roo.fly(cell).addClass("x-grid-cell-selected");
57341         }
57342     },
57343
57344     onCellDeselect : function(row, col){
57345         var cell = this.getCell(row, col);
57346         if(cell){
57347             Roo.fly(cell).removeClass("x-grid-cell-selected");
57348         }
57349     },
57350
57351     updateHeaderSortState : function(){
57352         
57353         // sort state can be single { field: xxx, direction : yyy}
57354         // or   { xxx=>ASC , yyy : DESC ..... }
57355         
57356         var mstate = {};
57357         if (!this.ds.multiSort) { 
57358             var state = this.ds.getSortState();
57359             if(!state){
57360                 return;
57361             }
57362             mstate[state.field] = state.direction;
57363             // FIXME... - this is not used here.. but might be elsewhere..
57364             this.sortState = state;
57365             
57366         } else {
57367             mstate = this.ds.sortToggle;
57368         }
57369         //remove existing sort classes..
57370         
57371         var sc = this.sortClasses;
57372         var hds = this.el.select(this.headerSelector).removeClass(sc);
57373         
57374         for(var f in mstate) {
57375         
57376             var sortColumn = this.cm.findColumnIndex(f);
57377             
57378             if(sortColumn != -1){
57379                 var sortDir = mstate[f];        
57380                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
57381             }
57382         }
57383         
57384          
57385         
57386     },
57387
57388
57389     handleHeaderClick : function(g, index,e){
57390         
57391         Roo.log("header click");
57392         
57393         if (Roo.isTouch) {
57394             // touch events on header are handled by context
57395             this.handleHdCtx(g,index,e);
57396             return;
57397         }
57398         
57399         
57400         if(this.headersDisabled){
57401             return;
57402         }
57403         var dm = g.dataSource, cm = g.colModel;
57404         if(!cm.isSortable(index)){
57405             return;
57406         }
57407         g.stopEditing();
57408         
57409         if (dm.multiSort) {
57410             // update the sortOrder
57411             var so = [];
57412             for(var i = 0; i < cm.config.length; i++ ) {
57413                 
57414                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
57415                     continue; // dont' bother, it's not in sort list or being set.
57416                 }
57417                 
57418                 so.push(cm.config[i].dataIndex);
57419             };
57420             dm.sortOrder = so;
57421         }
57422         
57423         
57424         dm.sort(cm.getDataIndex(index));
57425     },
57426
57427
57428     destroy : function(){
57429         if(this.colMenu){
57430             this.colMenu.removeAll();
57431             Roo.menu.MenuMgr.unregister(this.colMenu);
57432             this.colMenu.getEl().remove();
57433             delete this.colMenu;
57434         }
57435         if(this.hmenu){
57436             this.hmenu.removeAll();
57437             Roo.menu.MenuMgr.unregister(this.hmenu);
57438             this.hmenu.getEl().remove();
57439             delete this.hmenu;
57440         }
57441         if(this.grid.enableColumnMove){
57442             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
57443             if(dds){
57444                 for(var dd in dds){
57445                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
57446                         var elid = dds[dd].dragElId;
57447                         dds[dd].unreg();
57448                         Roo.get(elid).remove();
57449                     } else if(dds[dd].config.isTarget){
57450                         dds[dd].proxyTop.remove();
57451                         dds[dd].proxyBottom.remove();
57452                         dds[dd].unreg();
57453                     }
57454                     if(Roo.dd.DDM.locationCache[dd]){
57455                         delete Roo.dd.DDM.locationCache[dd];
57456                     }
57457                 }
57458                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
57459             }
57460         }
57461         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
57462         this.bind(null, null);
57463         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
57464     },
57465
57466     handleLockChange : function(){
57467         this.refresh(true);
57468     },
57469
57470     onDenyColumnLock : function(){
57471
57472     },
57473
57474     onDenyColumnHide : function(){
57475
57476     },
57477
57478     handleHdMenuClick : function(item){
57479         var index = this.hdCtxIndex;
57480         var cm = this.cm, ds = this.ds;
57481         switch(item.id){
57482             case "asc":
57483                 ds.sort(cm.getDataIndex(index), "ASC");
57484                 break;
57485             case "desc":
57486                 ds.sort(cm.getDataIndex(index), "DESC");
57487                 break;
57488             case "lock":
57489                 var lc = cm.getLockedCount();
57490                 if(cm.getColumnCount(true) <= lc+1){
57491                     this.onDenyColumnLock();
57492                     return;
57493                 }
57494                 if(lc != index){
57495                     cm.setLocked(index, true, true);
57496                     cm.moveColumn(index, lc);
57497                     this.grid.fireEvent("columnmove", index, lc);
57498                 }else{
57499                     cm.setLocked(index, true);
57500                 }
57501             break;
57502             case "unlock":
57503                 var lc = cm.getLockedCount();
57504                 if((lc-1) != index){
57505                     cm.setLocked(index, false, true);
57506                     cm.moveColumn(index, lc-1);
57507                     this.grid.fireEvent("columnmove", index, lc-1);
57508                 }else{
57509                     cm.setLocked(index, false);
57510                 }
57511             break;
57512             case 'wider': // used to expand cols on touch..
57513             case 'narrow':
57514                 var cw = cm.getColumnWidth(index);
57515                 cw += (item.id == 'wider' ? 1 : -1) * 50;
57516                 cw = Math.max(0, cw);
57517                 cw = Math.min(cw,4000);
57518                 cm.setColumnWidth(index, cw);
57519                 break;
57520                 
57521             default:
57522                 index = cm.getIndexById(item.id.substr(4));
57523                 if(index != -1){
57524                     if(item.checked && cm.getColumnCount(true) <= 1){
57525                         this.onDenyColumnHide();
57526                         return false;
57527                     }
57528                     cm.setHidden(index, item.checked);
57529                 }
57530         }
57531         return true;
57532     },
57533
57534     beforeColMenuShow : function(){
57535         var cm = this.cm,  colCount = cm.getColumnCount();
57536         this.colMenu.removeAll();
57537         for(var i = 0; i < colCount; i++){
57538             this.colMenu.add(new Roo.menu.CheckItem({
57539                 id: "col-"+cm.getColumnId(i),
57540                 text: cm.getColumnHeader(i),
57541                 checked: !cm.isHidden(i),
57542                 hideOnClick:false
57543             }));
57544         }
57545     },
57546
57547     handleHdCtx : function(g, index, e){
57548         e.stopEvent();
57549         var hd = this.getHeaderCell(index);
57550         this.hdCtxIndex = index;
57551         var ms = this.hmenu.items, cm = this.cm;
57552         ms.get("asc").setDisabled(!cm.isSortable(index));
57553         ms.get("desc").setDisabled(!cm.isSortable(index));
57554         if(this.grid.enableColLock !== false){
57555             ms.get("lock").setDisabled(cm.isLocked(index));
57556             ms.get("unlock").setDisabled(!cm.isLocked(index));
57557         }
57558         this.hmenu.show(hd, "tl-bl");
57559     },
57560
57561     handleHdOver : function(e){
57562         var hd = this.findHeaderCell(e.getTarget());
57563         if(hd && !this.headersDisabled){
57564             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
57565                this.fly(hd).addClass("x-grid-hd-over");
57566             }
57567         }
57568     },
57569
57570     handleHdOut : function(e){
57571         var hd = this.findHeaderCell(e.getTarget());
57572         if(hd){
57573             this.fly(hd).removeClass("x-grid-hd-over");
57574         }
57575     },
57576
57577     handleSplitDblClick : function(e, t){
57578         var i = this.getCellIndex(t);
57579         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
57580             this.autoSizeColumn(i, true);
57581             this.layout();
57582         }
57583     },
57584
57585     render : function(){
57586
57587         var cm = this.cm;
57588         var colCount = cm.getColumnCount();
57589
57590         if(this.grid.monitorWindowResize === true){
57591             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
57592         }
57593         var header = this.renderHeaders();
57594         var body = this.templates.body.apply({rows:""});
57595         var html = this.templates.master.apply({
57596             lockedBody: body,
57597             body: body,
57598             lockedHeader: header[0],
57599             header: header[1]
57600         });
57601
57602         //this.updateColumns();
57603
57604         this.grid.getGridEl().dom.innerHTML = html;
57605
57606         this.initElements();
57607         
57608         // a kludge to fix the random scolling effect in webkit
57609         this.el.on("scroll", function() {
57610             this.el.dom.scrollTop=0; // hopefully not recursive..
57611         },this);
57612
57613         this.scroller.on("scroll", this.handleScroll, this);
57614         this.lockedBody.on("mousewheel", this.handleWheel, this);
57615         this.mainBody.on("mousewheel", this.handleWheel, this);
57616
57617         this.mainHd.on("mouseover", this.handleHdOver, this);
57618         this.mainHd.on("mouseout", this.handleHdOut, this);
57619         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
57620                 {delegate: "."+this.splitClass});
57621
57622         this.lockedHd.on("mouseover", this.handleHdOver, this);
57623         this.lockedHd.on("mouseout", this.handleHdOut, this);
57624         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
57625                 {delegate: "."+this.splitClass});
57626
57627         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
57628             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57629         }
57630
57631         this.updateSplitters();
57632
57633         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
57634             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57635             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57636         }
57637
57638         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
57639             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
57640             this.hmenu.add(
57641                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
57642                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
57643             );
57644             if(this.grid.enableColLock !== false){
57645                 this.hmenu.add('-',
57646                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
57647                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
57648                 );
57649             }
57650             if (Roo.isTouch) {
57651                  this.hmenu.add('-',
57652                     {id:"wider", text: this.columnsWiderText},
57653                     {id:"narrow", text: this.columnsNarrowText }
57654                 );
57655                 
57656                  
57657             }
57658             
57659             if(this.grid.enableColumnHide !== false){
57660
57661                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
57662                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
57663                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
57664
57665                 this.hmenu.add('-',
57666                     {id:"columns", text: this.columnsText, menu: this.colMenu}
57667                 );
57668             }
57669             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
57670
57671             this.grid.on("headercontextmenu", this.handleHdCtx, this);
57672         }
57673
57674         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
57675             this.dd = new Roo.grid.GridDragZone(this.grid, {
57676                 ddGroup : this.grid.ddGroup || 'GridDD'
57677             });
57678             
57679         }
57680
57681         /*
57682         for(var i = 0; i < colCount; i++){
57683             if(cm.isHidden(i)){
57684                 this.hideColumn(i);
57685             }
57686             if(cm.config[i].align){
57687                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
57688                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
57689             }
57690         }*/
57691         
57692         this.updateHeaderSortState();
57693
57694         this.beforeInitialResize();
57695         this.layout(true);
57696
57697         // two part rendering gives faster view to the user
57698         this.renderPhase2.defer(1, this);
57699     },
57700
57701     renderPhase2 : function(){
57702         // render the rows now
57703         this.refresh();
57704         if(this.grid.autoSizeColumns){
57705             this.autoSizeColumns();
57706         }
57707     },
57708
57709     beforeInitialResize : function(){
57710
57711     },
57712
57713     onColumnSplitterMoved : function(i, w){
57714         this.userResized = true;
57715         var cm = this.grid.colModel;
57716         cm.setColumnWidth(i, w, true);
57717         var cid = cm.getColumnId(i);
57718         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
57719         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
57720         this.updateSplitters();
57721         this.layout();
57722         this.grid.fireEvent("columnresize", i, w);
57723     },
57724
57725     syncRowHeights : function(startIndex, endIndex){
57726         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
57727             startIndex = startIndex || 0;
57728             var mrows = this.getBodyTable().rows;
57729             var lrows = this.getLockedTable().rows;
57730             var len = mrows.length-1;
57731             endIndex = Math.min(endIndex || len, len);
57732             for(var i = startIndex; i <= endIndex; i++){
57733                 var m = mrows[i], l = lrows[i];
57734                 var h = Math.max(m.offsetHeight, l.offsetHeight);
57735                 m.style.height = l.style.height = h + "px";
57736             }
57737         }
57738     },
57739
57740     layout : function(initialRender, is2ndPass){
57741         var g = this.grid;
57742         var auto = g.autoHeight;
57743         var scrollOffset = 16;
57744         var c = g.getGridEl(), cm = this.cm,
57745                 expandCol = g.autoExpandColumn,
57746                 gv = this;
57747         //c.beginMeasure();
57748
57749         if(!c.dom.offsetWidth){ // display:none?
57750             if(initialRender){
57751                 this.lockedWrap.show();
57752                 this.mainWrap.show();
57753             }
57754             return;
57755         }
57756
57757         var hasLock = this.cm.isLocked(0);
57758
57759         var tbh = this.headerPanel.getHeight();
57760         var bbh = this.footerPanel.getHeight();
57761
57762         if(auto){
57763             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
57764             var newHeight = ch + c.getBorderWidth("tb");
57765             if(g.maxHeight){
57766                 newHeight = Math.min(g.maxHeight, newHeight);
57767             }
57768             c.setHeight(newHeight);
57769         }
57770
57771         if(g.autoWidth){
57772             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
57773         }
57774
57775         var s = this.scroller;
57776
57777         var csize = c.getSize(true);
57778
57779         this.el.setSize(csize.width, csize.height);
57780
57781         this.headerPanel.setWidth(csize.width);
57782         this.footerPanel.setWidth(csize.width);
57783
57784         var hdHeight = this.mainHd.getHeight();
57785         var vw = csize.width;
57786         var vh = csize.height - (tbh + bbh);
57787
57788         s.setSize(vw, vh);
57789
57790         var bt = this.getBodyTable();
57791         
57792         if(cm.getLockedCount() == cm.config.length){
57793             bt = this.getLockedTable();
57794         }
57795         
57796         var ltWidth = hasLock ?
57797                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
57798
57799         var scrollHeight = bt.offsetHeight;
57800         var scrollWidth = ltWidth + bt.offsetWidth;
57801         var vscroll = false, hscroll = false;
57802
57803         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
57804
57805         var lw = this.lockedWrap, mw = this.mainWrap;
57806         var lb = this.lockedBody, mb = this.mainBody;
57807
57808         setTimeout(function(){
57809             var t = s.dom.offsetTop;
57810             var w = s.dom.clientWidth,
57811                 h = s.dom.clientHeight;
57812
57813             lw.setTop(t);
57814             lw.setSize(ltWidth, h);
57815
57816             mw.setLeftTop(ltWidth, t);
57817             mw.setSize(w-ltWidth, h);
57818
57819             lb.setHeight(h-hdHeight);
57820             mb.setHeight(h-hdHeight);
57821
57822             if(is2ndPass !== true && !gv.userResized && expandCol){
57823                 // high speed resize without full column calculation
57824                 
57825                 var ci = cm.getIndexById(expandCol);
57826                 if (ci < 0) {
57827                     ci = cm.findColumnIndex(expandCol);
57828                 }
57829                 ci = Math.max(0, ci); // make sure it's got at least the first col.
57830                 var expandId = cm.getColumnId(ci);
57831                 var  tw = cm.getTotalWidth(false);
57832                 var currentWidth = cm.getColumnWidth(ci);
57833                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
57834                 if(currentWidth != cw){
57835                     cm.setColumnWidth(ci, cw, true);
57836                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
57837                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
57838                     gv.updateSplitters();
57839                     gv.layout(false, true);
57840                 }
57841             }
57842
57843             if(initialRender){
57844                 lw.show();
57845                 mw.show();
57846             }
57847             //c.endMeasure();
57848         }, 10);
57849     },
57850
57851     onWindowResize : function(){
57852         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
57853             return;
57854         }
57855         this.layout();
57856     },
57857
57858     appendFooter : function(parentEl){
57859         return null;
57860     },
57861
57862     sortAscText : "Sort Ascending",
57863     sortDescText : "Sort Descending",
57864     lockText : "Lock Column",
57865     unlockText : "Unlock Column",
57866     columnsText : "Columns",
57867  
57868     columnsWiderText : "Wider",
57869     columnsNarrowText : "Thinner"
57870 });
57871
57872
57873 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
57874     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
57875     this.proxy.el.addClass('x-grid3-col-dd');
57876 };
57877
57878 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
57879     handleMouseDown : function(e){
57880
57881     },
57882
57883     callHandleMouseDown : function(e){
57884         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
57885     }
57886 });
57887 /*
57888  * Based on:
57889  * Ext JS Library 1.1.1
57890  * Copyright(c) 2006-2007, Ext JS, LLC.
57891  *
57892  * Originally Released Under LGPL - original licence link has changed is not relivant.
57893  *
57894  * Fork - LGPL
57895  * <script type="text/javascript">
57896  */
57897  
57898 // private
57899 // This is a support class used internally by the Grid components
57900 Roo.grid.SplitDragZone = function(grid, hd, hd2){
57901     this.grid = grid;
57902     this.view = grid.getView();
57903     this.proxy = this.view.resizeProxy;
57904     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
57905         "gridSplitters" + this.grid.getGridEl().id, {
57906         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
57907     });
57908     this.setHandleElId(Roo.id(hd));
57909     this.setOuterHandleElId(Roo.id(hd2));
57910     this.scroll = false;
57911 };
57912 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
57913     fly: Roo.Element.fly,
57914
57915     b4StartDrag : function(x, y){
57916         this.view.headersDisabled = true;
57917         this.proxy.setHeight(this.view.mainWrap.getHeight());
57918         var w = this.cm.getColumnWidth(this.cellIndex);
57919         var minw = Math.max(w-this.grid.minColumnWidth, 0);
57920         this.resetConstraints();
57921         this.setXConstraint(minw, 1000);
57922         this.setYConstraint(0, 0);
57923         this.minX = x - minw;
57924         this.maxX = x + 1000;
57925         this.startPos = x;
57926         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
57927     },
57928
57929
57930     handleMouseDown : function(e){
57931         ev = Roo.EventObject.setEvent(e);
57932         var t = this.fly(ev.getTarget());
57933         if(t.hasClass("x-grid-split")){
57934             this.cellIndex = this.view.getCellIndex(t.dom);
57935             this.split = t.dom;
57936             this.cm = this.grid.colModel;
57937             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
57938                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
57939             }
57940         }
57941     },
57942
57943     endDrag : function(e){
57944         this.view.headersDisabled = false;
57945         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
57946         var diff = endX - this.startPos;
57947         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
57948     },
57949
57950     autoOffset : function(){
57951         this.setDelta(0,0);
57952     }
57953 });/*
57954  * Based on:
57955  * Ext JS Library 1.1.1
57956  * Copyright(c) 2006-2007, Ext JS, LLC.
57957  *
57958  * Originally Released Under LGPL - original licence link has changed is not relivant.
57959  *
57960  * Fork - LGPL
57961  * <script type="text/javascript">
57962  */
57963  
57964 // private
57965 // This is a support class used internally by the Grid components
57966 Roo.grid.GridDragZone = function(grid, config){
57967     this.view = grid.getView();
57968     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
57969     if(this.view.lockedBody){
57970         this.setHandleElId(Roo.id(this.view.mainBody.dom));
57971         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
57972     }
57973     this.scroll = false;
57974     this.grid = grid;
57975     this.ddel = document.createElement('div');
57976     this.ddel.className = 'x-grid-dd-wrap';
57977 };
57978
57979 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
57980     ddGroup : "GridDD",
57981
57982     getDragData : function(e){
57983         var t = Roo.lib.Event.getTarget(e);
57984         var rowIndex = this.view.findRowIndex(t);
57985         var sm = this.grid.selModel;
57986             
57987         //Roo.log(rowIndex);
57988         
57989         if (sm.getSelectedCell) {
57990             // cell selection..
57991             if (!sm.getSelectedCell()) {
57992                 return false;
57993             }
57994             if (rowIndex != sm.getSelectedCell()[0]) {
57995                 return false;
57996             }
57997         
57998         }
57999         
58000         if(rowIndex !== false){
58001             
58002             // if editorgrid.. 
58003             
58004             
58005             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
58006                
58007             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
58008               //  
58009             //}
58010             if (e.hasModifier()){
58011                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
58012             }
58013             
58014             Roo.log("getDragData");
58015             
58016             return {
58017                 grid: this.grid,
58018                 ddel: this.ddel,
58019                 rowIndex: rowIndex,
58020                 selections:sm.getSelections ? sm.getSelections() : (
58021                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
58022                 )
58023             };
58024         }
58025         return false;
58026     },
58027
58028     onInitDrag : function(e){
58029         var data = this.dragData;
58030         this.ddel.innerHTML = this.grid.getDragDropText();
58031         this.proxy.update(this.ddel);
58032         // fire start drag?
58033     },
58034
58035     afterRepair : function(){
58036         this.dragging = false;
58037     },
58038
58039     getRepairXY : function(e, data){
58040         return false;
58041     },
58042
58043     onEndDrag : function(data, e){
58044         // fire end drag?
58045     },
58046
58047     onValidDrop : function(dd, e, id){
58048         // fire drag drop?
58049         this.hideProxy();
58050     },
58051
58052     beforeInvalidDrop : function(e, id){
58053
58054     }
58055 });/*
58056  * Based on:
58057  * Ext JS Library 1.1.1
58058  * Copyright(c) 2006-2007, Ext JS, LLC.
58059  *
58060  * Originally Released Under LGPL - original licence link has changed is not relivant.
58061  *
58062  * Fork - LGPL
58063  * <script type="text/javascript">
58064  */
58065  
58066
58067 /**
58068  * @class Roo.grid.ColumnModel
58069  * @extends Roo.util.Observable
58070  * This is the default implementation of a ColumnModel used by the Grid. It defines
58071  * the columns in the grid.
58072  * <br>Usage:<br>
58073  <pre><code>
58074  var colModel = new Roo.grid.ColumnModel([
58075         {header: "Ticker", width: 60, sortable: true, locked: true},
58076         {header: "Company Name", width: 150, sortable: true},
58077         {header: "Market Cap.", width: 100, sortable: true},
58078         {header: "$ Sales", width: 100, sortable: true, renderer: money},
58079         {header: "Employees", width: 100, sortable: true, resizable: false}
58080  ]);
58081  </code></pre>
58082  * <p>
58083  
58084  * The config options listed for this class are options which may appear in each
58085  * individual column definition.
58086  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
58087  * @constructor
58088  * @param {Object} config An Array of column config objects. See this class's
58089  * config objects for details.
58090 */
58091 Roo.grid.ColumnModel = function(config){
58092         /**
58093      * The config passed into the constructor
58094      */
58095     this.config = config;
58096     this.lookup = {};
58097
58098     // if no id, create one
58099     // if the column does not have a dataIndex mapping,
58100     // map it to the order it is in the config
58101     for(var i = 0, len = config.length; i < len; i++){
58102         var c = config[i];
58103         if(typeof c.dataIndex == "undefined"){
58104             c.dataIndex = i;
58105         }
58106         if(typeof c.renderer == "string"){
58107             c.renderer = Roo.util.Format[c.renderer];
58108         }
58109         if(typeof c.id == "undefined"){
58110             c.id = Roo.id();
58111         }
58112         if(c.editor && c.editor.xtype){
58113             c.editor  = Roo.factory(c.editor, Roo.grid);
58114         }
58115         if(c.editor && c.editor.isFormField){
58116             c.editor = new Roo.grid.GridEditor(c.editor);
58117         }
58118         this.lookup[c.id] = c;
58119     }
58120
58121     /**
58122      * The width of columns which have no width specified (defaults to 100)
58123      * @type Number
58124      */
58125     this.defaultWidth = 100;
58126
58127     /**
58128      * Default sortable of columns which have no sortable specified (defaults to false)
58129      * @type Boolean
58130      */
58131     this.defaultSortable = false;
58132
58133     this.addEvents({
58134         /**
58135              * @event widthchange
58136              * Fires when the width of a column changes.
58137              * @param {ColumnModel} this
58138              * @param {Number} columnIndex The column index
58139              * @param {Number} newWidth The new width
58140              */
58141             "widthchange": true,
58142         /**
58143              * @event headerchange
58144              * Fires when the text of a header changes.
58145              * @param {ColumnModel} this
58146              * @param {Number} columnIndex The column index
58147              * @param {Number} newText The new header text
58148              */
58149             "headerchange": true,
58150         /**
58151              * @event hiddenchange
58152              * Fires when a column is hidden or "unhidden".
58153              * @param {ColumnModel} this
58154              * @param {Number} columnIndex The column index
58155              * @param {Boolean} hidden true if hidden, false otherwise
58156              */
58157             "hiddenchange": true,
58158             /**
58159          * @event columnmoved
58160          * Fires when a column is moved.
58161          * @param {ColumnModel} this
58162          * @param {Number} oldIndex
58163          * @param {Number} newIndex
58164          */
58165         "columnmoved" : true,
58166         /**
58167          * @event columlockchange
58168          * Fires when a column's locked state is changed
58169          * @param {ColumnModel} this
58170          * @param {Number} colIndex
58171          * @param {Boolean} locked true if locked
58172          */
58173         "columnlockchange" : true
58174     });
58175     Roo.grid.ColumnModel.superclass.constructor.call(this);
58176 };
58177 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
58178     /**
58179      * @cfg {String} header The header text to display in the Grid view.
58180      */
58181     /**
58182      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
58183      * {@link Roo.data.Record} definition from which to draw the column's value. If not
58184      * specified, the column's index is used as an index into the Record's data Array.
58185      */
58186     /**
58187      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
58188      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
58189      */
58190     /**
58191      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
58192      * Defaults to the value of the {@link #defaultSortable} property.
58193      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
58194      */
58195     /**
58196      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
58197      */
58198     /**
58199      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
58200      */
58201     /**
58202      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
58203      */
58204     /**
58205      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
58206      */
58207     /**
58208      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
58209      * given the cell's data value. See {@link #setRenderer}. If not specified, the
58210      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
58211      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
58212      */
58213        /**
58214      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
58215      */
58216     /**
58217      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
58218      */
58219     /**
58220      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
58221      */
58222     /**
58223      * @cfg {String} cursor (Optional)
58224      */
58225     /**
58226      * @cfg {String} tooltip (Optional)
58227      */
58228     /**
58229      * @cfg {Number} xs (Optional)
58230      */
58231     /**
58232      * @cfg {Number} sm (Optional)
58233      */
58234     /**
58235      * @cfg {Number} md (Optional)
58236      */
58237     /**
58238      * @cfg {Number} lg (Optional)
58239      */
58240     /**
58241      * Returns the id of the column at the specified index.
58242      * @param {Number} index The column index
58243      * @return {String} the id
58244      */
58245     getColumnId : function(index){
58246         return this.config[index].id;
58247     },
58248
58249     /**
58250      * Returns the column for a specified id.
58251      * @param {String} id The column id
58252      * @return {Object} the column
58253      */
58254     getColumnById : function(id){
58255         return this.lookup[id];
58256     },
58257
58258     
58259     /**
58260      * Returns the column for a specified dataIndex.
58261      * @param {String} dataIndex The column dataIndex
58262      * @return {Object|Boolean} the column or false if not found
58263      */
58264     getColumnByDataIndex: function(dataIndex){
58265         var index = this.findColumnIndex(dataIndex);
58266         return index > -1 ? this.config[index] : false;
58267     },
58268     
58269     /**
58270      * Returns the index for a specified column id.
58271      * @param {String} id The column id
58272      * @return {Number} the index, or -1 if not found
58273      */
58274     getIndexById : function(id){
58275         for(var i = 0, len = this.config.length; i < len; i++){
58276             if(this.config[i].id == id){
58277                 return i;
58278             }
58279         }
58280         return -1;
58281     },
58282     
58283     /**
58284      * Returns the index for a specified column dataIndex.
58285      * @param {String} dataIndex The column dataIndex
58286      * @return {Number} the index, or -1 if not found
58287      */
58288     
58289     findColumnIndex : function(dataIndex){
58290         for(var i = 0, len = this.config.length; i < len; i++){
58291             if(this.config[i].dataIndex == dataIndex){
58292                 return i;
58293             }
58294         }
58295         return -1;
58296     },
58297     
58298     
58299     moveColumn : function(oldIndex, newIndex){
58300         var c = this.config[oldIndex];
58301         this.config.splice(oldIndex, 1);
58302         this.config.splice(newIndex, 0, c);
58303         this.dataMap = null;
58304         this.fireEvent("columnmoved", this, oldIndex, newIndex);
58305     },
58306
58307     isLocked : function(colIndex){
58308         return this.config[colIndex].locked === true;
58309     },
58310
58311     setLocked : function(colIndex, value, suppressEvent){
58312         if(this.isLocked(colIndex) == value){
58313             return;
58314         }
58315         this.config[colIndex].locked = value;
58316         if(!suppressEvent){
58317             this.fireEvent("columnlockchange", this, colIndex, value);
58318         }
58319     },
58320
58321     getTotalLockedWidth : function(){
58322         var totalWidth = 0;
58323         for(var i = 0; i < this.config.length; i++){
58324             if(this.isLocked(i) && !this.isHidden(i)){
58325                 this.totalWidth += this.getColumnWidth(i);
58326             }
58327         }
58328         return totalWidth;
58329     },
58330
58331     getLockedCount : function(){
58332         for(var i = 0, len = this.config.length; i < len; i++){
58333             if(!this.isLocked(i)){
58334                 return i;
58335             }
58336         }
58337         
58338         return this.config.length;
58339     },
58340
58341     /**
58342      * Returns the number of columns.
58343      * @return {Number}
58344      */
58345     getColumnCount : function(visibleOnly){
58346         if(visibleOnly === true){
58347             var c = 0;
58348             for(var i = 0, len = this.config.length; i < len; i++){
58349                 if(!this.isHidden(i)){
58350                     c++;
58351                 }
58352             }
58353             return c;
58354         }
58355         return this.config.length;
58356     },
58357
58358     /**
58359      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
58360      * @param {Function} fn
58361      * @param {Object} scope (optional)
58362      * @return {Array} result
58363      */
58364     getColumnsBy : function(fn, scope){
58365         var r = [];
58366         for(var i = 0, len = this.config.length; i < len; i++){
58367             var c = this.config[i];
58368             if(fn.call(scope||this, c, i) === true){
58369                 r[r.length] = c;
58370             }
58371         }
58372         return r;
58373     },
58374
58375     /**
58376      * Returns true if the specified column is sortable.
58377      * @param {Number} col The column index
58378      * @return {Boolean}
58379      */
58380     isSortable : function(col){
58381         if(typeof this.config[col].sortable == "undefined"){
58382             return this.defaultSortable;
58383         }
58384         return this.config[col].sortable;
58385     },
58386
58387     /**
58388      * Returns the rendering (formatting) function defined for the column.
58389      * @param {Number} col The column index.
58390      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
58391      */
58392     getRenderer : function(col){
58393         if(!this.config[col].renderer){
58394             return Roo.grid.ColumnModel.defaultRenderer;
58395         }
58396         return this.config[col].renderer;
58397     },
58398
58399     /**
58400      * Sets the rendering (formatting) function for a column.
58401      * @param {Number} col The column index
58402      * @param {Function} fn The function to use to process the cell's raw data
58403      * to return HTML markup for the grid view. The render function is called with
58404      * the following parameters:<ul>
58405      * <li>Data value.</li>
58406      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
58407      * <li>css A CSS style string to apply to the table cell.</li>
58408      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
58409      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
58410      * <li>Row index</li>
58411      * <li>Column index</li>
58412      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
58413      */
58414     setRenderer : function(col, fn){
58415         this.config[col].renderer = fn;
58416     },
58417
58418     /**
58419      * Returns the width for the specified column.
58420      * @param {Number} col The column index
58421      * @return {Number}
58422      */
58423     getColumnWidth : function(col){
58424         return this.config[col].width * 1 || this.defaultWidth;
58425     },
58426
58427     /**
58428      * Sets the width for a column.
58429      * @param {Number} col The column index
58430      * @param {Number} width The new width
58431      */
58432     setColumnWidth : function(col, width, suppressEvent){
58433         this.config[col].width = width;
58434         this.totalWidth = null;
58435         if(!suppressEvent){
58436              this.fireEvent("widthchange", this, col, width);
58437         }
58438     },
58439
58440     /**
58441      * Returns the total width of all columns.
58442      * @param {Boolean} includeHidden True to include hidden column widths
58443      * @return {Number}
58444      */
58445     getTotalWidth : function(includeHidden){
58446         if(!this.totalWidth){
58447             this.totalWidth = 0;
58448             for(var i = 0, len = this.config.length; i < len; i++){
58449                 if(includeHidden || !this.isHidden(i)){
58450                     this.totalWidth += this.getColumnWidth(i);
58451                 }
58452             }
58453         }
58454         return this.totalWidth;
58455     },
58456
58457     /**
58458      * Returns the header for the specified column.
58459      * @param {Number} col The column index
58460      * @return {String}
58461      */
58462     getColumnHeader : function(col){
58463         return this.config[col].header;
58464     },
58465
58466     /**
58467      * Sets the header for a column.
58468      * @param {Number} col The column index
58469      * @param {String} header The new header
58470      */
58471     setColumnHeader : function(col, header){
58472         this.config[col].header = header;
58473         this.fireEvent("headerchange", this, col, header);
58474     },
58475
58476     /**
58477      * Returns the tooltip for the specified column.
58478      * @param {Number} col The column index
58479      * @return {String}
58480      */
58481     getColumnTooltip : function(col){
58482             return this.config[col].tooltip;
58483     },
58484     /**
58485      * Sets the tooltip for a column.
58486      * @param {Number} col The column index
58487      * @param {String} tooltip The new tooltip
58488      */
58489     setColumnTooltip : function(col, tooltip){
58490             this.config[col].tooltip = tooltip;
58491     },
58492
58493     /**
58494      * Returns the dataIndex for the specified column.
58495      * @param {Number} col The column index
58496      * @return {Number}
58497      */
58498     getDataIndex : function(col){
58499         return this.config[col].dataIndex;
58500     },
58501
58502     /**
58503      * Sets the dataIndex for a column.
58504      * @param {Number} col The column index
58505      * @param {Number} dataIndex The new dataIndex
58506      */
58507     setDataIndex : function(col, dataIndex){
58508         this.config[col].dataIndex = dataIndex;
58509     },
58510
58511     
58512     
58513     /**
58514      * Returns true if the cell is editable.
58515      * @param {Number} colIndex The column index
58516      * @param {Number} rowIndex The row index - this is nto actually used..?
58517      * @return {Boolean}
58518      */
58519     isCellEditable : function(colIndex, rowIndex){
58520         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
58521     },
58522
58523     /**
58524      * Returns the editor defined for the cell/column.
58525      * return false or null to disable editing.
58526      * @param {Number} colIndex The column index
58527      * @param {Number} rowIndex The row index
58528      * @return {Object}
58529      */
58530     getCellEditor : function(colIndex, rowIndex){
58531         return this.config[colIndex].editor;
58532     },
58533
58534     /**
58535      * Sets if a column is editable.
58536      * @param {Number} col The column index
58537      * @param {Boolean} editable True if the column is editable
58538      */
58539     setEditable : function(col, editable){
58540         this.config[col].editable = editable;
58541     },
58542
58543
58544     /**
58545      * Returns true if the column is hidden.
58546      * @param {Number} colIndex The column index
58547      * @return {Boolean}
58548      */
58549     isHidden : function(colIndex){
58550         return this.config[colIndex].hidden;
58551     },
58552
58553
58554     /**
58555      * Returns true if the column width cannot be changed
58556      */
58557     isFixed : function(colIndex){
58558         return this.config[colIndex].fixed;
58559     },
58560
58561     /**
58562      * Returns true if the column can be resized
58563      * @return {Boolean}
58564      */
58565     isResizable : function(colIndex){
58566         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
58567     },
58568     /**
58569      * Sets if a column is hidden.
58570      * @param {Number} colIndex The column index
58571      * @param {Boolean} hidden True if the column is hidden
58572      */
58573     setHidden : function(colIndex, hidden){
58574         this.config[colIndex].hidden = hidden;
58575         this.totalWidth = null;
58576         this.fireEvent("hiddenchange", this, colIndex, hidden);
58577     },
58578
58579     /**
58580      * Sets the editor for a column.
58581      * @param {Number} col The column index
58582      * @param {Object} editor The editor object
58583      */
58584     setEditor : function(col, editor){
58585         this.config[col].editor = editor;
58586     }
58587 });
58588
58589 Roo.grid.ColumnModel.defaultRenderer = function(value)
58590 {
58591     if(typeof value == "object") {
58592         return value;
58593     }
58594         if(typeof value == "string" && value.length < 1){
58595             return "&#160;";
58596         }
58597     
58598         return String.format("{0}", value);
58599 };
58600
58601 // Alias for backwards compatibility
58602 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
58603 /*
58604  * Based on:
58605  * Ext JS Library 1.1.1
58606  * Copyright(c) 2006-2007, Ext JS, LLC.
58607  *
58608  * Originally Released Under LGPL - original licence link has changed is not relivant.
58609  *
58610  * Fork - LGPL
58611  * <script type="text/javascript">
58612  */
58613
58614 /**
58615  * @class Roo.grid.AbstractSelectionModel
58616  * @extends Roo.util.Observable
58617  * Abstract base class for grid SelectionModels.  It provides the interface that should be
58618  * implemented by descendant classes.  This class should not be directly instantiated.
58619  * @constructor
58620  */
58621 Roo.grid.AbstractSelectionModel = function(){
58622     this.locked = false;
58623     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
58624 };
58625
58626 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
58627     /** @ignore Called by the grid automatically. Do not call directly. */
58628     init : function(grid){
58629         this.grid = grid;
58630         this.initEvents();
58631     },
58632
58633     /**
58634      * Locks the selections.
58635      */
58636     lock : function(){
58637         this.locked = true;
58638     },
58639
58640     /**
58641      * Unlocks the selections.
58642      */
58643     unlock : function(){
58644         this.locked = false;
58645     },
58646
58647     /**
58648      * Returns true if the selections are locked.
58649      * @return {Boolean}
58650      */
58651     isLocked : function(){
58652         return this.locked;
58653     }
58654 });/*
58655  * Based on:
58656  * Ext JS Library 1.1.1
58657  * Copyright(c) 2006-2007, Ext JS, LLC.
58658  *
58659  * Originally Released Under LGPL - original licence link has changed is not relivant.
58660  *
58661  * Fork - LGPL
58662  * <script type="text/javascript">
58663  */
58664 /**
58665  * @extends Roo.grid.AbstractSelectionModel
58666  * @class Roo.grid.RowSelectionModel
58667  * The default SelectionModel used by {@link Roo.grid.Grid}.
58668  * It supports multiple selections and keyboard selection/navigation. 
58669  * @constructor
58670  * @param {Object} config
58671  */
58672 Roo.grid.RowSelectionModel = function(config){
58673     Roo.apply(this, config);
58674     this.selections = new Roo.util.MixedCollection(false, function(o){
58675         return o.id;
58676     });
58677
58678     this.last = false;
58679     this.lastActive = false;
58680
58681     this.addEvents({
58682         /**
58683              * @event selectionchange
58684              * Fires when the selection changes
58685              * @param {SelectionModel} this
58686              */
58687             "selectionchange" : true,
58688         /**
58689              * @event afterselectionchange
58690              * Fires after the selection changes (eg. by key press or clicking)
58691              * @param {SelectionModel} this
58692              */
58693             "afterselectionchange" : true,
58694         /**
58695              * @event beforerowselect
58696              * Fires when a row is selected being selected, return false to cancel.
58697              * @param {SelectionModel} this
58698              * @param {Number} rowIndex The selected index
58699              * @param {Boolean} keepExisting False if other selections will be cleared
58700              */
58701             "beforerowselect" : true,
58702         /**
58703              * @event rowselect
58704              * Fires when a row is selected.
58705              * @param {SelectionModel} this
58706              * @param {Number} rowIndex The selected index
58707              * @param {Roo.data.Record} r The record
58708              */
58709             "rowselect" : true,
58710         /**
58711              * @event rowdeselect
58712              * Fires when a row is deselected.
58713              * @param {SelectionModel} this
58714              * @param {Number} rowIndex The selected index
58715              */
58716         "rowdeselect" : true
58717     });
58718     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
58719     this.locked = false;
58720 };
58721
58722 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
58723     /**
58724      * @cfg {Boolean} singleSelect
58725      * True to allow selection of only one row at a time (defaults to false)
58726      */
58727     singleSelect : false,
58728
58729     // private
58730     initEvents : function(){
58731
58732         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
58733             this.grid.on("mousedown", this.handleMouseDown, this);
58734         }else{ // allow click to work like normal
58735             this.grid.on("rowclick", this.handleDragableRowClick, this);
58736         }
58737
58738         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
58739             "up" : function(e){
58740                 if(!e.shiftKey){
58741                     this.selectPrevious(e.shiftKey);
58742                 }else if(this.last !== false && this.lastActive !== false){
58743                     var last = this.last;
58744                     this.selectRange(this.last,  this.lastActive-1);
58745                     this.grid.getView().focusRow(this.lastActive);
58746                     if(last !== false){
58747                         this.last = last;
58748                     }
58749                 }else{
58750                     this.selectFirstRow();
58751                 }
58752                 this.fireEvent("afterselectionchange", this);
58753             },
58754             "down" : function(e){
58755                 if(!e.shiftKey){
58756                     this.selectNext(e.shiftKey);
58757                 }else if(this.last !== false && this.lastActive !== false){
58758                     var last = this.last;
58759                     this.selectRange(this.last,  this.lastActive+1);
58760                     this.grid.getView().focusRow(this.lastActive);
58761                     if(last !== false){
58762                         this.last = last;
58763                     }
58764                 }else{
58765                     this.selectFirstRow();
58766                 }
58767                 this.fireEvent("afterselectionchange", this);
58768             },
58769             scope: this
58770         });
58771
58772         var view = this.grid.view;
58773         view.on("refresh", this.onRefresh, this);
58774         view.on("rowupdated", this.onRowUpdated, this);
58775         view.on("rowremoved", this.onRemove, this);
58776     },
58777
58778     // private
58779     onRefresh : function(){
58780         var ds = this.grid.dataSource, i, v = this.grid.view;
58781         var s = this.selections;
58782         s.each(function(r){
58783             if((i = ds.indexOfId(r.id)) != -1){
58784                 v.onRowSelect(i);
58785                 s.add(ds.getAt(i)); // updating the selection relate data
58786             }else{
58787                 s.remove(r);
58788             }
58789         });
58790     },
58791
58792     // private
58793     onRemove : function(v, index, r){
58794         this.selections.remove(r);
58795     },
58796
58797     // private
58798     onRowUpdated : function(v, index, r){
58799         if(this.isSelected(r)){
58800             v.onRowSelect(index);
58801         }
58802     },
58803
58804     /**
58805      * Select records.
58806      * @param {Array} records The records to select
58807      * @param {Boolean} keepExisting (optional) True to keep existing selections
58808      */
58809     selectRecords : function(records, keepExisting){
58810         if(!keepExisting){
58811             this.clearSelections();
58812         }
58813         var ds = this.grid.dataSource;
58814         for(var i = 0, len = records.length; i < len; i++){
58815             this.selectRow(ds.indexOf(records[i]), true);
58816         }
58817     },
58818
58819     /**
58820      * Gets the number of selected rows.
58821      * @return {Number}
58822      */
58823     getCount : function(){
58824         return this.selections.length;
58825     },
58826
58827     /**
58828      * Selects the first row in the grid.
58829      */
58830     selectFirstRow : function(){
58831         this.selectRow(0);
58832     },
58833
58834     /**
58835      * Select the last row.
58836      * @param {Boolean} keepExisting (optional) True to keep existing selections
58837      */
58838     selectLastRow : function(keepExisting){
58839         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
58840     },
58841
58842     /**
58843      * Selects the row immediately following the last selected row.
58844      * @param {Boolean} keepExisting (optional) True to keep existing selections
58845      */
58846     selectNext : function(keepExisting){
58847         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
58848             this.selectRow(this.last+1, keepExisting);
58849             this.grid.getView().focusRow(this.last);
58850         }
58851     },
58852
58853     /**
58854      * Selects the row that precedes the last selected row.
58855      * @param {Boolean} keepExisting (optional) True to keep existing selections
58856      */
58857     selectPrevious : function(keepExisting){
58858         if(this.last){
58859             this.selectRow(this.last-1, keepExisting);
58860             this.grid.getView().focusRow(this.last);
58861         }
58862     },
58863
58864     /**
58865      * Returns the selected records
58866      * @return {Array} Array of selected records
58867      */
58868     getSelections : function(){
58869         return [].concat(this.selections.items);
58870     },
58871
58872     /**
58873      * Returns the first selected record.
58874      * @return {Record}
58875      */
58876     getSelected : function(){
58877         return this.selections.itemAt(0);
58878     },
58879
58880
58881     /**
58882      * Clears all selections.
58883      */
58884     clearSelections : function(fast){
58885         if(this.locked) {
58886             return;
58887         }
58888         if(fast !== true){
58889             var ds = this.grid.dataSource;
58890             var s = this.selections;
58891             s.each(function(r){
58892                 this.deselectRow(ds.indexOfId(r.id));
58893             }, this);
58894             s.clear();
58895         }else{
58896             this.selections.clear();
58897         }
58898         this.last = false;
58899     },
58900
58901
58902     /**
58903      * Selects all rows.
58904      */
58905     selectAll : function(){
58906         if(this.locked) {
58907             return;
58908         }
58909         this.selections.clear();
58910         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
58911             this.selectRow(i, true);
58912         }
58913     },
58914
58915     /**
58916      * Returns True if there is a selection.
58917      * @return {Boolean}
58918      */
58919     hasSelection : function(){
58920         return this.selections.length > 0;
58921     },
58922
58923     /**
58924      * Returns True if the specified row is selected.
58925      * @param {Number/Record} record The record or index of the record to check
58926      * @return {Boolean}
58927      */
58928     isSelected : function(index){
58929         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
58930         return (r && this.selections.key(r.id) ? true : false);
58931     },
58932
58933     /**
58934      * Returns True if the specified record id is selected.
58935      * @param {String} id The id of record to check
58936      * @return {Boolean}
58937      */
58938     isIdSelected : function(id){
58939         return (this.selections.key(id) ? true : false);
58940     },
58941
58942     // private
58943     handleMouseDown : function(e, t){
58944         var view = this.grid.getView(), rowIndex;
58945         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
58946             return;
58947         };
58948         if(e.shiftKey && this.last !== false){
58949             var last = this.last;
58950             this.selectRange(last, rowIndex, e.ctrlKey);
58951             this.last = last; // reset the last
58952             view.focusRow(rowIndex);
58953         }else{
58954             var isSelected = this.isSelected(rowIndex);
58955             if(e.button !== 0 && isSelected){
58956                 view.focusRow(rowIndex);
58957             }else if(e.ctrlKey && isSelected){
58958                 this.deselectRow(rowIndex);
58959             }else if(!isSelected){
58960                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
58961                 view.focusRow(rowIndex);
58962             }
58963         }
58964         this.fireEvent("afterselectionchange", this);
58965     },
58966     // private
58967     handleDragableRowClick :  function(grid, rowIndex, e) 
58968     {
58969         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
58970             this.selectRow(rowIndex, false);
58971             grid.view.focusRow(rowIndex);
58972              this.fireEvent("afterselectionchange", this);
58973         }
58974     },
58975     
58976     /**
58977      * Selects multiple rows.
58978      * @param {Array} rows Array of the indexes of the row to select
58979      * @param {Boolean} keepExisting (optional) True to keep existing selections
58980      */
58981     selectRows : function(rows, keepExisting){
58982         if(!keepExisting){
58983             this.clearSelections();
58984         }
58985         for(var i = 0, len = rows.length; i < len; i++){
58986             this.selectRow(rows[i], true);
58987         }
58988     },
58989
58990     /**
58991      * Selects a range of rows. All rows in between startRow and endRow are also selected.
58992      * @param {Number} startRow The index of the first row in the range
58993      * @param {Number} endRow The index of the last row in the range
58994      * @param {Boolean} keepExisting (optional) True to retain existing selections
58995      */
58996     selectRange : function(startRow, endRow, keepExisting){
58997         if(this.locked) {
58998             return;
58999         }
59000         if(!keepExisting){
59001             this.clearSelections();
59002         }
59003         if(startRow <= endRow){
59004             for(var i = startRow; i <= endRow; i++){
59005                 this.selectRow(i, true);
59006             }
59007         }else{
59008             for(var i = startRow; i >= endRow; i--){
59009                 this.selectRow(i, true);
59010             }
59011         }
59012     },
59013
59014     /**
59015      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
59016      * @param {Number} startRow The index of the first row in the range
59017      * @param {Number} endRow The index of the last row in the range
59018      */
59019     deselectRange : function(startRow, endRow, preventViewNotify){
59020         if(this.locked) {
59021             return;
59022         }
59023         for(var i = startRow; i <= endRow; i++){
59024             this.deselectRow(i, preventViewNotify);
59025         }
59026     },
59027
59028     /**
59029      * Selects a row.
59030      * @param {Number} row The index of the row to select
59031      * @param {Boolean} keepExisting (optional) True to keep existing selections
59032      */
59033     selectRow : function(index, keepExisting, preventViewNotify){
59034         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
59035             return;
59036         }
59037         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
59038             if(!keepExisting || this.singleSelect){
59039                 this.clearSelections();
59040             }
59041             var r = this.grid.dataSource.getAt(index);
59042             this.selections.add(r);
59043             this.last = this.lastActive = index;
59044             if(!preventViewNotify){
59045                 this.grid.getView().onRowSelect(index);
59046             }
59047             this.fireEvent("rowselect", this, index, r);
59048             this.fireEvent("selectionchange", this);
59049         }
59050     },
59051
59052     /**
59053      * Deselects a row.
59054      * @param {Number} row The index of the row to deselect
59055      */
59056     deselectRow : function(index, preventViewNotify){
59057         if(this.locked) {
59058             return;
59059         }
59060         if(this.last == index){
59061             this.last = false;
59062         }
59063         if(this.lastActive == index){
59064             this.lastActive = false;
59065         }
59066         var r = this.grid.dataSource.getAt(index);
59067         this.selections.remove(r);
59068         if(!preventViewNotify){
59069             this.grid.getView().onRowDeselect(index);
59070         }
59071         this.fireEvent("rowdeselect", this, index);
59072         this.fireEvent("selectionchange", this);
59073     },
59074
59075     // private
59076     restoreLast : function(){
59077         if(this._last){
59078             this.last = this._last;
59079         }
59080     },
59081
59082     // private
59083     acceptsNav : function(row, col, cm){
59084         return !cm.isHidden(col) && cm.isCellEditable(col, row);
59085     },
59086
59087     // private
59088     onEditorKey : function(field, e){
59089         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
59090         if(k == e.TAB){
59091             e.stopEvent();
59092             ed.completeEdit();
59093             if(e.shiftKey){
59094                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
59095             }else{
59096                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59097             }
59098         }else if(k == e.ENTER && !e.ctrlKey){
59099             e.stopEvent();
59100             ed.completeEdit();
59101             if(e.shiftKey){
59102                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
59103             }else{
59104                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
59105             }
59106         }else if(k == e.ESC){
59107             ed.cancelEdit();
59108         }
59109         if(newCell){
59110             g.startEditing(newCell[0], newCell[1]);
59111         }
59112     }
59113 });/*
59114  * Based on:
59115  * Ext JS Library 1.1.1
59116  * Copyright(c) 2006-2007, Ext JS, LLC.
59117  *
59118  * Originally Released Under LGPL - original licence link has changed is not relivant.
59119  *
59120  * Fork - LGPL
59121  * <script type="text/javascript">
59122  */
59123 /**
59124  * @class Roo.grid.CellSelectionModel
59125  * @extends Roo.grid.AbstractSelectionModel
59126  * This class provides the basic implementation for cell selection in a grid.
59127  * @constructor
59128  * @param {Object} config The object containing the configuration of this model.
59129  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
59130  */
59131 Roo.grid.CellSelectionModel = function(config){
59132     Roo.apply(this, config);
59133
59134     this.selection = null;
59135
59136     this.addEvents({
59137         /**
59138              * @event beforerowselect
59139              * Fires before a cell is selected.
59140              * @param {SelectionModel} this
59141              * @param {Number} rowIndex The selected row index
59142              * @param {Number} colIndex The selected cell index
59143              */
59144             "beforecellselect" : true,
59145         /**
59146              * @event cellselect
59147              * Fires when a cell is selected.
59148              * @param {SelectionModel} this
59149              * @param {Number} rowIndex The selected row index
59150              * @param {Number} colIndex The selected cell index
59151              */
59152             "cellselect" : true,
59153         /**
59154              * @event selectionchange
59155              * Fires when the active selection changes.
59156              * @param {SelectionModel} this
59157              * @param {Object} selection null for no selection or an object (o) with two properties
59158                 <ul>
59159                 <li>o.record: the record object for the row the selection is in</li>
59160                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
59161                 </ul>
59162              */
59163             "selectionchange" : true,
59164         /**
59165              * @event tabend
59166              * Fires when the tab (or enter) was pressed on the last editable cell
59167              * You can use this to trigger add new row.
59168              * @param {SelectionModel} this
59169              */
59170             "tabend" : true,
59171          /**
59172              * @event beforeeditnext
59173              * Fires before the next editable sell is made active
59174              * You can use this to skip to another cell or fire the tabend
59175              *    if you set cell to false
59176              * @param {Object} eventdata object : { cell : [ row, col ] } 
59177              */
59178             "beforeeditnext" : true
59179     });
59180     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
59181 };
59182
59183 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
59184     
59185     enter_is_tab: false,
59186
59187     /** @ignore */
59188     initEvents : function(){
59189         this.grid.on("mousedown", this.handleMouseDown, this);
59190         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
59191         var view = this.grid.view;
59192         view.on("refresh", this.onViewChange, this);
59193         view.on("rowupdated", this.onRowUpdated, this);
59194         view.on("beforerowremoved", this.clearSelections, this);
59195         view.on("beforerowsinserted", this.clearSelections, this);
59196         if(this.grid.isEditor){
59197             this.grid.on("beforeedit", this.beforeEdit,  this);
59198         }
59199     },
59200
59201         //private
59202     beforeEdit : function(e){
59203         this.select(e.row, e.column, false, true, e.record);
59204     },
59205
59206         //private
59207     onRowUpdated : function(v, index, r){
59208         if(this.selection && this.selection.record == r){
59209             v.onCellSelect(index, this.selection.cell[1]);
59210         }
59211     },
59212
59213         //private
59214     onViewChange : function(){
59215         this.clearSelections(true);
59216     },
59217
59218         /**
59219          * Returns the currently selected cell,.
59220          * @return {Array} The selected cell (row, column) or null if none selected.
59221          */
59222     getSelectedCell : function(){
59223         return this.selection ? this.selection.cell : null;
59224     },
59225
59226     /**
59227      * Clears all selections.
59228      * @param {Boolean} true to prevent the gridview from being notified about the change.
59229      */
59230     clearSelections : function(preventNotify){
59231         var s = this.selection;
59232         if(s){
59233             if(preventNotify !== true){
59234                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
59235             }
59236             this.selection = null;
59237             this.fireEvent("selectionchange", this, null);
59238         }
59239     },
59240
59241     /**
59242      * Returns true if there is a selection.
59243      * @return {Boolean}
59244      */
59245     hasSelection : function(){
59246         return this.selection ? true : false;
59247     },
59248
59249     /** @ignore */
59250     handleMouseDown : function(e, t){
59251         var v = this.grid.getView();
59252         if(this.isLocked()){
59253             return;
59254         };
59255         var row = v.findRowIndex(t);
59256         var cell = v.findCellIndex(t);
59257         if(row !== false && cell !== false){
59258             this.select(row, cell);
59259         }
59260     },
59261
59262     /**
59263      * Selects a cell.
59264      * @param {Number} rowIndex
59265      * @param {Number} collIndex
59266      */
59267     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
59268         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
59269             this.clearSelections();
59270             r = r || this.grid.dataSource.getAt(rowIndex);
59271             this.selection = {
59272                 record : r,
59273                 cell : [rowIndex, colIndex]
59274             };
59275             if(!preventViewNotify){
59276                 var v = this.grid.getView();
59277                 v.onCellSelect(rowIndex, colIndex);
59278                 if(preventFocus !== true){
59279                     v.focusCell(rowIndex, colIndex);
59280                 }
59281             }
59282             this.fireEvent("cellselect", this, rowIndex, colIndex);
59283             this.fireEvent("selectionchange", this, this.selection);
59284         }
59285     },
59286
59287         //private
59288     isSelectable : function(rowIndex, colIndex, cm){
59289         return !cm.isHidden(colIndex);
59290     },
59291
59292     /** @ignore */
59293     handleKeyDown : function(e){
59294         //Roo.log('Cell Sel Model handleKeyDown');
59295         if(!e.isNavKeyPress()){
59296             return;
59297         }
59298         var g = this.grid, s = this.selection;
59299         if(!s){
59300             e.stopEvent();
59301             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
59302             if(cell){
59303                 this.select(cell[0], cell[1]);
59304             }
59305             return;
59306         }
59307         var sm = this;
59308         var walk = function(row, col, step){
59309             return g.walkCells(row, col, step, sm.isSelectable,  sm);
59310         };
59311         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
59312         var newCell;
59313
59314       
59315
59316         switch(k){
59317             case e.TAB:
59318                 // handled by onEditorKey
59319                 if (g.isEditor && g.editing) {
59320                     return;
59321                 }
59322                 if(e.shiftKey) {
59323                     newCell = walk(r, c-1, -1);
59324                 } else {
59325                     newCell = walk(r, c+1, 1);
59326                 }
59327                 break;
59328             
59329             case e.DOWN:
59330                newCell = walk(r+1, c, 1);
59331                 break;
59332             
59333             case e.UP:
59334                 newCell = walk(r-1, c, -1);
59335                 break;
59336             
59337             case e.RIGHT:
59338                 newCell = walk(r, c+1, 1);
59339                 break;
59340             
59341             case e.LEFT:
59342                 newCell = walk(r, c-1, -1);
59343                 break;
59344             
59345             case e.ENTER:
59346                 
59347                 if(g.isEditor && !g.editing){
59348                    g.startEditing(r, c);
59349                    e.stopEvent();
59350                    return;
59351                 }
59352                 
59353                 
59354              break;
59355         };
59356         if(newCell){
59357             this.select(newCell[0], newCell[1]);
59358             e.stopEvent();
59359             
59360         }
59361     },
59362
59363     acceptsNav : function(row, col, cm){
59364         return !cm.isHidden(col) && cm.isCellEditable(col, row);
59365     },
59366     /**
59367      * Selects a cell.
59368      * @param {Number} field (not used) - as it's normally used as a listener
59369      * @param {Number} e - event - fake it by using
59370      *
59371      * var e = Roo.EventObjectImpl.prototype;
59372      * e.keyCode = e.TAB
59373      *
59374      * 
59375      */
59376     onEditorKey : function(field, e){
59377         
59378         var k = e.getKey(),
59379             newCell,
59380             g = this.grid,
59381             ed = g.activeEditor,
59382             forward = false;
59383         ///Roo.log('onEditorKey' + k);
59384         
59385         
59386         if (this.enter_is_tab && k == e.ENTER) {
59387             k = e.TAB;
59388         }
59389         
59390         if(k == e.TAB){
59391             if(e.shiftKey){
59392                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
59393             }else{
59394                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59395                 forward = true;
59396             }
59397             
59398             e.stopEvent();
59399             
59400         } else if(k == e.ENTER &&  !e.ctrlKey){
59401             ed.completeEdit();
59402             e.stopEvent();
59403             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59404         
59405                 } else if(k == e.ESC){
59406             ed.cancelEdit();
59407         }
59408                 
59409         if (newCell) {
59410             var ecall = { cell : newCell, forward : forward };
59411             this.fireEvent('beforeeditnext', ecall );
59412             newCell = ecall.cell;
59413                         forward = ecall.forward;
59414         }
59415                 
59416         if(newCell){
59417             //Roo.log('next cell after edit');
59418             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
59419         } else if (forward) {
59420             // tabbed past last
59421             this.fireEvent.defer(100, this, ['tabend',this]);
59422         }
59423     }
59424 });/*
59425  * Based on:
59426  * Ext JS Library 1.1.1
59427  * Copyright(c) 2006-2007, Ext JS, LLC.
59428  *
59429  * Originally Released Under LGPL - original licence link has changed is not relivant.
59430  *
59431  * Fork - LGPL
59432  * <script type="text/javascript">
59433  */
59434  
59435 /**
59436  * @class Roo.grid.EditorGrid
59437  * @extends Roo.grid.Grid
59438  * Class for creating and editable grid.
59439  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
59440  * The container MUST have some type of size defined for the grid to fill. The container will be 
59441  * automatically set to position relative if it isn't already.
59442  * @param {Object} dataSource The data model to bind to
59443  * @param {Object} colModel The column model with info about this grid's columns
59444  */
59445 Roo.grid.EditorGrid = function(container, config){
59446     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
59447     this.getGridEl().addClass("xedit-grid");
59448
59449     if(!this.selModel){
59450         this.selModel = new Roo.grid.CellSelectionModel();
59451     }
59452
59453     this.activeEditor = null;
59454
59455         this.addEvents({
59456             /**
59457              * @event beforeedit
59458              * Fires before cell editing is triggered. The edit event object has the following properties <br />
59459              * <ul style="padding:5px;padding-left:16px;">
59460              * <li>grid - This grid</li>
59461              * <li>record - The record being edited</li>
59462              * <li>field - The field name being edited</li>
59463              * <li>value - The value for the field being edited.</li>
59464              * <li>row - The grid row index</li>
59465              * <li>column - The grid column index</li>
59466              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
59467              * </ul>
59468              * @param {Object} e An edit event (see above for description)
59469              */
59470             "beforeedit" : true,
59471             /**
59472              * @event afteredit
59473              * Fires after a cell is edited. <br />
59474              * <ul style="padding:5px;padding-left:16px;">
59475              * <li>grid - This grid</li>
59476              * <li>record - The record being edited</li>
59477              * <li>field - The field name being edited</li>
59478              * <li>value - The value being set</li>
59479              * <li>originalValue - The original value for the field, before the edit.</li>
59480              * <li>row - The grid row index</li>
59481              * <li>column - The grid column index</li>
59482              * </ul>
59483              * @param {Object} e An edit event (see above for description)
59484              */
59485             "afteredit" : true,
59486             /**
59487              * @event validateedit
59488              * Fires after a cell is edited, but before the value is set in the record. 
59489          * You can use this to modify the value being set in the field, Return false
59490              * to cancel the change. The edit event object has the following properties <br />
59491              * <ul style="padding:5px;padding-left:16px;">
59492          * <li>editor - This editor</li>
59493              * <li>grid - This grid</li>
59494              * <li>record - The record being edited</li>
59495              * <li>field - The field name being edited</li>
59496              * <li>value - The value being set</li>
59497              * <li>originalValue - The original value for the field, before the edit.</li>
59498              * <li>row - The grid row index</li>
59499              * <li>column - The grid column index</li>
59500              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
59501              * </ul>
59502              * @param {Object} e An edit event (see above for description)
59503              */
59504             "validateedit" : true
59505         });
59506     this.on("bodyscroll", this.stopEditing,  this);
59507     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
59508 };
59509
59510 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
59511     /**
59512      * @cfg {Number} clicksToEdit
59513      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
59514      */
59515     clicksToEdit: 2,
59516
59517     // private
59518     isEditor : true,
59519     // private
59520     trackMouseOver: false, // causes very odd FF errors
59521
59522     onCellDblClick : function(g, row, col){
59523         this.startEditing(row, col);
59524     },
59525
59526     onEditComplete : function(ed, value, startValue){
59527         this.editing = false;
59528         this.activeEditor = null;
59529         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
59530         var r = ed.record;
59531         var field = this.colModel.getDataIndex(ed.col);
59532         var e = {
59533             grid: this,
59534             record: r,
59535             field: field,
59536             originalValue: startValue,
59537             value: value,
59538             row: ed.row,
59539             column: ed.col,
59540             cancel:false,
59541             editor: ed
59542         };
59543         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
59544         cell.show();
59545           
59546         if(String(value) !== String(startValue)){
59547             
59548             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
59549                 r.set(field, e.value);
59550                 // if we are dealing with a combo box..
59551                 // then we also set the 'name' colum to be the displayField
59552                 if (ed.field.displayField && ed.field.name) {
59553                     r.set(ed.field.name, ed.field.el.dom.value);
59554                 }
59555                 
59556                 delete e.cancel; //?? why!!!
59557                 this.fireEvent("afteredit", e);
59558             }
59559         } else {
59560             this.fireEvent("afteredit", e); // always fire it!
59561         }
59562         this.view.focusCell(ed.row, ed.col);
59563     },
59564
59565     /**
59566      * Starts editing the specified for the specified row/column
59567      * @param {Number} rowIndex
59568      * @param {Number} colIndex
59569      */
59570     startEditing : function(row, col){
59571         this.stopEditing();
59572         if(this.colModel.isCellEditable(col, row)){
59573             this.view.ensureVisible(row, col, true);
59574           
59575             var r = this.dataSource.getAt(row);
59576             var field = this.colModel.getDataIndex(col);
59577             var cell = Roo.get(this.view.getCell(row,col));
59578             var e = {
59579                 grid: this,
59580                 record: r,
59581                 field: field,
59582                 value: r.data[field],
59583                 row: row,
59584                 column: col,
59585                 cancel:false 
59586             };
59587             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
59588                 this.editing = true;
59589                 var ed = this.colModel.getCellEditor(col, row);
59590                 
59591                 if (!ed) {
59592                     return;
59593                 }
59594                 if(!ed.rendered){
59595                     ed.render(ed.parentEl || document.body);
59596                 }
59597                 ed.field.reset();
59598                
59599                 cell.hide();
59600                 
59601                 (function(){ // complex but required for focus issues in safari, ie and opera
59602                     ed.row = row;
59603                     ed.col = col;
59604                     ed.record = r;
59605                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
59606                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
59607                     this.activeEditor = ed;
59608                     var v = r.data[field];
59609                     ed.startEdit(this.view.getCell(row, col), v);
59610                     // combo's with 'displayField and name set
59611                     if (ed.field.displayField && ed.field.name) {
59612                         ed.field.el.dom.value = r.data[ed.field.name];
59613                     }
59614                     
59615                     
59616                 }).defer(50, this);
59617             }
59618         }
59619     },
59620         
59621     /**
59622      * Stops any active editing
59623      */
59624     stopEditing : function(){
59625         if(this.activeEditor){
59626             this.activeEditor.completeEdit();
59627         }
59628         this.activeEditor = null;
59629     },
59630         
59631          /**
59632      * Called to get grid's drag proxy text, by default returns this.ddText.
59633      * @return {String}
59634      */
59635     getDragDropText : function(){
59636         var count = this.selModel.getSelectedCell() ? 1 : 0;
59637         return String.format(this.ddText, count, count == 1 ? '' : 's');
59638     }
59639         
59640 });/*
59641  * Based on:
59642  * Ext JS Library 1.1.1
59643  * Copyright(c) 2006-2007, Ext JS, LLC.
59644  *
59645  * Originally Released Under LGPL - original licence link has changed is not relivant.
59646  *
59647  * Fork - LGPL
59648  * <script type="text/javascript">
59649  */
59650
59651 // private - not really -- you end up using it !
59652 // This is a support class used internally by the Grid components
59653
59654 /**
59655  * @class Roo.grid.GridEditor
59656  * @extends Roo.Editor
59657  * Class for creating and editable grid elements.
59658  * @param {Object} config any settings (must include field)
59659  */
59660 Roo.grid.GridEditor = function(field, config){
59661     if (!config && field.field) {
59662         config = field;
59663         field = Roo.factory(config.field, Roo.form);
59664     }
59665     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
59666     field.monitorTab = false;
59667 };
59668
59669 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
59670     
59671     /**
59672      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
59673      */
59674     
59675     alignment: "tl-tl",
59676     autoSize: "width",
59677     hideEl : false,
59678     cls: "x-small-editor x-grid-editor",
59679     shim:false,
59680     shadow:"frame"
59681 });/*
59682  * Based on:
59683  * Ext JS Library 1.1.1
59684  * Copyright(c) 2006-2007, Ext JS, LLC.
59685  *
59686  * Originally Released Under LGPL - original licence link has changed is not relivant.
59687  *
59688  * Fork - LGPL
59689  * <script type="text/javascript">
59690  */
59691   
59692
59693   
59694 Roo.grid.PropertyRecord = Roo.data.Record.create([
59695     {name:'name',type:'string'},  'value'
59696 ]);
59697
59698
59699 Roo.grid.PropertyStore = function(grid, source){
59700     this.grid = grid;
59701     this.store = new Roo.data.Store({
59702         recordType : Roo.grid.PropertyRecord
59703     });
59704     this.store.on('update', this.onUpdate,  this);
59705     if(source){
59706         this.setSource(source);
59707     }
59708     Roo.grid.PropertyStore.superclass.constructor.call(this);
59709 };
59710
59711
59712
59713 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
59714     setSource : function(o){
59715         this.source = o;
59716         this.store.removeAll();
59717         var data = [];
59718         for(var k in o){
59719             if(this.isEditableValue(o[k])){
59720                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
59721             }
59722         }
59723         this.store.loadRecords({records: data}, {}, true);
59724     },
59725
59726     onUpdate : function(ds, record, type){
59727         if(type == Roo.data.Record.EDIT){
59728             var v = record.data['value'];
59729             var oldValue = record.modified['value'];
59730             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
59731                 this.source[record.id] = v;
59732                 record.commit();
59733                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
59734             }else{
59735                 record.reject();
59736             }
59737         }
59738     },
59739
59740     getProperty : function(row){
59741        return this.store.getAt(row);
59742     },
59743
59744     isEditableValue: function(val){
59745         if(val && val instanceof Date){
59746             return true;
59747         }else if(typeof val == 'object' || typeof val == 'function'){
59748             return false;
59749         }
59750         return true;
59751     },
59752
59753     setValue : function(prop, value){
59754         this.source[prop] = value;
59755         this.store.getById(prop).set('value', value);
59756     },
59757
59758     getSource : function(){
59759         return this.source;
59760     }
59761 });
59762
59763 Roo.grid.PropertyColumnModel = function(grid, store){
59764     this.grid = grid;
59765     var g = Roo.grid;
59766     g.PropertyColumnModel.superclass.constructor.call(this, [
59767         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
59768         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
59769     ]);
59770     this.store = store;
59771     this.bselect = Roo.DomHelper.append(document.body, {
59772         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
59773             {tag: 'option', value: 'true', html: 'true'},
59774             {tag: 'option', value: 'false', html: 'false'}
59775         ]
59776     });
59777     Roo.id(this.bselect);
59778     var f = Roo.form;
59779     this.editors = {
59780         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
59781         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
59782         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
59783         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
59784         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
59785     };
59786     this.renderCellDelegate = this.renderCell.createDelegate(this);
59787     this.renderPropDelegate = this.renderProp.createDelegate(this);
59788 };
59789
59790 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
59791     
59792     
59793     nameText : 'Name',
59794     valueText : 'Value',
59795     
59796     dateFormat : 'm/j/Y',
59797     
59798     
59799     renderDate : function(dateVal){
59800         return dateVal.dateFormat(this.dateFormat);
59801     },
59802
59803     renderBool : function(bVal){
59804         return bVal ? 'true' : 'false';
59805     },
59806
59807     isCellEditable : function(colIndex, rowIndex){
59808         return colIndex == 1;
59809     },
59810
59811     getRenderer : function(col){
59812         return col == 1 ?
59813             this.renderCellDelegate : this.renderPropDelegate;
59814     },
59815
59816     renderProp : function(v){
59817         return this.getPropertyName(v);
59818     },
59819
59820     renderCell : function(val){
59821         var rv = val;
59822         if(val instanceof Date){
59823             rv = this.renderDate(val);
59824         }else if(typeof val == 'boolean'){
59825             rv = this.renderBool(val);
59826         }
59827         return Roo.util.Format.htmlEncode(rv);
59828     },
59829
59830     getPropertyName : function(name){
59831         var pn = this.grid.propertyNames;
59832         return pn && pn[name] ? pn[name] : name;
59833     },
59834
59835     getCellEditor : function(colIndex, rowIndex){
59836         var p = this.store.getProperty(rowIndex);
59837         var n = p.data['name'], val = p.data['value'];
59838         
59839         if(typeof(this.grid.customEditors[n]) == 'string'){
59840             return this.editors[this.grid.customEditors[n]];
59841         }
59842         if(typeof(this.grid.customEditors[n]) != 'undefined'){
59843             return this.grid.customEditors[n];
59844         }
59845         if(val instanceof Date){
59846             return this.editors['date'];
59847         }else if(typeof val == 'number'){
59848             return this.editors['number'];
59849         }else if(typeof val == 'boolean'){
59850             return this.editors['boolean'];
59851         }else{
59852             return this.editors['string'];
59853         }
59854     }
59855 });
59856
59857 /**
59858  * @class Roo.grid.PropertyGrid
59859  * @extends Roo.grid.EditorGrid
59860  * This class represents the  interface of a component based property grid control.
59861  * <br><br>Usage:<pre><code>
59862  var grid = new Roo.grid.PropertyGrid("my-container-id", {
59863       
59864  });
59865  // set any options
59866  grid.render();
59867  * </code></pre>
59868   
59869  * @constructor
59870  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
59871  * The container MUST have some type of size defined for the grid to fill. The container will be
59872  * automatically set to position relative if it isn't already.
59873  * @param {Object} config A config object that sets properties on this grid.
59874  */
59875 Roo.grid.PropertyGrid = function(container, config){
59876     config = config || {};
59877     var store = new Roo.grid.PropertyStore(this);
59878     this.store = store;
59879     var cm = new Roo.grid.PropertyColumnModel(this, store);
59880     store.store.sort('name', 'ASC');
59881     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
59882         ds: store.store,
59883         cm: cm,
59884         enableColLock:false,
59885         enableColumnMove:false,
59886         stripeRows:false,
59887         trackMouseOver: false,
59888         clicksToEdit:1
59889     }, config));
59890     this.getGridEl().addClass('x-props-grid');
59891     this.lastEditRow = null;
59892     this.on('columnresize', this.onColumnResize, this);
59893     this.addEvents({
59894          /**
59895              * @event beforepropertychange
59896              * Fires before a property changes (return false to stop?)
59897              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
59898              * @param {String} id Record Id
59899              * @param {String} newval New Value
59900          * @param {String} oldval Old Value
59901              */
59902         "beforepropertychange": true,
59903         /**
59904              * @event propertychange
59905              * Fires after a property changes
59906              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
59907              * @param {String} id Record Id
59908              * @param {String} newval New Value
59909          * @param {String} oldval Old Value
59910              */
59911         "propertychange": true
59912     });
59913     this.customEditors = this.customEditors || {};
59914 };
59915 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
59916     
59917      /**
59918      * @cfg {Object} customEditors map of colnames=> custom editors.
59919      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
59920      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
59921      * false disables editing of the field.
59922          */
59923     
59924       /**
59925      * @cfg {Object} propertyNames map of property Names to their displayed value
59926          */
59927     
59928     render : function(){
59929         Roo.grid.PropertyGrid.superclass.render.call(this);
59930         this.autoSize.defer(100, this);
59931     },
59932
59933     autoSize : function(){
59934         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
59935         if(this.view){
59936             this.view.fitColumns();
59937         }
59938     },
59939
59940     onColumnResize : function(){
59941         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
59942         this.autoSize();
59943     },
59944     /**
59945      * Sets the data for the Grid
59946      * accepts a Key => Value object of all the elements avaiable.
59947      * @param {Object} data  to appear in grid.
59948      */
59949     setSource : function(source){
59950         this.store.setSource(source);
59951         //this.autoSize();
59952     },
59953     /**
59954      * Gets all the data from the grid.
59955      * @return {Object} data  data stored in grid
59956      */
59957     getSource : function(){
59958         return this.store.getSource();
59959     }
59960 });/*
59961   
59962  * Licence LGPL
59963  
59964  */
59965  
59966 /**
59967  * @class Roo.grid.Calendar
59968  * @extends Roo.util.Grid
59969  * This class extends the Grid to provide a calendar widget
59970  * <br><br>Usage:<pre><code>
59971  var grid = new Roo.grid.Calendar("my-container-id", {
59972      ds: myDataStore,
59973      cm: myColModel,
59974      selModel: mySelectionModel,
59975      autoSizeColumns: true,
59976      monitorWindowResize: false,
59977      trackMouseOver: true
59978      eventstore : real data store..
59979  });
59980  // set any options
59981  grid.render();
59982   
59983   * @constructor
59984  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
59985  * The container MUST have some type of size defined for the grid to fill. The container will be
59986  * automatically set to position relative if it isn't already.
59987  * @param {Object} config A config object that sets properties on this grid.
59988  */
59989 Roo.grid.Calendar = function(container, config){
59990         // initialize the container
59991         this.container = Roo.get(container);
59992         this.container.update("");
59993         this.container.setStyle("overflow", "hidden");
59994     this.container.addClass('x-grid-container');
59995
59996     this.id = this.container.id;
59997
59998     Roo.apply(this, config);
59999     // check and correct shorthanded configs
60000     
60001     var rows = [];
60002     var d =1;
60003     for (var r = 0;r < 6;r++) {
60004         
60005         rows[r]=[];
60006         for (var c =0;c < 7;c++) {
60007             rows[r][c]= '';
60008         }
60009     }
60010     if (this.eventStore) {
60011         this.eventStore= Roo.factory(this.eventStore, Roo.data);
60012         this.eventStore.on('load',this.onLoad, this);
60013         this.eventStore.on('beforeload',this.clearEvents, this);
60014          
60015     }
60016     
60017     this.dataSource = new Roo.data.Store({
60018             proxy: new Roo.data.MemoryProxy(rows),
60019             reader: new Roo.data.ArrayReader({}, [
60020                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
60021     });
60022
60023     this.dataSource.load();
60024     this.ds = this.dataSource;
60025     this.ds.xmodule = this.xmodule || false;
60026     
60027     
60028     var cellRender = function(v,x,r)
60029     {
60030         return String.format(
60031             '<div class="fc-day  fc-widget-content"><div>' +
60032                 '<div class="fc-event-container"></div>' +
60033                 '<div class="fc-day-number">{0}</div>'+
60034                 
60035                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
60036             '</div></div>', v);
60037     
60038     }
60039     
60040     
60041     this.colModel = new Roo.grid.ColumnModel( [
60042         {
60043             xtype: 'ColumnModel',
60044             xns: Roo.grid,
60045             dataIndex : 'weekday0',
60046             header : 'Sunday',
60047             renderer : cellRender
60048         },
60049         {
60050             xtype: 'ColumnModel',
60051             xns: Roo.grid,
60052             dataIndex : 'weekday1',
60053             header : 'Monday',
60054             renderer : cellRender
60055         },
60056         {
60057             xtype: 'ColumnModel',
60058             xns: Roo.grid,
60059             dataIndex : 'weekday2',
60060             header : 'Tuesday',
60061             renderer : cellRender
60062         },
60063         {
60064             xtype: 'ColumnModel',
60065             xns: Roo.grid,
60066             dataIndex : 'weekday3',
60067             header : 'Wednesday',
60068             renderer : cellRender
60069         },
60070         {
60071             xtype: 'ColumnModel',
60072             xns: Roo.grid,
60073             dataIndex : 'weekday4',
60074             header : 'Thursday',
60075             renderer : cellRender
60076         },
60077         {
60078             xtype: 'ColumnModel',
60079             xns: Roo.grid,
60080             dataIndex : 'weekday5',
60081             header : 'Friday',
60082             renderer : cellRender
60083         },
60084         {
60085             xtype: 'ColumnModel',
60086             xns: Roo.grid,
60087             dataIndex : 'weekday6',
60088             header : 'Saturday',
60089             renderer : cellRender
60090         }
60091     ]);
60092     this.cm = this.colModel;
60093     this.cm.xmodule = this.xmodule || false;
60094  
60095         
60096           
60097     //this.selModel = new Roo.grid.CellSelectionModel();
60098     //this.sm = this.selModel;
60099     //this.selModel.init(this);
60100     
60101     
60102     if(this.width){
60103         this.container.setWidth(this.width);
60104     }
60105
60106     if(this.height){
60107         this.container.setHeight(this.height);
60108     }
60109     /** @private */
60110         this.addEvents({
60111         // raw events
60112         /**
60113          * @event click
60114          * The raw click event for the entire grid.
60115          * @param {Roo.EventObject} e
60116          */
60117         "click" : true,
60118         /**
60119          * @event dblclick
60120          * The raw dblclick event for the entire grid.
60121          * @param {Roo.EventObject} e
60122          */
60123         "dblclick" : true,
60124         /**
60125          * @event contextmenu
60126          * The raw contextmenu event for the entire grid.
60127          * @param {Roo.EventObject} e
60128          */
60129         "contextmenu" : true,
60130         /**
60131          * @event mousedown
60132          * The raw mousedown event for the entire grid.
60133          * @param {Roo.EventObject} e
60134          */
60135         "mousedown" : true,
60136         /**
60137          * @event mouseup
60138          * The raw mouseup event for the entire grid.
60139          * @param {Roo.EventObject} e
60140          */
60141         "mouseup" : true,
60142         /**
60143          * @event mouseover
60144          * The raw mouseover event for the entire grid.
60145          * @param {Roo.EventObject} e
60146          */
60147         "mouseover" : true,
60148         /**
60149          * @event mouseout
60150          * The raw mouseout event for the entire grid.
60151          * @param {Roo.EventObject} e
60152          */
60153         "mouseout" : true,
60154         /**
60155          * @event keypress
60156          * The raw keypress event for the entire grid.
60157          * @param {Roo.EventObject} e
60158          */
60159         "keypress" : true,
60160         /**
60161          * @event keydown
60162          * The raw keydown event for the entire grid.
60163          * @param {Roo.EventObject} e
60164          */
60165         "keydown" : true,
60166
60167         // custom events
60168
60169         /**
60170          * @event cellclick
60171          * Fires when a cell is clicked
60172          * @param {Grid} this
60173          * @param {Number} rowIndex
60174          * @param {Number} columnIndex
60175          * @param {Roo.EventObject} e
60176          */
60177         "cellclick" : true,
60178         /**
60179          * @event celldblclick
60180          * Fires when a cell is double clicked
60181          * @param {Grid} this
60182          * @param {Number} rowIndex
60183          * @param {Number} columnIndex
60184          * @param {Roo.EventObject} e
60185          */
60186         "celldblclick" : true,
60187         /**
60188          * @event rowclick
60189          * Fires when a row is clicked
60190          * @param {Grid} this
60191          * @param {Number} rowIndex
60192          * @param {Roo.EventObject} e
60193          */
60194         "rowclick" : true,
60195         /**
60196          * @event rowdblclick
60197          * Fires when a row is double clicked
60198          * @param {Grid} this
60199          * @param {Number} rowIndex
60200          * @param {Roo.EventObject} e
60201          */
60202         "rowdblclick" : true,
60203         /**
60204          * @event headerclick
60205          * Fires when a header is clicked
60206          * @param {Grid} this
60207          * @param {Number} columnIndex
60208          * @param {Roo.EventObject} e
60209          */
60210         "headerclick" : true,
60211         /**
60212          * @event headerdblclick
60213          * Fires when a header cell is double clicked
60214          * @param {Grid} this
60215          * @param {Number} columnIndex
60216          * @param {Roo.EventObject} e
60217          */
60218         "headerdblclick" : true,
60219         /**
60220          * @event rowcontextmenu
60221          * Fires when a row is right clicked
60222          * @param {Grid} this
60223          * @param {Number} rowIndex
60224          * @param {Roo.EventObject} e
60225          */
60226         "rowcontextmenu" : true,
60227         /**
60228          * @event cellcontextmenu
60229          * Fires when a cell is right clicked
60230          * @param {Grid} this
60231          * @param {Number} rowIndex
60232          * @param {Number} cellIndex
60233          * @param {Roo.EventObject} e
60234          */
60235          "cellcontextmenu" : true,
60236         /**
60237          * @event headercontextmenu
60238          * Fires when a header is right clicked
60239          * @param {Grid} this
60240          * @param {Number} columnIndex
60241          * @param {Roo.EventObject} e
60242          */
60243         "headercontextmenu" : true,
60244         /**
60245          * @event bodyscroll
60246          * Fires when the body element is scrolled
60247          * @param {Number} scrollLeft
60248          * @param {Number} scrollTop
60249          */
60250         "bodyscroll" : true,
60251         /**
60252          * @event columnresize
60253          * Fires when the user resizes a column
60254          * @param {Number} columnIndex
60255          * @param {Number} newSize
60256          */
60257         "columnresize" : true,
60258         /**
60259          * @event columnmove
60260          * Fires when the user moves a column
60261          * @param {Number} oldIndex
60262          * @param {Number} newIndex
60263          */
60264         "columnmove" : true,
60265         /**
60266          * @event startdrag
60267          * Fires when row(s) start being dragged
60268          * @param {Grid} this
60269          * @param {Roo.GridDD} dd The drag drop object
60270          * @param {event} e The raw browser event
60271          */
60272         "startdrag" : true,
60273         /**
60274          * @event enddrag
60275          * Fires when a drag operation is complete
60276          * @param {Grid} this
60277          * @param {Roo.GridDD} dd The drag drop object
60278          * @param {event} e The raw browser event
60279          */
60280         "enddrag" : true,
60281         /**
60282          * @event dragdrop
60283          * Fires when dragged row(s) are dropped on a valid DD target
60284          * @param {Grid} this
60285          * @param {Roo.GridDD} dd The drag drop object
60286          * @param {String} targetId The target drag drop object
60287          * @param {event} e The raw browser event
60288          */
60289         "dragdrop" : true,
60290         /**
60291          * @event dragover
60292          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
60293          * @param {Grid} this
60294          * @param {Roo.GridDD} dd The drag drop object
60295          * @param {String} targetId The target drag drop object
60296          * @param {event} e The raw browser event
60297          */
60298         "dragover" : true,
60299         /**
60300          * @event dragenter
60301          *  Fires when the dragged row(s) first cross another DD target while being dragged
60302          * @param {Grid} this
60303          * @param {Roo.GridDD} dd The drag drop object
60304          * @param {String} targetId The target drag drop object
60305          * @param {event} e The raw browser event
60306          */
60307         "dragenter" : true,
60308         /**
60309          * @event dragout
60310          * Fires when the dragged row(s) leave another DD target while being dragged
60311          * @param {Grid} this
60312          * @param {Roo.GridDD} dd The drag drop object
60313          * @param {String} targetId The target drag drop object
60314          * @param {event} e The raw browser event
60315          */
60316         "dragout" : true,
60317         /**
60318          * @event rowclass
60319          * Fires when a row is rendered, so you can change add a style to it.
60320          * @param {GridView} gridview   The grid view
60321          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
60322          */
60323         'rowclass' : true,
60324
60325         /**
60326          * @event render
60327          * Fires when the grid is rendered
60328          * @param {Grid} grid
60329          */
60330         'render' : true,
60331             /**
60332              * @event select
60333              * Fires when a date is selected
60334              * @param {DatePicker} this
60335              * @param {Date} date The selected date
60336              */
60337         'select': true,
60338         /**
60339              * @event monthchange
60340              * Fires when the displayed month changes 
60341              * @param {DatePicker} this
60342              * @param {Date} date The selected month
60343              */
60344         'monthchange': true,
60345         /**
60346              * @event evententer
60347              * Fires when mouse over an event
60348              * @param {Calendar} this
60349              * @param {event} Event
60350              */
60351         'evententer': true,
60352         /**
60353              * @event eventleave
60354              * Fires when the mouse leaves an
60355              * @param {Calendar} this
60356              * @param {event}
60357              */
60358         'eventleave': true,
60359         /**
60360              * @event eventclick
60361              * Fires when the mouse click an
60362              * @param {Calendar} this
60363              * @param {event}
60364              */
60365         'eventclick': true,
60366         /**
60367              * @event eventrender
60368              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
60369              * @param {Calendar} this
60370              * @param {data} data to be modified
60371              */
60372         'eventrender': true
60373         
60374     });
60375
60376     Roo.grid.Grid.superclass.constructor.call(this);
60377     this.on('render', function() {
60378         this.view.el.addClass('x-grid-cal'); 
60379         
60380         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
60381
60382     },this);
60383     
60384     if (!Roo.grid.Calendar.style) {
60385         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
60386             
60387             
60388             '.x-grid-cal .x-grid-col' :  {
60389                 height: 'auto !important',
60390                 'vertical-align': 'top'
60391             },
60392             '.x-grid-cal  .fc-event-hori' : {
60393                 height: '14px'
60394             }
60395              
60396             
60397         }, Roo.id());
60398     }
60399
60400     
60401     
60402 };
60403 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
60404     /**
60405      * @cfg {Store} eventStore The store that loads events.
60406      */
60407     eventStore : 25,
60408
60409      
60410     activeDate : false,
60411     startDay : 0,
60412     autoWidth : true,
60413     monitorWindowResize : false,
60414
60415     
60416     resizeColumns : function() {
60417         var col = (this.view.el.getWidth() / 7) - 3;
60418         // loop through cols, and setWidth
60419         for(var i =0 ; i < 7 ; i++){
60420             this.cm.setColumnWidth(i, col);
60421         }
60422     },
60423      setDate :function(date) {
60424         
60425         Roo.log('setDate?');
60426         
60427         this.resizeColumns();
60428         var vd = this.activeDate;
60429         this.activeDate = date;
60430 //        if(vd && this.el){
60431 //            var t = date.getTime();
60432 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
60433 //                Roo.log('using add remove');
60434 //                
60435 //                this.fireEvent('monthchange', this, date);
60436 //                
60437 //                this.cells.removeClass("fc-state-highlight");
60438 //                this.cells.each(function(c){
60439 //                   if(c.dateValue == t){
60440 //                       c.addClass("fc-state-highlight");
60441 //                       setTimeout(function(){
60442 //                            try{c.dom.firstChild.focus();}catch(e){}
60443 //                       }, 50);
60444 //                       return false;
60445 //                   }
60446 //                   return true;
60447 //                });
60448 //                return;
60449 //            }
60450 //        }
60451         
60452         var days = date.getDaysInMonth();
60453         
60454         var firstOfMonth = date.getFirstDateOfMonth();
60455         var startingPos = firstOfMonth.getDay()-this.startDay;
60456         
60457         if(startingPos < this.startDay){
60458             startingPos += 7;
60459         }
60460         
60461         var pm = date.add(Date.MONTH, -1);
60462         var prevStart = pm.getDaysInMonth()-startingPos;
60463 //        
60464         
60465         
60466         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
60467         
60468         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
60469         //this.cells.addClassOnOver('fc-state-hover');
60470         
60471         var cells = this.cells.elements;
60472         var textEls = this.textNodes;
60473         
60474         //Roo.each(cells, function(cell){
60475         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
60476         //});
60477         
60478         days += startingPos;
60479
60480         // convert everything to numbers so it's fast
60481         var day = 86400000;
60482         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
60483         //Roo.log(d);
60484         //Roo.log(pm);
60485         //Roo.log(prevStart);
60486         
60487         var today = new Date().clearTime().getTime();
60488         var sel = date.clearTime().getTime();
60489         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
60490         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
60491         var ddMatch = this.disabledDatesRE;
60492         var ddText = this.disabledDatesText;
60493         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
60494         var ddaysText = this.disabledDaysText;
60495         var format = this.format;
60496         
60497         var setCellClass = function(cal, cell){
60498             
60499             //Roo.log('set Cell Class');
60500             cell.title = "";
60501             var t = d.getTime();
60502             
60503             //Roo.log(d);
60504             
60505             
60506             cell.dateValue = t;
60507             if(t == today){
60508                 cell.className += " fc-today";
60509                 cell.className += " fc-state-highlight";
60510                 cell.title = cal.todayText;
60511             }
60512             if(t == sel){
60513                 // disable highlight in other month..
60514                 cell.className += " fc-state-highlight";
60515                 
60516             }
60517             // disabling
60518             if(t < min) {
60519                 //cell.className = " fc-state-disabled";
60520                 cell.title = cal.minText;
60521                 return;
60522             }
60523             if(t > max) {
60524                 //cell.className = " fc-state-disabled";
60525                 cell.title = cal.maxText;
60526                 return;
60527             }
60528             if(ddays){
60529                 if(ddays.indexOf(d.getDay()) != -1){
60530                     // cell.title = ddaysText;
60531                    // cell.className = " fc-state-disabled";
60532                 }
60533             }
60534             if(ddMatch && format){
60535                 var fvalue = d.dateFormat(format);
60536                 if(ddMatch.test(fvalue)){
60537                     cell.title = ddText.replace("%0", fvalue);
60538                    cell.className = " fc-state-disabled";
60539                 }
60540             }
60541             
60542             if (!cell.initialClassName) {
60543                 cell.initialClassName = cell.dom.className;
60544             }
60545             
60546             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
60547         };
60548
60549         var i = 0;
60550         
60551         for(; i < startingPos; i++) {
60552             cells[i].dayName =  (++prevStart);
60553             Roo.log(textEls[i]);
60554             d.setDate(d.getDate()+1);
60555             
60556             //cells[i].className = "fc-past fc-other-month";
60557             setCellClass(this, cells[i]);
60558         }
60559         
60560         var intDay = 0;
60561         
60562         for(; i < days; i++){
60563             intDay = i - startingPos + 1;
60564             cells[i].dayName =  (intDay);
60565             d.setDate(d.getDate()+1);
60566             
60567             cells[i].className = ''; // "x-date-active";
60568             setCellClass(this, cells[i]);
60569         }
60570         var extraDays = 0;
60571         
60572         for(; i < 42; i++) {
60573             //textEls[i].innerHTML = (++extraDays);
60574             
60575             d.setDate(d.getDate()+1);
60576             cells[i].dayName = (++extraDays);
60577             cells[i].className = "fc-future fc-other-month";
60578             setCellClass(this, cells[i]);
60579         }
60580         
60581         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
60582         
60583         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
60584         
60585         // this will cause all the cells to mis
60586         var rows= [];
60587         var i =0;
60588         for (var r = 0;r < 6;r++) {
60589             for (var c =0;c < 7;c++) {
60590                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
60591             }    
60592         }
60593         
60594         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
60595         for(i=0;i<cells.length;i++) {
60596             
60597             this.cells.elements[i].dayName = cells[i].dayName ;
60598             this.cells.elements[i].className = cells[i].className;
60599             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
60600             this.cells.elements[i].title = cells[i].title ;
60601             this.cells.elements[i].dateValue = cells[i].dateValue ;
60602         }
60603         
60604         
60605         
60606         
60607         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
60608         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
60609         
60610         ////if(totalRows != 6){
60611             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
60612            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
60613        // }
60614         
60615         this.fireEvent('monthchange', this, date);
60616         
60617         
60618     },
60619  /**
60620      * Returns the grid's SelectionModel.
60621      * @return {SelectionModel}
60622      */
60623     getSelectionModel : function(){
60624         if(!this.selModel){
60625             this.selModel = new Roo.grid.CellSelectionModel();
60626         }
60627         return this.selModel;
60628     },
60629
60630     load: function() {
60631         this.eventStore.load()
60632         
60633         
60634         
60635     },
60636     
60637     findCell : function(dt) {
60638         dt = dt.clearTime().getTime();
60639         var ret = false;
60640         this.cells.each(function(c){
60641             //Roo.log("check " +c.dateValue + '?=' + dt);
60642             if(c.dateValue == dt){
60643                 ret = c;
60644                 return false;
60645             }
60646             return true;
60647         });
60648         
60649         return ret;
60650     },
60651     
60652     findCells : function(rec) {
60653         var s = rec.data.start_dt.clone().clearTime().getTime();
60654        // Roo.log(s);
60655         var e= rec.data.end_dt.clone().clearTime().getTime();
60656        // Roo.log(e);
60657         var ret = [];
60658         this.cells.each(function(c){
60659              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
60660             
60661             if(c.dateValue > e){
60662                 return ;
60663             }
60664             if(c.dateValue < s){
60665                 return ;
60666             }
60667             ret.push(c);
60668         });
60669         
60670         return ret;    
60671     },
60672     
60673     findBestRow: function(cells)
60674     {
60675         var ret = 0;
60676         
60677         for (var i =0 ; i < cells.length;i++) {
60678             ret  = Math.max(cells[i].rows || 0,ret);
60679         }
60680         return ret;
60681         
60682     },
60683     
60684     
60685     addItem : function(rec)
60686     {
60687         // look for vertical location slot in
60688         var cells = this.findCells(rec);
60689         
60690         rec.row = this.findBestRow(cells);
60691         
60692         // work out the location.
60693         
60694         var crow = false;
60695         var rows = [];
60696         for(var i =0; i < cells.length; i++) {
60697             if (!crow) {
60698                 crow = {
60699                     start : cells[i],
60700                     end :  cells[i]
60701                 };
60702                 continue;
60703             }
60704             if (crow.start.getY() == cells[i].getY()) {
60705                 // on same row.
60706                 crow.end = cells[i];
60707                 continue;
60708             }
60709             // different row.
60710             rows.push(crow);
60711             crow = {
60712                 start: cells[i],
60713                 end : cells[i]
60714             };
60715             
60716         }
60717         
60718         rows.push(crow);
60719         rec.els = [];
60720         rec.rows = rows;
60721         rec.cells = cells;
60722         for (var i = 0; i < cells.length;i++) {
60723             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
60724             
60725         }
60726         
60727         
60728     },
60729     
60730     clearEvents: function() {
60731         
60732         if (!this.eventStore.getCount()) {
60733             return;
60734         }
60735         // reset number of rows in cells.
60736         Roo.each(this.cells.elements, function(c){
60737             c.rows = 0;
60738         });
60739         
60740         this.eventStore.each(function(e) {
60741             this.clearEvent(e);
60742         },this);
60743         
60744     },
60745     
60746     clearEvent : function(ev)
60747     {
60748         if (ev.els) {
60749             Roo.each(ev.els, function(el) {
60750                 el.un('mouseenter' ,this.onEventEnter, this);
60751                 el.un('mouseleave' ,this.onEventLeave, this);
60752                 el.remove();
60753             },this);
60754             ev.els = [];
60755         }
60756     },
60757     
60758     
60759     renderEvent : function(ev,ctr) {
60760         if (!ctr) {
60761              ctr = this.view.el.select('.fc-event-container',true).first();
60762         }
60763         
60764          
60765         this.clearEvent(ev);
60766             //code
60767        
60768         
60769         
60770         ev.els = [];
60771         var cells = ev.cells;
60772         var rows = ev.rows;
60773         this.fireEvent('eventrender', this, ev);
60774         
60775         for(var i =0; i < rows.length; i++) {
60776             
60777             cls = '';
60778             if (i == 0) {
60779                 cls += ' fc-event-start';
60780             }
60781             if ((i+1) == rows.length) {
60782                 cls += ' fc-event-end';
60783             }
60784             
60785             //Roo.log(ev.data);
60786             // how many rows should it span..
60787             var cg = this.eventTmpl.append(ctr,Roo.apply({
60788                 fccls : cls
60789                 
60790             }, ev.data) , true);
60791             
60792             
60793             cg.on('mouseenter' ,this.onEventEnter, this, ev);
60794             cg.on('mouseleave' ,this.onEventLeave, this, ev);
60795             cg.on('click', this.onEventClick, this, ev);
60796             
60797             ev.els.push(cg);
60798             
60799             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
60800             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
60801             //Roo.log(cg);
60802              
60803             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
60804             cg.setWidth(ebox.right - sbox.x -2);
60805         }
60806     },
60807     
60808     renderEvents: function()
60809     {   
60810         // first make sure there is enough space..
60811         
60812         if (!this.eventTmpl) {
60813             this.eventTmpl = new Roo.Template(
60814                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
60815                     '<div class="fc-event-inner">' +
60816                         '<span class="fc-event-time">{time}</span>' +
60817                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
60818                     '</div>' +
60819                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
60820                 '</div>'
60821             );
60822                 
60823         }
60824                
60825         
60826         
60827         this.cells.each(function(c) {
60828             //Roo.log(c.select('.fc-day-content div',true).first());
60829             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
60830         });
60831         
60832         var ctr = this.view.el.select('.fc-event-container',true).first();
60833         
60834         var cls;
60835         this.eventStore.each(function(ev){
60836             
60837             this.renderEvent(ev);
60838              
60839              
60840         }, this);
60841         this.view.layout();
60842         
60843     },
60844     
60845     onEventEnter: function (e, el,event,d) {
60846         this.fireEvent('evententer', this, el, event);
60847     },
60848     
60849     onEventLeave: function (e, el,event,d) {
60850         this.fireEvent('eventleave', this, el, event);
60851     },
60852     
60853     onEventClick: function (e, el,event,d) {
60854         this.fireEvent('eventclick', this, el, event);
60855     },
60856     
60857     onMonthChange: function () {
60858         this.store.load();
60859     },
60860     
60861     onLoad: function () {
60862         
60863         //Roo.log('calendar onload');
60864 //         
60865         if(this.eventStore.getCount() > 0){
60866             
60867            
60868             
60869             this.eventStore.each(function(d){
60870                 
60871                 
60872                 // FIXME..
60873                 var add =   d.data;
60874                 if (typeof(add.end_dt) == 'undefined')  {
60875                     Roo.log("Missing End time in calendar data: ");
60876                     Roo.log(d);
60877                     return;
60878                 }
60879                 if (typeof(add.start_dt) == 'undefined')  {
60880                     Roo.log("Missing Start time in calendar data: ");
60881                     Roo.log(d);
60882                     return;
60883                 }
60884                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
60885                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
60886                 add.id = add.id || d.id;
60887                 add.title = add.title || '??';
60888                 
60889                 this.addItem(d);
60890                 
60891              
60892             },this);
60893         }
60894         
60895         this.renderEvents();
60896     }
60897     
60898
60899 });
60900 /*
60901  grid : {
60902                 xtype: 'Grid',
60903                 xns: Roo.grid,
60904                 listeners : {
60905                     render : function ()
60906                     {
60907                         _this.grid = this;
60908                         
60909                         if (!this.view.el.hasClass('course-timesheet')) {
60910                             this.view.el.addClass('course-timesheet');
60911                         }
60912                         if (this.tsStyle) {
60913                             this.ds.load({});
60914                             return; 
60915                         }
60916                         Roo.log('width');
60917                         Roo.log(_this.grid.view.el.getWidth());
60918                         
60919                         
60920                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
60921                             '.course-timesheet .x-grid-row' : {
60922                                 height: '80px'
60923                             },
60924                             '.x-grid-row td' : {
60925                                 'vertical-align' : 0
60926                             },
60927                             '.course-edit-link' : {
60928                                 'color' : 'blue',
60929                                 'text-overflow' : 'ellipsis',
60930                                 'overflow' : 'hidden',
60931                                 'white-space' : 'nowrap',
60932                                 'cursor' : 'pointer'
60933                             },
60934                             '.sub-link' : {
60935                                 'color' : 'green'
60936                             },
60937                             '.de-act-sup-link' : {
60938                                 'color' : 'purple',
60939                                 'text-decoration' : 'line-through'
60940                             },
60941                             '.de-act-link' : {
60942                                 'color' : 'red',
60943                                 'text-decoration' : 'line-through'
60944                             },
60945                             '.course-timesheet .course-highlight' : {
60946                                 'border-top-style': 'dashed !important',
60947                                 'border-bottom-bottom': 'dashed !important'
60948                             },
60949                             '.course-timesheet .course-item' : {
60950                                 'font-family'   : 'tahoma, arial, helvetica',
60951                                 'font-size'     : '11px',
60952                                 'overflow'      : 'hidden',
60953                                 'padding-left'  : '10px',
60954                                 'padding-right' : '10px',
60955                                 'padding-top' : '10px' 
60956                             }
60957                             
60958                         }, Roo.id());
60959                                 this.ds.load({});
60960                     }
60961                 },
60962                 autoWidth : true,
60963                 monitorWindowResize : false,
60964                 cellrenderer : function(v,x,r)
60965                 {
60966                     return v;
60967                 },
60968                 sm : {
60969                     xtype: 'CellSelectionModel',
60970                     xns: Roo.grid
60971                 },
60972                 dataSource : {
60973                     xtype: 'Store',
60974                     xns: Roo.data,
60975                     listeners : {
60976                         beforeload : function (_self, options)
60977                         {
60978                             options.params = options.params || {};
60979                             options.params._month = _this.monthField.getValue();
60980                             options.params.limit = 9999;
60981                             options.params['sort'] = 'when_dt';    
60982                             options.params['dir'] = 'ASC';    
60983                             this.proxy.loadResponse = this.loadResponse;
60984                             Roo.log("load?");
60985                             //this.addColumns();
60986                         },
60987                         load : function (_self, records, options)
60988                         {
60989                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
60990                                 // if you click on the translation.. you can edit it...
60991                                 var el = Roo.get(this);
60992                                 var id = el.dom.getAttribute('data-id');
60993                                 var d = el.dom.getAttribute('data-date');
60994                                 var t = el.dom.getAttribute('data-time');
60995                                 //var id = this.child('span').dom.textContent;
60996                                 
60997                                 //Roo.log(this);
60998                                 Pman.Dialog.CourseCalendar.show({
60999                                     id : id,
61000                                     when_d : d,
61001                                     when_t : t,
61002                                     productitem_active : id ? 1 : 0
61003                                 }, function() {
61004                                     _this.grid.ds.load({});
61005                                 });
61006                            
61007                            });
61008                            
61009                            _this.panel.fireEvent('resize', [ '', '' ]);
61010                         }
61011                     },
61012                     loadResponse : function(o, success, response){
61013                             // this is overridden on before load..
61014                             
61015                             Roo.log("our code?");       
61016                             //Roo.log(success);
61017                             //Roo.log(response)
61018                             delete this.activeRequest;
61019                             if(!success){
61020                                 this.fireEvent("loadexception", this, o, response);
61021                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
61022                                 return;
61023                             }
61024                             var result;
61025                             try {
61026                                 result = o.reader.read(response);
61027                             }catch(e){
61028                                 Roo.log("load exception?");
61029                                 this.fireEvent("loadexception", this, o, response, e);
61030                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
61031                                 return;
61032                             }
61033                             Roo.log("ready...");        
61034                             // loop through result.records;
61035                             // and set this.tdate[date] = [] << array of records..
61036                             _this.tdata  = {};
61037                             Roo.each(result.records, function(r){
61038                                 //Roo.log(r.data);
61039                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
61040                                     _this.tdata[r.data.when_dt.format('j')] = [];
61041                                 }
61042                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
61043                             });
61044                             
61045                             //Roo.log(_this.tdata);
61046                             
61047                             result.records = [];
61048                             result.totalRecords = 6;
61049                     
61050                             // let's generate some duumy records for the rows.
61051                             //var st = _this.dateField.getValue();
61052                             
61053                             // work out monday..
61054                             //st = st.add(Date.DAY, -1 * st.format('w'));
61055                             
61056                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
61057                             
61058                             var firstOfMonth = date.getFirstDayOfMonth();
61059                             var days = date.getDaysInMonth();
61060                             var d = 1;
61061                             var firstAdded = false;
61062                             for (var i = 0; i < result.totalRecords ; i++) {
61063                                 //var d= st.add(Date.DAY, i);
61064                                 var row = {};
61065                                 var added = 0;
61066                                 for(var w = 0 ; w < 7 ; w++){
61067                                     if(!firstAdded && firstOfMonth != w){
61068                                         continue;
61069                                     }
61070                                     if(d > days){
61071                                         continue;
61072                                     }
61073                                     firstAdded = true;
61074                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
61075                                     row['weekday'+w] = String.format(
61076                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
61077                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
61078                                                     d,
61079                                                     date.format('Y-m-')+dd
61080                                                 );
61081                                     added++;
61082                                     if(typeof(_this.tdata[d]) != 'undefined'){
61083                                         Roo.each(_this.tdata[d], function(r){
61084                                             var is_sub = '';
61085                                             var deactive = '';
61086                                             var id = r.id;
61087                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
61088                                             if(r.parent_id*1>0){
61089                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
61090                                                 id = r.parent_id;
61091                                             }
61092                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
61093                                                 deactive = 'de-act-link';
61094                                             }
61095                                             
61096                                             row['weekday'+w] += String.format(
61097                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
61098                                                     id, //0
61099                                                     r.product_id_name, //1
61100                                                     r.when_dt.format('h:ia'), //2
61101                                                     is_sub, //3
61102                                                     deactive, //4
61103                                                     desc // 5
61104                                             );
61105                                         });
61106                                     }
61107                                     d++;
61108                                 }
61109                                 
61110                                 // only do this if something added..
61111                                 if(added > 0){ 
61112                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
61113                                 }
61114                                 
61115                                 
61116                                 // push it twice. (second one with an hour..
61117                                 
61118                             }
61119                             //Roo.log(result);
61120                             this.fireEvent("load", this, o, o.request.arg);
61121                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
61122                         },
61123                     sortInfo : {field: 'when_dt', direction : 'ASC' },
61124                     proxy : {
61125                         xtype: 'HttpProxy',
61126                         xns: Roo.data,
61127                         method : 'GET',
61128                         url : baseURL + '/Roo/Shop_course.php'
61129                     },
61130                     reader : {
61131                         xtype: 'JsonReader',
61132                         xns: Roo.data,
61133                         id : 'id',
61134                         fields : [
61135                             {
61136                                 'name': 'id',
61137                                 'type': 'int'
61138                             },
61139                             {
61140                                 'name': 'when_dt',
61141                                 'type': 'string'
61142                             },
61143                             {
61144                                 'name': 'end_dt',
61145                                 'type': 'string'
61146                             },
61147                             {
61148                                 'name': 'parent_id',
61149                                 'type': 'int'
61150                             },
61151                             {
61152                                 'name': 'product_id',
61153                                 'type': 'int'
61154                             },
61155                             {
61156                                 'name': 'productitem_id',
61157                                 'type': 'int'
61158                             },
61159                             {
61160                                 'name': 'guid',
61161                                 'type': 'int'
61162                             }
61163                         ]
61164                     }
61165                 },
61166                 toolbar : {
61167                     xtype: 'Toolbar',
61168                     xns: Roo,
61169                     items : [
61170                         {
61171                             xtype: 'Button',
61172                             xns: Roo.Toolbar,
61173                             listeners : {
61174                                 click : function (_self, e)
61175                                 {
61176                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
61177                                     sd.setMonth(sd.getMonth()-1);
61178                                     _this.monthField.setValue(sd.format('Y-m-d'));
61179                                     _this.grid.ds.load({});
61180                                 }
61181                             },
61182                             text : "Back"
61183                         },
61184                         {
61185                             xtype: 'Separator',
61186                             xns: Roo.Toolbar
61187                         },
61188                         {
61189                             xtype: 'MonthField',
61190                             xns: Roo.form,
61191                             listeners : {
61192                                 render : function (_self)
61193                                 {
61194                                     _this.monthField = _self;
61195                                    // _this.monthField.set  today
61196                                 },
61197                                 select : function (combo, date)
61198                                 {
61199                                     _this.grid.ds.load({});
61200                                 }
61201                             },
61202                             value : (function() { return new Date(); })()
61203                         },
61204                         {
61205                             xtype: 'Separator',
61206                             xns: Roo.Toolbar
61207                         },
61208                         {
61209                             xtype: 'TextItem',
61210                             xns: Roo.Toolbar,
61211                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
61212                         },
61213                         {
61214                             xtype: 'Fill',
61215                             xns: Roo.Toolbar
61216                         },
61217                         {
61218                             xtype: 'Button',
61219                             xns: Roo.Toolbar,
61220                             listeners : {
61221                                 click : function (_self, e)
61222                                 {
61223                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
61224                                     sd.setMonth(sd.getMonth()+1);
61225                                     _this.monthField.setValue(sd.format('Y-m-d'));
61226                                     _this.grid.ds.load({});
61227                                 }
61228                             },
61229                             text : "Next"
61230                         }
61231                     ]
61232                 },
61233                  
61234             }
61235         };
61236         
61237         *//*
61238  * Based on:
61239  * Ext JS Library 1.1.1
61240  * Copyright(c) 2006-2007, Ext JS, LLC.
61241  *
61242  * Originally Released Under LGPL - original licence link has changed is not relivant.
61243  *
61244  * Fork - LGPL
61245  * <script type="text/javascript">
61246  */
61247  
61248 /**
61249  * @class Roo.LoadMask
61250  * A simple utility class for generically masking elements while loading data.  If the element being masked has
61251  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
61252  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
61253  * element's UpdateManager load indicator and will be destroyed after the initial load.
61254  * @constructor
61255  * Create a new LoadMask
61256  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
61257  * @param {Object} config The config object
61258  */
61259 Roo.LoadMask = function(el, config){
61260     this.el = Roo.get(el);
61261     Roo.apply(this, config);
61262     if(this.store){
61263         this.store.on('beforeload', this.onBeforeLoad, this);
61264         this.store.on('load', this.onLoad, this);
61265         this.store.on('loadexception', this.onLoadException, this);
61266         this.removeMask = false;
61267     }else{
61268         var um = this.el.getUpdateManager();
61269         um.showLoadIndicator = false; // disable the default indicator
61270         um.on('beforeupdate', this.onBeforeLoad, this);
61271         um.on('update', this.onLoad, this);
61272         um.on('failure', this.onLoad, this);
61273         this.removeMask = true;
61274     }
61275 };
61276
61277 Roo.LoadMask.prototype = {
61278     /**
61279      * @cfg {Boolean} removeMask
61280      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
61281      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
61282      */
61283     /**
61284      * @cfg {String} msg
61285      * The text to display in a centered loading message box (defaults to 'Loading...')
61286      */
61287     msg : 'Loading...',
61288     /**
61289      * @cfg {String} msgCls
61290      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
61291      */
61292     msgCls : 'x-mask-loading',
61293
61294     /**
61295      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
61296      * @type Boolean
61297      */
61298     disabled: false,
61299
61300     /**
61301      * Disables the mask to prevent it from being displayed
61302      */
61303     disable : function(){
61304        this.disabled = true;
61305     },
61306
61307     /**
61308      * Enables the mask so that it can be displayed
61309      */
61310     enable : function(){
61311         this.disabled = false;
61312     },
61313     
61314     onLoadException : function()
61315     {
61316         Roo.log(arguments);
61317         
61318         if (typeof(arguments[3]) != 'undefined') {
61319             Roo.MessageBox.alert("Error loading",arguments[3]);
61320         } 
61321         /*
61322         try {
61323             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
61324                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
61325             }   
61326         } catch(e) {
61327             
61328         }
61329         */
61330     
61331         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
61332     },
61333     // private
61334     onLoad : function()
61335     {
61336         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
61337     },
61338
61339     // private
61340     onBeforeLoad : function(){
61341         if(!this.disabled){
61342             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
61343         }
61344     },
61345
61346     // private
61347     destroy : function(){
61348         if(this.store){
61349             this.store.un('beforeload', this.onBeforeLoad, this);
61350             this.store.un('load', this.onLoad, this);
61351             this.store.un('loadexception', this.onLoadException, this);
61352         }else{
61353             var um = this.el.getUpdateManager();
61354             um.un('beforeupdate', this.onBeforeLoad, this);
61355             um.un('update', this.onLoad, this);
61356             um.un('failure', this.onLoad, this);
61357         }
61358     }
61359 };/*
61360  * Based on:
61361  * Ext JS Library 1.1.1
61362  * Copyright(c) 2006-2007, Ext JS, LLC.
61363  *
61364  * Originally Released Under LGPL - original licence link has changed is not relivant.
61365  *
61366  * Fork - LGPL
61367  * <script type="text/javascript">
61368  */
61369
61370
61371 /**
61372  * @class Roo.XTemplate
61373  * @extends Roo.Template
61374  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
61375 <pre><code>
61376 var t = new Roo.XTemplate(
61377         '&lt;select name="{name}"&gt;',
61378                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
61379         '&lt;/select&gt;'
61380 );
61381  
61382 // then append, applying the master template values
61383  </code></pre>
61384  *
61385  * Supported features:
61386  *
61387  *  Tags:
61388
61389 <pre><code>
61390       {a_variable} - output encoded.
61391       {a_variable.format:("Y-m-d")} - call a method on the variable
61392       {a_variable:raw} - unencoded output
61393       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
61394       {a_variable:this.method_on_template(...)} - call a method on the template object.
61395  
61396 </code></pre>
61397  *  The tpl tag:
61398 <pre><code>
61399         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
61400         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
61401         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
61402         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
61403   
61404         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
61405         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
61406 </code></pre>
61407  *      
61408  */
61409 Roo.XTemplate = function()
61410 {
61411     Roo.XTemplate.superclass.constructor.apply(this, arguments);
61412     if (this.html) {
61413         this.compile();
61414     }
61415 };
61416
61417
61418 Roo.extend(Roo.XTemplate, Roo.Template, {
61419
61420     /**
61421      * The various sub templates
61422      */
61423     tpls : false,
61424     /**
61425      *
61426      * basic tag replacing syntax
61427      * WORD:WORD()
61428      *
61429      * // you can fake an object call by doing this
61430      *  x.t:(test,tesT) 
61431      * 
61432      */
61433     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
61434
61435     /**
61436      * compile the template
61437      *
61438      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
61439      *
61440      */
61441     compile: function()
61442     {
61443         var s = this.html;
61444      
61445         s = ['<tpl>', s, '</tpl>'].join('');
61446     
61447         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
61448             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
61449             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
61450             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
61451             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
61452             m,
61453             id     = 0,
61454             tpls   = [];
61455     
61456         while(true == !!(m = s.match(re))){
61457             var forMatch   = m[0].match(nameRe),
61458                 ifMatch   = m[0].match(ifRe),
61459                 execMatch   = m[0].match(execRe),
61460                 namedMatch   = m[0].match(namedRe),
61461                 
61462                 exp  = null, 
61463                 fn   = null,
61464                 exec = null,
61465                 name = forMatch && forMatch[1] ? forMatch[1] : '';
61466                 
61467             if (ifMatch) {
61468                 // if - puts fn into test..
61469                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
61470                 if(exp){
61471                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
61472                 }
61473             }
61474             
61475             if (execMatch) {
61476                 // exec - calls a function... returns empty if true is  returned.
61477                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
61478                 if(exp){
61479                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
61480                 }
61481             }
61482             
61483             
61484             if (name) {
61485                 // for = 
61486                 switch(name){
61487                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
61488                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
61489                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
61490                 }
61491             }
61492             var uid = namedMatch ? namedMatch[1] : id;
61493             
61494             
61495             tpls.push({
61496                 id:     namedMatch ? namedMatch[1] : id,
61497                 target: name,
61498                 exec:   exec,
61499                 test:   fn,
61500                 body:   m[1] || ''
61501             });
61502             if (namedMatch) {
61503                 s = s.replace(m[0], '');
61504             } else { 
61505                 s = s.replace(m[0], '{xtpl'+ id + '}');
61506             }
61507             ++id;
61508         }
61509         this.tpls = [];
61510         for(var i = tpls.length-1; i >= 0; --i){
61511             this.compileTpl(tpls[i]);
61512             this.tpls[tpls[i].id] = tpls[i];
61513         }
61514         this.master = tpls[tpls.length-1];
61515         return this;
61516     },
61517     /**
61518      * same as applyTemplate, except it's done to one of the subTemplates
61519      * when using named templates, you can do:
61520      *
61521      * var str = pl.applySubTemplate('your-name', values);
61522      *
61523      * 
61524      * @param {Number} id of the template
61525      * @param {Object} values to apply to template
61526      * @param {Object} parent (normaly the instance of this object)
61527      */
61528     applySubTemplate : function(id, values, parent)
61529     {
61530         
61531         
61532         var t = this.tpls[id];
61533         
61534         
61535         try { 
61536             if(t.test && !t.test.call(this, values, parent)){
61537                 return '';
61538             }
61539         } catch(e) {
61540             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
61541             Roo.log(e.toString());
61542             Roo.log(t.test);
61543             return ''
61544         }
61545         try { 
61546             
61547             if(t.exec && t.exec.call(this, values, parent)){
61548                 return '';
61549             }
61550         } catch(e) {
61551             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
61552             Roo.log(e.toString());
61553             Roo.log(t.exec);
61554             return ''
61555         }
61556         try {
61557             var vs = t.target ? t.target.call(this, values, parent) : values;
61558             parent = t.target ? values : parent;
61559             if(t.target && vs instanceof Array){
61560                 var buf = [];
61561                 for(var i = 0, len = vs.length; i < len; i++){
61562                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
61563                 }
61564                 return buf.join('');
61565             }
61566             return t.compiled.call(this, vs, parent);
61567         } catch (e) {
61568             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
61569             Roo.log(e.toString());
61570             Roo.log(t.compiled);
61571             return '';
61572         }
61573     },
61574
61575     compileTpl : function(tpl)
61576     {
61577         var fm = Roo.util.Format;
61578         var useF = this.disableFormats !== true;
61579         var sep = Roo.isGecko ? "+" : ",";
61580         var undef = function(str) {
61581             Roo.log("Property not found :"  + str);
61582             return '';
61583         };
61584         
61585         var fn = function(m, name, format, args)
61586         {
61587             //Roo.log(arguments);
61588             args = args ? args.replace(/\\'/g,"'") : args;
61589             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
61590             if (typeof(format) == 'undefined') {
61591                 format= 'htmlEncode';
61592             }
61593             if (format == 'raw' ) {
61594                 format = false;
61595             }
61596             
61597             if(name.substr(0, 4) == 'xtpl'){
61598                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
61599             }
61600             
61601             // build an array of options to determine if value is undefined..
61602             
61603             // basically get 'xxxx.yyyy' then do
61604             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
61605             //    (function () { Roo.log("Property not found"); return ''; })() :
61606             //    ......
61607             
61608             var udef_ar = [];
61609             var lookfor = '';
61610             Roo.each(name.split('.'), function(st) {
61611                 lookfor += (lookfor.length ? '.': '') + st;
61612                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
61613             });
61614             
61615             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
61616             
61617             
61618             if(format && useF){
61619                 
61620                 args = args ? ',' + args : "";
61621                  
61622                 if(format.substr(0, 5) != "this."){
61623                     format = "fm." + format + '(';
61624                 }else{
61625                     format = 'this.call("'+ format.substr(5) + '", ';
61626                     args = ", values";
61627                 }
61628                 
61629                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
61630             }
61631              
61632             if (args.length) {
61633                 // called with xxyx.yuu:(test,test)
61634                 // change to ()
61635                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
61636             }
61637             // raw.. - :raw modifier..
61638             return "'"+ sep + udef_st  + name + ")"+sep+"'";
61639             
61640         };
61641         var body;
61642         // branched to use + in gecko and [].join() in others
61643         if(Roo.isGecko){
61644             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
61645                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
61646                     "';};};";
61647         }else{
61648             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
61649             body.push(tpl.body.replace(/(\r\n|\n)/g,
61650                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
61651             body.push("'].join('');};};");
61652             body = body.join('');
61653         }
61654         
61655         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
61656        
61657         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
61658         eval(body);
61659         
61660         return this;
61661     },
61662
61663     applyTemplate : function(values){
61664         return this.master.compiled.call(this, values, {});
61665         //var s = this.subs;
61666     },
61667
61668     apply : function(){
61669         return this.applyTemplate.apply(this, arguments);
61670     }
61671
61672  });
61673
61674 Roo.XTemplate.from = function(el){
61675     el = Roo.getDom(el);
61676     return new Roo.XTemplate(el.value || el.innerHTML);
61677 };