2b68f2b01be3f06b73446670323891c616b47edf
[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 });
1021
1022
1023  
1024 /*
1025  * Based on:
1026  * Ext JS Library 1.1.1
1027  * Copyright(c) 2006-2007, Ext JS, LLC.
1028  *
1029  * Originally Released Under LGPL - original licence link has changed is not relivant.
1030  *
1031  * Fork - LGPL
1032  * <script type="text/javascript">
1033  */
1034
1035 /**
1036  * @class Date
1037  *
1038  * The date parsing and format syntax is a subset of
1039  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
1040  * supported will provide results equivalent to their PHP versions.
1041  *
1042  * Following is the list of all currently supported formats:
1043  *<pre>
1044 Sample date:
1045 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1046
1047 Format  Output      Description
1048 ------  ----------  --------------------------------------------------------------
1049   d      10         Day of the month, 2 digits with leading zeros
1050   D      Wed        A textual representation of a day, three letters
1051   j      10         Day of the month without leading zeros
1052   l      Wednesday  A full textual representation of the day of the week
1053   S      th         English ordinal day of month suffix, 2 chars (use with j)
1054   w      3          Numeric representation of the day of the week
1055   z      9          The julian date, or day of the year (0-365)
1056   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1057   F      January    A full textual representation of the month
1058   m      01         Numeric representation of a month, with leading zeros
1059   M      Jan        Month name abbreviation, three letters
1060   n      1          Numeric representation of a month, without leading zeros
1061   t      31         Number of days in the given month
1062   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1063   Y      2007       A full numeric representation of a year, 4 digits
1064   y      07         A two digit representation of a year
1065   a      pm         Lowercase Ante meridiem and Post meridiem
1066   A      PM         Uppercase Ante meridiem and Post meridiem
1067   g      3          12-hour format of an hour without leading zeros
1068   G      15         24-hour format of an hour without leading zeros
1069   h      03         12-hour format of an hour with leading zeros
1070   H      15         24-hour format of an hour with leading zeros
1071   i      05         Minutes with leading zeros
1072   s      01         Seconds, with leading zeros
1073   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1074   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1075   T      CST        Timezone setting of the machine running the code
1076   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1077 </pre>
1078  *
1079  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1080  * <pre><code>
1081 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1082 document.write(dt.format('Y-m-d'));                         //2007-01-10
1083 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1084 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
1085  </code></pre>
1086  *
1087  * Here are some standard date/time patterns that you might find helpful.  They
1088  * are not part of the source of Date.js, but to use them you can simply copy this
1089  * block of code into any script that is included after Date.js and they will also become
1090  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1091  * <pre><code>
1092 Date.patterns = {
1093     ISO8601Long:"Y-m-d H:i:s",
1094     ISO8601Short:"Y-m-d",
1095     ShortDate: "n/j/Y",
1096     LongDate: "l, F d, Y",
1097     FullDateTime: "l, F d, Y g:i:s A",
1098     MonthDay: "F d",
1099     ShortTime: "g:i A",
1100     LongTime: "g:i:s A",
1101     SortableDateTime: "Y-m-d\\TH:i:s",
1102     UniversalSortableDateTime: "Y-m-d H:i:sO",
1103     YearMonth: "F, Y"
1104 };
1105 </code></pre>
1106  *
1107  * Example usage:
1108  * <pre><code>
1109 var dt = new Date();
1110 document.write(dt.format(Date.patterns.ShortDate));
1111  </code></pre>
1112  */
1113
1114 /*
1115  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1116  * They generate precompiled functions from date formats instead of parsing and
1117  * processing the pattern every time you format a date.  These functions are available
1118  * on every Date object (any javascript function).
1119  *
1120  * The original article and download are here:
1121  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1122  *
1123  */
1124  
1125  
1126  // was in core
1127 /**
1128  Returns the number of milliseconds between this date and date
1129  @param {Date} date (optional) Defaults to now
1130  @return {Number} The diff in milliseconds
1131  @member Date getElapsed
1132  */
1133 Date.prototype.getElapsed = function(date) {
1134         return Math.abs((date || new Date()).getTime()-this.getTime());
1135 };
1136 // was in date file..
1137
1138
1139 // private
1140 Date.parseFunctions = {count:0};
1141 // private
1142 Date.parseRegexes = [];
1143 // private
1144 Date.formatFunctions = {count:0};
1145
1146 // private
1147 Date.prototype.dateFormat = function(format) {
1148     if (Date.formatFunctions[format] == null) {
1149         Date.createNewFormat(format);
1150     }
1151     var func = Date.formatFunctions[format];
1152     return this[func]();
1153 };
1154
1155
1156 /**
1157  * Formats a date given the supplied format string
1158  * @param {String} format The format string
1159  * @return {String} The formatted date
1160  * @method
1161  */
1162 Date.prototype.format = Date.prototype.dateFormat;
1163
1164 // private
1165 Date.createNewFormat = function(format) {
1166     var funcName = "format" + Date.formatFunctions.count++;
1167     Date.formatFunctions[format] = funcName;
1168     var code = "Date.prototype." + funcName + " = function(){return ";
1169     var special = false;
1170     var ch = '';
1171     for (var i = 0; i < format.length; ++i) {
1172         ch = format.charAt(i);
1173         if (!special && ch == "\\") {
1174             special = true;
1175         }
1176         else if (special) {
1177             special = false;
1178             code += "'" + String.escape(ch) + "' + ";
1179         }
1180         else {
1181             code += Date.getFormatCode(ch);
1182         }
1183     }
1184     /** eval:var:zzzzzzzzzzzzz */
1185     eval(code.substring(0, code.length - 3) + ";}");
1186 };
1187
1188 // private
1189 Date.getFormatCode = function(character) {
1190     switch (character) {
1191     case "d":
1192         return "String.leftPad(this.getDate(), 2, '0') + ";
1193     case "D":
1194         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1195     case "j":
1196         return "this.getDate() + ";
1197     case "l":
1198         return "Date.dayNames[this.getDay()] + ";
1199     case "S":
1200         return "this.getSuffix() + ";
1201     case "w":
1202         return "this.getDay() + ";
1203     case "z":
1204         return "this.getDayOfYear() + ";
1205     case "W":
1206         return "this.getWeekOfYear() + ";
1207     case "F":
1208         return "Date.monthNames[this.getMonth()] + ";
1209     case "m":
1210         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1211     case "M":
1212         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1213     case "n":
1214         return "(this.getMonth() + 1) + ";
1215     case "t":
1216         return "this.getDaysInMonth() + ";
1217     case "L":
1218         return "(this.isLeapYear() ? 1 : 0) + ";
1219     case "Y":
1220         return "this.getFullYear() + ";
1221     case "y":
1222         return "('' + this.getFullYear()).substring(2, 4) + ";
1223     case "a":
1224         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1225     case "A":
1226         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1227     case "g":
1228         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1229     case "G":
1230         return "this.getHours() + ";
1231     case "h":
1232         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1233     case "H":
1234         return "String.leftPad(this.getHours(), 2, '0') + ";
1235     case "i":
1236         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1237     case "s":
1238         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1239     case "O":
1240         return "this.getGMTOffset() + ";
1241     case "P":
1242         return "this.getGMTColonOffset() + ";
1243     case "T":
1244         return "this.getTimezone() + ";
1245     case "Z":
1246         return "(this.getTimezoneOffset() * -60) + ";
1247     default:
1248         return "'" + String.escape(character) + "' + ";
1249     }
1250 };
1251
1252 /**
1253  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1254  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1255  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1256  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1257  * string or the parse operation will fail.
1258  * Example Usage:
1259 <pre><code>
1260 //dt = Fri May 25 2007 (current date)
1261 var dt = new Date();
1262
1263 //dt = Thu May 25 2006 (today's month/day in 2006)
1264 dt = Date.parseDate("2006", "Y");
1265
1266 //dt = Sun Jan 15 2006 (all date parts specified)
1267 dt = Date.parseDate("2006-1-15", "Y-m-d");
1268
1269 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1270 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1271 </code></pre>
1272  * @param {String} input The unparsed date as a string
1273  * @param {String} format The format the date is in
1274  * @return {Date} The parsed date
1275  * @static
1276  */
1277 Date.parseDate = function(input, format) {
1278     if (Date.parseFunctions[format] == null) {
1279         Date.createParser(format);
1280     }
1281     var func = Date.parseFunctions[format];
1282     return Date[func](input);
1283 };
1284 /**
1285  * @private
1286  */
1287
1288 Date.createParser = function(format) {
1289     var funcName = "parse" + Date.parseFunctions.count++;
1290     var regexNum = Date.parseRegexes.length;
1291     var currentGroup = 1;
1292     Date.parseFunctions[format] = funcName;
1293
1294     var code = "Date." + funcName + " = function(input){\n"
1295         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1296         + "var d = new Date();\n"
1297         + "y = d.getFullYear();\n"
1298         + "m = d.getMonth();\n"
1299         + "d = d.getDate();\n"
1300         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1301         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1302         + "if (results && results.length > 0) {";
1303     var regex = "";
1304
1305     var special = false;
1306     var ch = '';
1307     for (var i = 0; i < format.length; ++i) {
1308         ch = format.charAt(i);
1309         if (!special && ch == "\\") {
1310             special = true;
1311         }
1312         else if (special) {
1313             special = false;
1314             regex += String.escape(ch);
1315         }
1316         else {
1317             var obj = Date.formatCodeToRegex(ch, currentGroup);
1318             currentGroup += obj.g;
1319             regex += obj.s;
1320             if (obj.g && obj.c) {
1321                 code += obj.c;
1322             }
1323         }
1324     }
1325
1326     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1327         + "{v = new Date(y, m, d, h, i, s);}\n"
1328         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1329         + "{v = new Date(y, m, d, h, i);}\n"
1330         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1331         + "{v = new Date(y, m, d, h);}\n"
1332         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1333         + "{v = new Date(y, m, d);}\n"
1334         + "else if (y >= 0 && m >= 0)\n"
1335         + "{v = new Date(y, m);}\n"
1336         + "else if (y >= 0)\n"
1337         + "{v = new Date(y);}\n"
1338         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1339         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1340         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1341         + ";}";
1342
1343     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1344     /** eval:var:zzzzzzzzzzzzz */
1345     eval(code);
1346 };
1347
1348 // private
1349 Date.formatCodeToRegex = function(character, currentGroup) {
1350     switch (character) {
1351     case "D":
1352         return {g:0,
1353         c:null,
1354         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1355     case "j":
1356         return {g:1,
1357             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1358             s:"(\\d{1,2})"}; // day of month without leading zeroes
1359     case "d":
1360         return {g:1,
1361             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1362             s:"(\\d{2})"}; // day of month with leading zeroes
1363     case "l":
1364         return {g:0,
1365             c:null,
1366             s:"(?:" + Date.dayNames.join("|") + ")"};
1367     case "S":
1368         return {g:0,
1369             c:null,
1370             s:"(?:st|nd|rd|th)"};
1371     case "w":
1372         return {g:0,
1373             c:null,
1374             s:"\\d"};
1375     case "z":
1376         return {g:0,
1377             c:null,
1378             s:"(?:\\d{1,3})"};
1379     case "W":
1380         return {g:0,
1381             c:null,
1382             s:"(?:\\d{2})"};
1383     case "F":
1384         return {g:1,
1385             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1386             s:"(" + Date.monthNames.join("|") + ")"};
1387     case "M":
1388         return {g:1,
1389             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1390             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1391     case "n":
1392         return {g:1,
1393             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1394             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1395     case "m":
1396         return {g:1,
1397             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1398             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1399     case "t":
1400         return {g:0,
1401             c:null,
1402             s:"\\d{1,2}"};
1403     case "L":
1404         return {g:0,
1405             c:null,
1406             s:"(?:1|0)"};
1407     case "Y":
1408         return {g:1,
1409             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1410             s:"(\\d{4})"};
1411     case "y":
1412         return {g:1,
1413             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1414                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1415             s:"(\\d{1,2})"};
1416     case "a":
1417         return {g:1,
1418             c:"if (results[" + currentGroup + "] == 'am') {\n"
1419                 + "if (h == 12) { h = 0; }\n"
1420                 + "} else { if (h < 12) { h += 12; }}",
1421             s:"(am|pm)"};
1422     case "A":
1423         return {g:1,
1424             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1425                 + "if (h == 12) { h = 0; }\n"
1426                 + "} else { if (h < 12) { h += 12; }}",
1427             s:"(AM|PM)"};
1428     case "g":
1429     case "G":
1430         return {g:1,
1431             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1432             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1433     case "h":
1434     case "H":
1435         return {g:1,
1436             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1437             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1438     case "i":
1439         return {g:1,
1440             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1441             s:"(\\d{2})"};
1442     case "s":
1443         return {g:1,
1444             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1445             s:"(\\d{2})"};
1446     case "O":
1447         return {g:1,
1448             c:[
1449                 "o = results[", currentGroup, "];\n",
1450                 "var sn = o.substring(0,1);\n", // get + / - sign
1451                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1452                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1453                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1454                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1455             ].join(""),
1456             s:"([+\-]\\d{2,4})"};
1457     
1458     
1459     case "P":
1460         return {g:1,
1461                 c:[
1462                    "o = results[", currentGroup, "];\n",
1463                    "var sn = o.substring(0,1);\n",
1464                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1465                    "var mn = o.substring(4,6) % 60;\n",
1466                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1467                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1468             ].join(""),
1469             s:"([+\-]\\d{4})"};
1470     case "T":
1471         return {g:0,
1472             c:null,
1473             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1474     case "Z":
1475         return {g:1,
1476             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1477                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1478             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1479     default:
1480         return {g:0,
1481             c:null,
1482             s:String.escape(character)};
1483     }
1484 };
1485
1486 /**
1487  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1488  * @return {String} The abbreviated timezone name (e.g. 'CST')
1489  */
1490 Date.prototype.getTimezone = function() {
1491     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1492 };
1493
1494 /**
1495  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1496  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1497  */
1498 Date.prototype.getGMTOffset = function() {
1499     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1500         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1501         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1502 };
1503
1504 /**
1505  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1506  * @return {String} 2-characters representing hours and 2-characters representing minutes
1507  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1508  */
1509 Date.prototype.getGMTColonOffset = function() {
1510         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1511                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1512                 + ":"
1513                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1514 }
1515
1516 /**
1517  * Get the numeric day number of the year, adjusted for leap year.
1518  * @return {Number} 0 through 364 (365 in leap years)
1519  */
1520 Date.prototype.getDayOfYear = function() {
1521     var num = 0;
1522     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1523     for (var i = 0; i < this.getMonth(); ++i) {
1524         num += Date.daysInMonth[i];
1525     }
1526     return num + this.getDate() - 1;
1527 };
1528
1529 /**
1530  * Get the string representation of the numeric week number of the year
1531  * (equivalent to the format specifier 'W').
1532  * @return {String} '00' through '52'
1533  */
1534 Date.prototype.getWeekOfYear = function() {
1535     // Skip to Thursday of this week
1536     var now = this.getDayOfYear() + (4 - this.getDay());
1537     // Find the first Thursday of the year
1538     var jan1 = new Date(this.getFullYear(), 0, 1);
1539     var then = (7 - jan1.getDay() + 4);
1540     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1541 };
1542
1543 /**
1544  * Whether or not the current date is in a leap year.
1545  * @return {Boolean} True if the current date is in a leap year, else false
1546  */
1547 Date.prototype.isLeapYear = function() {
1548     var year = this.getFullYear();
1549     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1550 };
1551
1552 /**
1553  * Get the first day of the current month, adjusted for leap year.  The returned value
1554  * is the numeric day index within the week (0-6) which can be used in conjunction with
1555  * the {@link #monthNames} array to retrieve the textual day name.
1556  * Example:
1557  *<pre><code>
1558 var dt = new Date('1/10/2007');
1559 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1560 </code></pre>
1561  * @return {Number} The day number (0-6)
1562  */
1563 Date.prototype.getFirstDayOfMonth = function() {
1564     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1565     return (day < 0) ? (day + 7) : day;
1566 };
1567
1568 /**
1569  * Get the last day of the current month, adjusted for leap year.  The returned value
1570  * is the numeric day index within the week (0-6) which can be used in conjunction with
1571  * the {@link #monthNames} array to retrieve the textual day name.
1572  * Example:
1573  *<pre><code>
1574 var dt = new Date('1/10/2007');
1575 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1576 </code></pre>
1577  * @return {Number} The day number (0-6)
1578  */
1579 Date.prototype.getLastDayOfMonth = function() {
1580     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1581     return (day < 0) ? (day + 7) : day;
1582 };
1583
1584
1585 /**
1586  * Get the first date of this date's month
1587  * @return {Date}
1588  */
1589 Date.prototype.getFirstDateOfMonth = function() {
1590     return new Date(this.getFullYear(), this.getMonth(), 1);
1591 };
1592
1593 /**
1594  * Get the last date of this date's month
1595  * @return {Date}
1596  */
1597 Date.prototype.getLastDateOfMonth = function() {
1598     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1599 };
1600 /**
1601  * Get the number of days in the current month, adjusted for leap year.
1602  * @return {Number} The number of days in the month
1603  */
1604 Date.prototype.getDaysInMonth = function() {
1605     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1606     return Date.daysInMonth[this.getMonth()];
1607 };
1608
1609 /**
1610  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1611  * @return {String} 'st, 'nd', 'rd' or 'th'
1612  */
1613 Date.prototype.getSuffix = function() {
1614     switch (this.getDate()) {
1615         case 1:
1616         case 21:
1617         case 31:
1618             return "st";
1619         case 2:
1620         case 22:
1621             return "nd";
1622         case 3:
1623         case 23:
1624             return "rd";
1625         default:
1626             return "th";
1627     }
1628 };
1629
1630 // private
1631 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1632
1633 /**
1634  * An array of textual month names.
1635  * Override these values for international dates, for example...
1636  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1637  * @type Array
1638  * @static
1639  */
1640 Date.monthNames =
1641    ["January",
1642     "February",
1643     "March",
1644     "April",
1645     "May",
1646     "June",
1647     "July",
1648     "August",
1649     "September",
1650     "October",
1651     "November",
1652     "December"];
1653
1654 /**
1655  * An array of textual day names.
1656  * Override these values for international dates, for example...
1657  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1658  * @type Array
1659  * @static
1660  */
1661 Date.dayNames =
1662    ["Sunday",
1663     "Monday",
1664     "Tuesday",
1665     "Wednesday",
1666     "Thursday",
1667     "Friday",
1668     "Saturday"];
1669
1670 // private
1671 Date.y2kYear = 50;
1672 // private
1673 Date.monthNumbers = {
1674     Jan:0,
1675     Feb:1,
1676     Mar:2,
1677     Apr:3,
1678     May:4,
1679     Jun:5,
1680     Jul:6,
1681     Aug:7,
1682     Sep:8,
1683     Oct:9,
1684     Nov:10,
1685     Dec:11};
1686
1687 /**
1688  * Creates and returns a new Date instance with the exact same date value as the called instance.
1689  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1690  * variable will also be changed.  When the intention is to create a new variable that will not
1691  * modify the original instance, you should create a clone.
1692  *
1693  * Example of correctly cloning a date:
1694  * <pre><code>
1695 //wrong way:
1696 var orig = new Date('10/1/2006');
1697 var copy = orig;
1698 copy.setDate(5);
1699 document.write(orig);  //returns 'Thu Oct 05 2006'!
1700
1701 //correct way:
1702 var orig = new Date('10/1/2006');
1703 var copy = orig.clone();
1704 copy.setDate(5);
1705 document.write(orig);  //returns 'Thu Oct 01 2006'
1706 </code></pre>
1707  * @return {Date} The new Date instance
1708  */
1709 Date.prototype.clone = function() {
1710         return new Date(this.getTime());
1711 };
1712
1713 /**
1714  * Clears any time information from this date
1715  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1716  @return {Date} this or the clone
1717  */
1718 Date.prototype.clearTime = function(clone){
1719     if(clone){
1720         return this.clone().clearTime();
1721     }
1722     this.setHours(0);
1723     this.setMinutes(0);
1724     this.setSeconds(0);
1725     this.setMilliseconds(0);
1726     return this;
1727 };
1728
1729 // private
1730 // safari setMonth is broken -- check that this is only donw once...
1731 if(Roo.isSafari && typeof(Date.brokenSetMonth) == 'undefined'){
1732     Date.brokenSetMonth = Date.prototype.setMonth;
1733         Date.prototype.setMonth = function(num){
1734                 if(num <= -1){
1735                         var n = Math.ceil(-num);
1736                         var back_year = Math.ceil(n/12);
1737                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1738                         this.setFullYear(this.getFullYear() - back_year);
1739                         return Date.brokenSetMonth.call(this, month);
1740                 } else {
1741                         return Date.brokenSetMonth.apply(this, arguments);
1742                 }
1743         };
1744 }
1745
1746 /** Date interval constant 
1747 * @static 
1748 * @type String */
1749 Date.MILLI = "ms";
1750 /** Date interval constant 
1751 * @static 
1752 * @type String */
1753 Date.SECOND = "s";
1754 /** Date interval constant 
1755 * @static 
1756 * @type String */
1757 Date.MINUTE = "mi";
1758 /** Date interval constant 
1759 * @static 
1760 * @type String */
1761 Date.HOUR = "h";
1762 /** Date interval constant 
1763 * @static 
1764 * @type String */
1765 Date.DAY = "d";
1766 /** Date interval constant 
1767 * @static 
1768 * @type String */
1769 Date.MONTH = "mo";
1770 /** Date interval constant 
1771 * @static 
1772 * @type String */
1773 Date.YEAR = "y";
1774
1775 /**
1776  * Provides a convenient method of performing basic date arithmetic.  This method
1777  * does not modify the Date instance being called - it creates and returns
1778  * a new Date instance containing the resulting date value.
1779  *
1780  * Examples:
1781  * <pre><code>
1782 //Basic usage:
1783 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1784 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1785
1786 //Negative values will subtract correctly:
1787 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1788 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1789
1790 //You can even chain several calls together in one line!
1791 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1792 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1793  </code></pre>
1794  *
1795  * @param {String} interval   A valid date interval enum value
1796  * @param {Number} value      The amount to add to the current date
1797  * @return {Date} The new Date instance
1798  */
1799 Date.prototype.add = function(interval, value){
1800   var d = this.clone();
1801   if (!interval || value === 0) { return d; }
1802   switch(interval.toLowerCase()){
1803     case Date.MILLI:
1804       d.setMilliseconds(this.getMilliseconds() + value);
1805       break;
1806     case Date.SECOND:
1807       d.setSeconds(this.getSeconds() + value);
1808       break;
1809     case Date.MINUTE:
1810       d.setMinutes(this.getMinutes() + value);
1811       break;
1812     case Date.HOUR:
1813       d.setHours(this.getHours() + value);
1814       break;
1815     case Date.DAY:
1816       d.setDate(this.getDate() + value);
1817       break;
1818     case Date.MONTH:
1819       var day = this.getDate();
1820       if(day > 28){
1821           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1822       }
1823       d.setDate(day);
1824       d.setMonth(this.getMonth() + value);
1825       break;
1826     case Date.YEAR:
1827       d.setFullYear(this.getFullYear() + value);
1828       break;
1829   }
1830   return d;
1831 };
1832 /*
1833  * Based on:
1834  * Ext JS Library 1.1.1
1835  * Copyright(c) 2006-2007, Ext JS, LLC.
1836  *
1837  * Originally Released Under LGPL - original licence link has changed is not relivant.
1838  *
1839  * Fork - LGPL
1840  * <script type="text/javascript">
1841  */
1842
1843 /**
1844  * @class Roo.lib.Dom
1845  * @static
1846  * 
1847  * Dom utils (from YIU afaik)
1848  * 
1849  **/
1850 Roo.lib.Dom = {
1851     /**
1852      * Get the view width
1853      * @param {Boolean} full True will get the full document, otherwise it's the view width
1854      * @return {Number} The width
1855      */
1856      
1857     getViewWidth : function(full) {
1858         return full ? this.getDocumentWidth() : this.getViewportWidth();
1859     },
1860     /**
1861      * Get the view height
1862      * @param {Boolean} full True will get the full document, otherwise it's the view height
1863      * @return {Number} The height
1864      */
1865     getViewHeight : function(full) {
1866         return full ? this.getDocumentHeight() : this.getViewportHeight();
1867     },
1868
1869     getDocumentHeight: function() {
1870         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1871         return Math.max(scrollHeight, this.getViewportHeight());
1872     },
1873
1874     getDocumentWidth: function() {
1875         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1876         return Math.max(scrollWidth, this.getViewportWidth());
1877     },
1878
1879     getViewportHeight: function() {
1880         var height = self.innerHeight;
1881         var mode = document.compatMode;
1882
1883         if ((mode || Roo.isIE) && !Roo.isOpera) {
1884             height = (mode == "CSS1Compat") ?
1885                      document.documentElement.clientHeight :
1886                      document.body.clientHeight;
1887         }
1888
1889         return height;
1890     },
1891
1892     getViewportWidth: function() {
1893         var width = self.innerWidth;
1894         var mode = document.compatMode;
1895
1896         if (mode || Roo.isIE) {
1897             width = (mode == "CSS1Compat") ?
1898                     document.documentElement.clientWidth :
1899                     document.body.clientWidth;
1900         }
1901         return width;
1902     },
1903
1904     isAncestor : function(p, c) {
1905         p = Roo.getDom(p);
1906         c = Roo.getDom(c);
1907         if (!p || !c) {
1908             return false;
1909         }
1910
1911         if (p.contains && !Roo.isSafari) {
1912             return p.contains(c);
1913         } else if (p.compareDocumentPosition) {
1914             return !!(p.compareDocumentPosition(c) & 16);
1915         } else {
1916             var parent = c.parentNode;
1917             while (parent) {
1918                 if (parent == p) {
1919                     return true;
1920                 }
1921                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1922                     return false;
1923                 }
1924                 parent = parent.parentNode;
1925             }
1926             return false;
1927         }
1928     },
1929
1930     getRegion : function(el) {
1931         return Roo.lib.Region.getRegion(el);
1932     },
1933
1934     getY : function(el) {
1935         return this.getXY(el)[1];
1936     },
1937
1938     getX : function(el) {
1939         return this.getXY(el)[0];
1940     },
1941
1942     getXY : function(el) {
1943         var p, pe, b, scroll, bd = document.body;
1944         el = Roo.getDom(el);
1945         var fly = Roo.lib.AnimBase.fly;
1946         if (el.getBoundingClientRect) {
1947             b = el.getBoundingClientRect();
1948             scroll = fly(document).getScroll();
1949             return [b.left + scroll.left, b.top + scroll.top];
1950         }
1951         var x = 0, y = 0;
1952
1953         p = el;
1954
1955         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1956
1957         while (p) {
1958
1959             x += p.offsetLeft;
1960             y += p.offsetTop;
1961
1962             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1963                 hasAbsolute = true;
1964             }
1965
1966             if (Roo.isGecko) {
1967                 pe = fly(p);
1968
1969                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1970                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1971
1972
1973                 x += bl;
1974                 y += bt;
1975
1976
1977                 if (p != el && pe.getStyle('overflow') != 'visible') {
1978                     x += bl;
1979                     y += bt;
1980                 }
1981             }
1982             p = p.offsetParent;
1983         }
1984
1985         if (Roo.isSafari && hasAbsolute) {
1986             x -= bd.offsetLeft;
1987             y -= bd.offsetTop;
1988         }
1989
1990         if (Roo.isGecko && !hasAbsolute) {
1991             var dbd = fly(bd);
1992             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1993             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1994         }
1995
1996         p = el.parentNode;
1997         while (p && p != bd) {
1998             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1999                 x -= p.scrollLeft;
2000                 y -= p.scrollTop;
2001             }
2002             p = p.parentNode;
2003         }
2004         return [x, y];
2005     },
2006  
2007   
2008
2009
2010     setXY : function(el, xy) {
2011         el = Roo.fly(el, '_setXY');
2012         el.position();
2013         var pts = el.translatePoints(xy);
2014         if (xy[0] !== false) {
2015             el.dom.style.left = pts.left + "px";
2016         }
2017         if (xy[1] !== false) {
2018             el.dom.style.top = pts.top + "px";
2019         }
2020     },
2021
2022     setX : function(el, x) {
2023         this.setXY(el, [x, false]);
2024     },
2025
2026     setY : function(el, y) {
2027         this.setXY(el, [false, y]);
2028     }
2029 };
2030 /*
2031  * Portions of this file are based on pieces of Yahoo User Interface Library
2032  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2033  * YUI licensed under the BSD License:
2034  * http://developer.yahoo.net/yui/license.txt
2035  * <script type="text/javascript">
2036  *
2037  */
2038
2039 Roo.lib.Event = function() {
2040     var loadComplete = false;
2041     var listeners = [];
2042     var unloadListeners = [];
2043     var retryCount = 0;
2044     var onAvailStack = [];
2045     var counter = 0;
2046     var lastError = null;
2047
2048     return {
2049         POLL_RETRYS: 200,
2050         POLL_INTERVAL: 20,
2051         EL: 0,
2052         TYPE: 1,
2053         FN: 2,
2054         WFN: 3,
2055         OBJ: 3,
2056         ADJ_SCOPE: 4,
2057         _interval: null,
2058
2059         startInterval: function() {
2060             if (!this._interval) {
2061                 var self = this;
2062                 var callback = function() {
2063                     self._tryPreloadAttach();
2064                 };
2065                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2066
2067             }
2068         },
2069
2070         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2071             onAvailStack.push({ id:         p_id,
2072                 fn:         p_fn,
2073                 obj:        p_obj,
2074                 override:   p_override,
2075                 checkReady: false    });
2076
2077             retryCount = this.POLL_RETRYS;
2078             this.startInterval();
2079         },
2080
2081
2082         addListener: function(el, eventName, fn) {
2083             el = Roo.getDom(el);
2084             if (!el || !fn) {
2085                 return false;
2086             }
2087
2088             if ("unload" == eventName) {
2089                 unloadListeners[unloadListeners.length] =
2090                 [el, eventName, fn];
2091                 return true;
2092             }
2093
2094             var wrappedFn = function(e) {
2095                 return fn(Roo.lib.Event.getEvent(e));
2096             };
2097
2098             var li = [el, eventName, fn, wrappedFn];
2099
2100             var index = listeners.length;
2101             listeners[index] = li;
2102
2103             this.doAdd(el, eventName, wrappedFn, false);
2104             return true;
2105
2106         },
2107
2108
2109         removeListener: function(el, eventName, fn) {
2110             var i, len;
2111
2112             el = Roo.getDom(el);
2113
2114             if(!fn) {
2115                 return this.purgeElement(el, false, eventName);
2116             }
2117
2118
2119             if ("unload" == eventName) {
2120
2121                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2122                     var li = unloadListeners[i];
2123                     if (li &&
2124                         li[0] == el &&
2125                         li[1] == eventName &&
2126                         li[2] == fn) {
2127                         unloadListeners.splice(i, 1);
2128                         return true;
2129                     }
2130                 }
2131
2132                 return false;
2133             }
2134
2135             var cacheItem = null;
2136
2137
2138             var index = arguments[3];
2139
2140             if ("undefined" == typeof index) {
2141                 index = this._getCacheIndex(el, eventName, fn);
2142             }
2143
2144             if (index >= 0) {
2145                 cacheItem = listeners[index];
2146             }
2147
2148             if (!el || !cacheItem) {
2149                 return false;
2150             }
2151
2152             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2153
2154             delete listeners[index][this.WFN];
2155             delete listeners[index][this.FN];
2156             listeners.splice(index, 1);
2157
2158             return true;
2159
2160         },
2161
2162
2163         getTarget: function(ev, resolveTextNode) {
2164             ev = ev.browserEvent || ev;
2165             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2166             var t = ev.target || ev.srcElement;
2167             return this.resolveTextNode(t);
2168         },
2169
2170
2171         resolveTextNode: function(node) {
2172             if (Roo.isSafari && node && 3 == node.nodeType) {
2173                 return node.parentNode;
2174             } else {
2175                 return node;
2176             }
2177         },
2178
2179
2180         getPageX: function(ev) {
2181             ev = ev.browserEvent || ev;
2182             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2183             var x = ev.pageX;
2184             if (!x && 0 !== x) {
2185                 x = ev.clientX || 0;
2186
2187                 if (Roo.isIE) {
2188                     x += this.getScroll()[1];
2189                 }
2190             }
2191
2192             return x;
2193         },
2194
2195
2196         getPageY: function(ev) {
2197             ev = ev.browserEvent || ev;
2198             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2199             var y = ev.pageY;
2200             if (!y && 0 !== y) {
2201                 y = ev.clientY || 0;
2202
2203                 if (Roo.isIE) {
2204                     y += this.getScroll()[0];
2205                 }
2206             }
2207
2208
2209             return y;
2210         },
2211
2212
2213         getXY: function(ev) {
2214             ev = ev.browserEvent || ev;
2215             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2216             return [this.getPageX(ev), this.getPageY(ev)];
2217         },
2218
2219
2220         getRelatedTarget: function(ev) {
2221             ev = ev.browserEvent || ev;
2222             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2223             var t = ev.relatedTarget;
2224             if (!t) {
2225                 if (ev.type == "mouseout") {
2226                     t = ev.toElement;
2227                 } else if (ev.type == "mouseover") {
2228                     t = ev.fromElement;
2229                 }
2230             }
2231
2232             return this.resolveTextNode(t);
2233         },
2234
2235
2236         getTime: function(ev) {
2237             ev = ev.browserEvent || ev;
2238             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2239             if (!ev.time) {
2240                 var t = new Date().getTime();
2241                 try {
2242                     ev.time = t;
2243                 } catch(ex) {
2244                     this.lastError = ex;
2245                     return t;
2246                 }
2247             }
2248
2249             return ev.time;
2250         },
2251
2252
2253         stopEvent: function(ev) {
2254             this.stopPropagation(ev);
2255             this.preventDefault(ev);
2256         },
2257
2258
2259         stopPropagation: function(ev) {
2260             ev = ev.browserEvent || ev;
2261             if (ev.stopPropagation) {
2262                 ev.stopPropagation();
2263             } else {
2264                 ev.cancelBubble = true;
2265             }
2266         },
2267
2268
2269         preventDefault: function(ev) {
2270             ev = ev.browserEvent || ev;
2271             if(ev.preventDefault) {
2272                 ev.preventDefault();
2273             } else {
2274                 ev.returnValue = false;
2275             }
2276         },
2277
2278
2279         getEvent: function(e) {
2280             var ev = e || window.event;
2281             if (!ev) {
2282                 var c = this.getEvent.caller;
2283                 while (c) {
2284                     ev = c.arguments[0];
2285                     if (ev && Event == ev.constructor) {
2286                         break;
2287                     }
2288                     c = c.caller;
2289                 }
2290             }
2291             return ev;
2292         },
2293
2294
2295         getCharCode: function(ev) {
2296             ev = ev.browserEvent || ev;
2297             return ev.charCode || ev.keyCode || 0;
2298         },
2299
2300
2301         _getCacheIndex: function(el, eventName, fn) {
2302             for (var i = 0,len = listeners.length; i < len; ++i) {
2303                 var li = listeners[i];
2304                 if (li &&
2305                     li[this.FN] == fn &&
2306                     li[this.EL] == el &&
2307                     li[this.TYPE] == eventName) {
2308                     return i;
2309                 }
2310             }
2311
2312             return -1;
2313         },
2314
2315
2316         elCache: {},
2317
2318
2319         getEl: function(id) {
2320             return document.getElementById(id);
2321         },
2322
2323
2324         clearCache: function() {
2325         },
2326
2327
2328         _load: function(e) {
2329             loadComplete = true;
2330             var EU = Roo.lib.Event;
2331
2332
2333             if (Roo.isIE) {
2334                 EU.doRemove(window, "load", EU._load);
2335             }
2336         },
2337
2338
2339         _tryPreloadAttach: function() {
2340
2341             if (this.locked) {
2342                 return false;
2343             }
2344
2345             this.locked = true;
2346
2347
2348             var tryAgain = !loadComplete;
2349             if (!tryAgain) {
2350                 tryAgain = (retryCount > 0);
2351             }
2352
2353
2354             var notAvail = [];
2355             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2356                 var item = onAvailStack[i];
2357                 if (item) {
2358                     var el = this.getEl(item.id);
2359
2360                     if (el) {
2361                         if (!item.checkReady ||
2362                             loadComplete ||
2363                             el.nextSibling ||
2364                             (document && document.body)) {
2365
2366                             var scope = el;
2367                             if (item.override) {
2368                                 if (item.override === true) {
2369                                     scope = item.obj;
2370                                 } else {
2371                                     scope = item.override;
2372                                 }
2373                             }
2374                             item.fn.call(scope, item.obj);
2375                             onAvailStack[i] = null;
2376                         }
2377                     } else {
2378                         notAvail.push(item);
2379                     }
2380                 }
2381             }
2382
2383             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2384
2385             if (tryAgain) {
2386
2387                 this.startInterval();
2388             } else {
2389                 clearInterval(this._interval);
2390                 this._interval = null;
2391             }
2392
2393             this.locked = false;
2394
2395             return true;
2396
2397         },
2398
2399
2400         purgeElement: function(el, recurse, eventName) {
2401             var elListeners = this.getListeners(el, eventName);
2402             if (elListeners) {
2403                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2404                     var l = elListeners[i];
2405                     this.removeListener(el, l.type, l.fn);
2406                 }
2407             }
2408
2409             if (recurse && el && el.childNodes) {
2410                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2411                     this.purgeElement(el.childNodes[i], recurse, eventName);
2412                 }
2413             }
2414         },
2415
2416
2417         getListeners: function(el, eventName) {
2418             var results = [], searchLists;
2419             if (!eventName) {
2420                 searchLists = [listeners, unloadListeners];
2421             } else if (eventName == "unload") {
2422                 searchLists = [unloadListeners];
2423             } else {
2424                 searchLists = [listeners];
2425             }
2426
2427             for (var j = 0; j < searchLists.length; ++j) {
2428                 var searchList = searchLists[j];
2429                 if (searchList && searchList.length > 0) {
2430                     for (var i = 0,len = searchList.length; i < len; ++i) {
2431                         var l = searchList[i];
2432                         if (l && l[this.EL] === el &&
2433                             (!eventName || eventName === l[this.TYPE])) {
2434                             results.push({
2435                                 type:   l[this.TYPE],
2436                                 fn:     l[this.FN],
2437                                 obj:    l[this.OBJ],
2438                                 adjust: l[this.ADJ_SCOPE],
2439                                 index:  i
2440                             });
2441                         }
2442                     }
2443                 }
2444             }
2445
2446             return (results.length) ? results : null;
2447         },
2448
2449
2450         _unload: function(e) {
2451
2452             var EU = Roo.lib.Event, i, j, l, len, index;
2453
2454             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2455                 l = unloadListeners[i];
2456                 if (l) {
2457                     var scope = window;
2458                     if (l[EU.ADJ_SCOPE]) {
2459                         if (l[EU.ADJ_SCOPE] === true) {
2460                             scope = l[EU.OBJ];
2461                         } else {
2462                             scope = l[EU.ADJ_SCOPE];
2463                         }
2464                     }
2465                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2466                     unloadListeners[i] = null;
2467                     l = null;
2468                     scope = null;
2469                 }
2470             }
2471
2472             unloadListeners = null;
2473
2474             if (listeners && listeners.length > 0) {
2475                 j = listeners.length;
2476                 while (j) {
2477                     index = j - 1;
2478                     l = listeners[index];
2479                     if (l) {
2480                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2481                                 l[EU.FN], index);
2482                     }
2483                     j = j - 1;
2484                 }
2485                 l = null;
2486
2487                 EU.clearCache();
2488             }
2489
2490             EU.doRemove(window, "unload", EU._unload);
2491
2492         },
2493
2494
2495         getScroll: function() {
2496             var dd = document.documentElement, db = document.body;
2497             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2498                 return [dd.scrollTop, dd.scrollLeft];
2499             } else if (db) {
2500                 return [db.scrollTop, db.scrollLeft];
2501             } else {
2502                 return [0, 0];
2503             }
2504         },
2505
2506
2507         doAdd: function () {
2508             if (window.addEventListener) {
2509                 return function(el, eventName, fn, capture) {
2510                     el.addEventListener(eventName, fn, (capture));
2511                 };
2512             } else if (window.attachEvent) {
2513                 return function(el, eventName, fn, capture) {
2514                     el.attachEvent("on" + eventName, fn);
2515                 };
2516             } else {
2517                 return function() {
2518                 };
2519             }
2520         }(),
2521
2522
2523         doRemove: function() {
2524             if (window.removeEventListener) {
2525                 return function (el, eventName, fn, capture) {
2526                     el.removeEventListener(eventName, fn, (capture));
2527                 };
2528             } else if (window.detachEvent) {
2529                 return function (el, eventName, fn) {
2530                     el.detachEvent("on" + eventName, fn);
2531                 };
2532             } else {
2533                 return function() {
2534                 };
2535             }
2536         }()
2537     };
2538     
2539 }();
2540 (function() {     
2541    
2542     var E = Roo.lib.Event;
2543     E.on = E.addListener;
2544     E.un = E.removeListener;
2545
2546     if (document && document.body) {
2547         E._load();
2548     } else {
2549         E.doAdd(window, "load", E._load);
2550     }
2551     E.doAdd(window, "unload", E._unload);
2552     E._tryPreloadAttach();
2553 })();
2554
2555 /*
2556  * Portions of this file are based on pieces of Yahoo User Interface Library
2557  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2558  * YUI licensed under the BSD License:
2559  * http://developer.yahoo.net/yui/license.txt
2560  * <script type="text/javascript">
2561  *
2562  */
2563
2564 (function() {
2565     /**
2566      * @class Roo.lib.Ajax
2567      *
2568      */
2569     Roo.lib.Ajax = {
2570         /**
2571          * @static 
2572          */
2573         request : function(method, uri, cb, data, options) {
2574             if(options){
2575                 var hs = options.headers;
2576                 if(hs){
2577                     for(var h in hs){
2578                         if(hs.hasOwnProperty(h)){
2579                             this.initHeader(h, hs[h], false);
2580                         }
2581                     }
2582                 }
2583                 if(options.xmlData){
2584                     this.initHeader('Content-Type', 'text/xml', false);
2585                     method = 'POST';
2586                     data = options.xmlData;
2587                 }
2588             }
2589
2590             return this.asyncRequest(method, uri, cb, data);
2591         },
2592
2593         serializeForm : function(form) {
2594             if(typeof form == 'string') {
2595                 form = (document.getElementById(form) || document.forms[form]);
2596             }
2597
2598             var el, name, val, disabled, data = '', hasSubmit = false;
2599             for (var i = 0; i < form.elements.length; i++) {
2600                 el = form.elements[i];
2601                 disabled = form.elements[i].disabled;
2602                 name = form.elements[i].name;
2603                 val = form.elements[i].value;
2604
2605                 if (!disabled && name){
2606                     switch (el.type)
2607                             {
2608                         case 'select-one':
2609                         case 'select-multiple':
2610                             for (var j = 0; j < el.options.length; j++) {
2611                                 if (el.options[j].selected) {
2612                                     if (Roo.isIE) {
2613                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2614                                     }
2615                                     else {
2616                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2617                                     }
2618                                 }
2619                             }
2620                             break;
2621                         case 'radio':
2622                         case 'checkbox':
2623                             if (el.checked) {
2624                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2625                             }
2626                             break;
2627                         case 'file':
2628
2629                         case undefined:
2630
2631                         case 'reset':
2632
2633                         case 'button':
2634
2635                             break;
2636                         case 'submit':
2637                             if(hasSubmit == false) {
2638                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2639                                 hasSubmit = true;
2640                             }
2641                             break;
2642                         default:
2643                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2644                             break;
2645                     }
2646                 }
2647             }
2648             data = data.substr(0, data.length - 1);
2649             return data;
2650         },
2651
2652         headers:{},
2653
2654         hasHeaders:false,
2655
2656         useDefaultHeader:true,
2657
2658         defaultPostHeader:'application/x-www-form-urlencoded',
2659
2660         useDefaultXhrHeader:true,
2661
2662         defaultXhrHeader:'XMLHttpRequest',
2663
2664         hasDefaultHeaders:true,
2665
2666         defaultHeaders:{},
2667
2668         poll:{},
2669
2670         timeout:{},
2671
2672         pollInterval:50,
2673
2674         transactionId:0,
2675
2676         setProgId:function(id)
2677         {
2678             this.activeX.unshift(id);
2679         },
2680
2681         setDefaultPostHeader:function(b)
2682         {
2683             this.useDefaultHeader = b;
2684         },
2685
2686         setDefaultXhrHeader:function(b)
2687         {
2688             this.useDefaultXhrHeader = b;
2689         },
2690
2691         setPollingInterval:function(i)
2692         {
2693             if (typeof i == 'number' && isFinite(i)) {
2694                 this.pollInterval = i;
2695             }
2696         },
2697
2698         createXhrObject:function(transactionId)
2699         {
2700             var obj,http;
2701             try
2702             {
2703
2704                 http = new XMLHttpRequest();
2705
2706                 obj = { conn:http, tId:transactionId };
2707             }
2708             catch(e)
2709             {
2710                 for (var i = 0; i < this.activeX.length; ++i) {
2711                     try
2712                     {
2713
2714                         http = new ActiveXObject(this.activeX[i]);
2715
2716                         obj = { conn:http, tId:transactionId };
2717                         break;
2718                     }
2719                     catch(e) {
2720                     }
2721                 }
2722             }
2723             finally
2724             {
2725                 return obj;
2726             }
2727         },
2728
2729         getConnectionObject:function()
2730         {
2731             var o;
2732             var tId = this.transactionId;
2733
2734             try
2735             {
2736                 o = this.createXhrObject(tId);
2737                 if (o) {
2738                     this.transactionId++;
2739                 }
2740             }
2741             catch(e) {
2742             }
2743             finally
2744             {
2745                 return o;
2746             }
2747         },
2748
2749         asyncRequest:function(method, uri, callback, postData)
2750         {
2751             var o = this.getConnectionObject();
2752
2753             if (!o) {
2754                 return null;
2755             }
2756             else {
2757                 o.conn.open(method, uri, true);
2758
2759                 if (this.useDefaultXhrHeader) {
2760                     if (!this.defaultHeaders['X-Requested-With']) {
2761                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2762                     }
2763                 }
2764
2765                 if(postData && this.useDefaultHeader){
2766                     this.initHeader('Content-Type', this.defaultPostHeader);
2767                 }
2768
2769                  if (this.hasDefaultHeaders || this.hasHeaders) {
2770                     this.setHeader(o);
2771                 }
2772
2773                 this.handleReadyState(o, callback);
2774                 o.conn.send(postData || null);
2775
2776                 return o;
2777             }
2778         },
2779
2780         handleReadyState:function(o, callback)
2781         {
2782             var oConn = this;
2783
2784             if (callback && callback.timeout) {
2785                 
2786                 this.timeout[o.tId] = window.setTimeout(function() {
2787                     oConn.abort(o, callback, true);
2788                 }, callback.timeout);
2789             }
2790
2791             this.poll[o.tId] = window.setInterval(
2792                     function() {
2793                         if (o.conn && o.conn.readyState == 4) {
2794                             window.clearInterval(oConn.poll[o.tId]);
2795                             delete oConn.poll[o.tId];
2796
2797                             if(callback && callback.timeout) {
2798                                 window.clearTimeout(oConn.timeout[o.tId]);
2799                                 delete oConn.timeout[o.tId];
2800                             }
2801
2802                             oConn.handleTransactionResponse(o, callback);
2803                         }
2804                     }
2805                     , this.pollInterval);
2806         },
2807
2808         handleTransactionResponse:function(o, callback, isAbort)
2809         {
2810
2811             if (!callback) {
2812                 this.releaseObject(o);
2813                 return;
2814             }
2815
2816             var httpStatus, responseObject;
2817
2818             try
2819             {
2820                 if (o.conn.status !== undefined && o.conn.status != 0) {
2821                     httpStatus = o.conn.status;
2822                 }
2823                 else {
2824                     httpStatus = 13030;
2825                 }
2826             }
2827             catch(e) {
2828
2829
2830                 httpStatus = 13030;
2831             }
2832
2833             if (httpStatus >= 200 && httpStatus < 300) {
2834                 responseObject = this.createResponseObject(o, callback.argument);
2835                 if (callback.success) {
2836                     if (!callback.scope) {
2837                         callback.success(responseObject);
2838                     }
2839                     else {
2840
2841
2842                         callback.success.apply(callback.scope, [responseObject]);
2843                     }
2844                 }
2845             }
2846             else {
2847                 switch (httpStatus) {
2848
2849                     case 12002:
2850                     case 12029:
2851                     case 12030:
2852                     case 12031:
2853                     case 12152:
2854                     case 13030:
2855                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2856                         if (callback.failure) {
2857                             if (!callback.scope) {
2858                                 callback.failure(responseObject);
2859                             }
2860                             else {
2861                                 callback.failure.apply(callback.scope, [responseObject]);
2862                             }
2863                         }
2864                         break;
2865                     default:
2866                         responseObject = this.createResponseObject(o, callback.argument);
2867                         if (callback.failure) {
2868                             if (!callback.scope) {
2869                                 callback.failure(responseObject);
2870                             }
2871                             else {
2872                                 callback.failure.apply(callback.scope, [responseObject]);
2873                             }
2874                         }
2875                 }
2876             }
2877
2878             this.releaseObject(o);
2879             responseObject = null;
2880         },
2881
2882         createResponseObject:function(o, callbackArg)
2883         {
2884             var obj = {};
2885             var headerObj = {};
2886
2887             try
2888             {
2889                 var headerStr = o.conn.getAllResponseHeaders();
2890                 var header = headerStr.split('\n');
2891                 for (var i = 0; i < header.length; i++) {
2892                     var delimitPos = header[i].indexOf(':');
2893                     if (delimitPos != -1) {
2894                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2895                     }
2896                 }
2897             }
2898             catch(e) {
2899             }
2900
2901             obj.tId = o.tId;
2902             obj.status = o.conn.status;
2903             obj.statusText = o.conn.statusText;
2904             obj.getResponseHeader = headerObj;
2905             obj.getAllResponseHeaders = headerStr;
2906             obj.responseText = o.conn.responseText;
2907             obj.responseXML = o.conn.responseXML;
2908
2909             if (typeof callbackArg !== undefined) {
2910                 obj.argument = callbackArg;
2911             }
2912
2913             return obj;
2914         },
2915
2916         createExceptionObject:function(tId, callbackArg, isAbort)
2917         {
2918             var COMM_CODE = 0;
2919             var COMM_ERROR = 'communication failure';
2920             var ABORT_CODE = -1;
2921             var ABORT_ERROR = 'transaction aborted';
2922
2923             var obj = {};
2924
2925             obj.tId = tId;
2926             if (isAbort) {
2927                 obj.status = ABORT_CODE;
2928                 obj.statusText = ABORT_ERROR;
2929             }
2930             else {
2931                 obj.status = COMM_CODE;
2932                 obj.statusText = COMM_ERROR;
2933             }
2934
2935             if (callbackArg) {
2936                 obj.argument = callbackArg;
2937             }
2938
2939             return obj;
2940         },
2941
2942         initHeader:function(label, value, isDefault)
2943         {
2944             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2945
2946             if (headerObj[label] === undefined) {
2947                 headerObj[label] = value;
2948             }
2949             else {
2950
2951
2952                 headerObj[label] = value + "," + headerObj[label];
2953             }
2954
2955             if (isDefault) {
2956                 this.hasDefaultHeaders = true;
2957             }
2958             else {
2959                 this.hasHeaders = true;
2960             }
2961         },
2962
2963
2964         setHeader:function(o)
2965         {
2966             if (this.hasDefaultHeaders) {
2967                 for (var prop in this.defaultHeaders) {
2968                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2969                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2970                     }
2971                 }
2972             }
2973
2974             if (this.hasHeaders) {
2975                 for (var prop in this.headers) {
2976                     if (this.headers.hasOwnProperty(prop)) {
2977                         o.conn.setRequestHeader(prop, this.headers[prop]);
2978                     }
2979                 }
2980                 this.headers = {};
2981                 this.hasHeaders = false;
2982             }
2983         },
2984
2985         resetDefaultHeaders:function() {
2986             delete this.defaultHeaders;
2987             this.defaultHeaders = {};
2988             this.hasDefaultHeaders = false;
2989         },
2990
2991         abort:function(o, callback, isTimeout)
2992         {
2993             if(this.isCallInProgress(o)) {
2994                 o.conn.abort();
2995                 window.clearInterval(this.poll[o.tId]);
2996                 delete this.poll[o.tId];
2997                 if (isTimeout) {
2998                     delete this.timeout[o.tId];
2999                 }
3000
3001                 this.handleTransactionResponse(o, callback, true);
3002
3003                 return true;
3004             }
3005             else {
3006                 return false;
3007             }
3008         },
3009
3010
3011         isCallInProgress:function(o)
3012         {
3013             if (o && o.conn) {
3014                 return o.conn.readyState != 4 && o.conn.readyState != 0;
3015             }
3016             else {
3017
3018                 return false;
3019             }
3020         },
3021
3022
3023         releaseObject:function(o)
3024         {
3025
3026             o.conn = null;
3027
3028             o = null;
3029         },
3030
3031         activeX:[
3032         'MSXML2.XMLHTTP.3.0',
3033         'MSXML2.XMLHTTP',
3034         'Microsoft.XMLHTTP'
3035         ]
3036
3037
3038     };
3039 })();/*
3040  * Portions of this file are based on pieces of Yahoo User Interface Library
3041  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3042  * YUI licensed under the BSD License:
3043  * http://developer.yahoo.net/yui/license.txt
3044  * <script type="text/javascript">
3045  *
3046  */
3047
3048 Roo.lib.Region = function(t, r, b, l) {
3049     this.top = t;
3050     this[1] = t;
3051     this.right = r;
3052     this.bottom = b;
3053     this.left = l;
3054     this[0] = l;
3055 };
3056
3057
3058 Roo.lib.Region.prototype = {
3059     contains : function(region) {
3060         return ( region.left >= this.left &&
3061                  region.right <= this.right &&
3062                  region.top >= this.top &&
3063                  region.bottom <= this.bottom    );
3064
3065     },
3066
3067     getArea : function() {
3068         return ( (this.bottom - this.top) * (this.right - this.left) );
3069     },
3070
3071     intersect : function(region) {
3072         var t = Math.max(this.top, region.top);
3073         var r = Math.min(this.right, region.right);
3074         var b = Math.min(this.bottom, region.bottom);
3075         var l = Math.max(this.left, region.left);
3076
3077         if (b >= t && r >= l) {
3078             return new Roo.lib.Region(t, r, b, l);
3079         } else {
3080             return null;
3081         }
3082     },
3083     union : function(region) {
3084         var t = Math.min(this.top, region.top);
3085         var r = Math.max(this.right, region.right);
3086         var b = Math.max(this.bottom, region.bottom);
3087         var l = Math.min(this.left, region.left);
3088
3089         return new Roo.lib.Region(t, r, b, l);
3090     },
3091
3092     adjust : function(t, l, b, r) {
3093         this.top += t;
3094         this.left += l;
3095         this.right += r;
3096         this.bottom += b;
3097         return this;
3098     }
3099 };
3100
3101 Roo.lib.Region.getRegion = function(el) {
3102     var p = Roo.lib.Dom.getXY(el);
3103
3104     var t = p[1];
3105     var r = p[0] + el.offsetWidth;
3106     var b = p[1] + el.offsetHeight;
3107     var l = p[0];
3108
3109     return new Roo.lib.Region(t, r, b, l);
3110 };
3111 /*
3112  * Portions of this file are based on pieces of Yahoo User Interface Library
3113  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3114  * YUI licensed under the BSD License:
3115  * http://developer.yahoo.net/yui/license.txt
3116  * <script type="text/javascript">
3117  *
3118  */
3119 //@@dep Roo.lib.Region
3120
3121
3122 Roo.lib.Point = function(x, y) {
3123     if (x instanceof Array) {
3124         y = x[1];
3125         x = x[0];
3126     }
3127     this.x = this.right = this.left = this[0] = x;
3128     this.y = this.top = this.bottom = this[1] = y;
3129 };
3130
3131 Roo.lib.Point.prototype = new Roo.lib.Region();
3132 /*
3133  * Portions of this file are based on pieces of Yahoo User Interface Library
3134  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3135  * YUI licensed under the BSD License:
3136  * http://developer.yahoo.net/yui/license.txt
3137  * <script type="text/javascript">
3138  *
3139  */
3140  
3141 (function() {   
3142
3143     Roo.lib.Anim = {
3144         scroll : function(el, args, duration, easing, cb, scope) {
3145             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3146         },
3147
3148         motion : function(el, args, duration, easing, cb, scope) {
3149             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3150         },
3151
3152         color : function(el, args, duration, easing, cb, scope) {
3153             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3154         },
3155
3156         run : function(el, args, duration, easing, cb, scope, type) {
3157             type = type || Roo.lib.AnimBase;
3158             if (typeof easing == "string") {
3159                 easing = Roo.lib.Easing[easing];
3160             }
3161             var anim = new type(el, args, duration, easing);
3162             anim.animateX(function() {
3163                 Roo.callback(cb, scope);
3164             });
3165             return anim;
3166         }
3167     };
3168 })();/*
3169  * Portions of this file are based on pieces of Yahoo User Interface Library
3170  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3171  * YUI licensed under the BSD License:
3172  * http://developer.yahoo.net/yui/license.txt
3173  * <script type="text/javascript">
3174  *
3175  */
3176
3177 (function() {    
3178     var libFlyweight;
3179     
3180     function fly(el) {
3181         if (!libFlyweight) {
3182             libFlyweight = new Roo.Element.Flyweight();
3183         }
3184         libFlyweight.dom = el;
3185         return libFlyweight;
3186     }
3187
3188     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3189     
3190    
3191     
3192     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3193         if (el) {
3194             this.init(el, attributes, duration, method);
3195         }
3196     };
3197
3198     Roo.lib.AnimBase.fly = fly;
3199     
3200     
3201     
3202     Roo.lib.AnimBase.prototype = {
3203
3204         toString: function() {
3205             var el = this.getEl();
3206             var id = el.id || el.tagName;
3207             return ("Anim " + id);
3208         },
3209
3210         patterns: {
3211             noNegatives:        /width|height|opacity|padding/i,
3212             offsetAttribute:  /^((width|height)|(top|left))$/,
3213             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3214             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3215         },
3216
3217
3218         doMethod: function(attr, start, end) {
3219             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3220         },
3221
3222
3223         setAttribute: function(attr, val, unit) {
3224             if (this.patterns.noNegatives.test(attr)) {
3225                 val = (val > 0) ? val : 0;
3226             }
3227
3228             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3229         },
3230
3231
3232         getAttribute: function(attr) {
3233             var el = this.getEl();
3234             var val = fly(el).getStyle(attr);
3235
3236             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3237                 return parseFloat(val);
3238             }
3239
3240             var a = this.patterns.offsetAttribute.exec(attr) || [];
3241             var pos = !!( a[3] );
3242             var box = !!( a[2] );
3243
3244
3245             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3246                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3247             } else {
3248                 val = 0;
3249             }
3250
3251             return val;
3252         },
3253
3254
3255         getDefaultUnit: function(attr) {
3256             if (this.patterns.defaultUnit.test(attr)) {
3257                 return 'px';
3258             }
3259
3260             return '';
3261         },
3262
3263         animateX : function(callback, scope) {
3264             var f = function() {
3265                 this.onComplete.removeListener(f);
3266                 if (typeof callback == "function") {
3267                     callback.call(scope || this, this);
3268                 }
3269             };
3270             this.onComplete.addListener(f, this);
3271             this.animate();
3272         },
3273
3274
3275         setRuntimeAttribute: function(attr) {
3276             var start;
3277             var end;
3278             var attributes = this.attributes;
3279
3280             this.runtimeAttributes[attr] = {};
3281
3282             var isset = function(prop) {
3283                 return (typeof prop !== 'undefined');
3284             };
3285
3286             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3287                 return false;
3288             }
3289
3290             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3291
3292
3293             if (isset(attributes[attr]['to'])) {
3294                 end = attributes[attr]['to'];
3295             } else if (isset(attributes[attr]['by'])) {
3296                 if (start.constructor == Array) {
3297                     end = [];
3298                     for (var i = 0, len = start.length; i < len; ++i) {
3299                         end[i] = start[i] + attributes[attr]['by'][i];
3300                     }
3301                 } else {
3302                     end = start + attributes[attr]['by'];
3303                 }
3304             }
3305
3306             this.runtimeAttributes[attr].start = start;
3307             this.runtimeAttributes[attr].end = end;
3308
3309
3310             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3311         },
3312
3313
3314         init: function(el, attributes, duration, method) {
3315
3316             var isAnimated = false;
3317
3318
3319             var startTime = null;
3320
3321
3322             var actualFrames = 0;
3323
3324
3325             el = Roo.getDom(el);
3326
3327
3328             this.attributes = attributes || {};
3329
3330
3331             this.duration = duration || 1;
3332
3333
3334             this.method = method || Roo.lib.Easing.easeNone;
3335
3336
3337             this.useSeconds = true;
3338
3339
3340             this.currentFrame = 0;
3341
3342
3343             this.totalFrames = Roo.lib.AnimMgr.fps;
3344
3345
3346             this.getEl = function() {
3347                 return el;
3348             };
3349
3350
3351             this.isAnimated = function() {
3352                 return isAnimated;
3353             };
3354
3355
3356             this.getStartTime = function() {
3357                 return startTime;
3358             };
3359
3360             this.runtimeAttributes = {};
3361
3362
3363             this.animate = function() {
3364                 if (this.isAnimated()) {
3365                     return false;
3366                 }
3367
3368                 this.currentFrame = 0;
3369
3370                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3371
3372                 Roo.lib.AnimMgr.registerElement(this);
3373             };
3374
3375
3376             this.stop = function(finish) {
3377                 if (finish) {
3378                     this.currentFrame = this.totalFrames;
3379                     this._onTween.fire();
3380                 }
3381                 Roo.lib.AnimMgr.stop(this);
3382             };
3383
3384             var onStart = function() {
3385                 this.onStart.fire();
3386
3387                 this.runtimeAttributes = {};
3388                 for (var attr in this.attributes) {
3389                     this.setRuntimeAttribute(attr);
3390                 }
3391
3392                 isAnimated = true;
3393                 actualFrames = 0;
3394                 startTime = new Date();
3395             };
3396
3397
3398             var onTween = function() {
3399                 var data = {
3400                     duration: new Date() - this.getStartTime(),
3401                     currentFrame: this.currentFrame
3402                 };
3403
3404                 data.toString = function() {
3405                     return (
3406                             'duration: ' + data.duration +
3407                             ', currentFrame: ' + data.currentFrame
3408                             );
3409                 };
3410
3411                 this.onTween.fire(data);
3412
3413                 var runtimeAttributes = this.runtimeAttributes;
3414
3415                 for (var attr in runtimeAttributes) {
3416                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3417                 }
3418
3419                 actualFrames += 1;
3420             };
3421
3422             var onComplete = function() {
3423                 var actual_duration = (new Date() - startTime) / 1000 ;
3424
3425                 var data = {
3426                     duration: actual_duration,
3427                     frames: actualFrames,
3428                     fps: actualFrames / actual_duration
3429                 };
3430
3431                 data.toString = function() {
3432                     return (
3433                             'duration: ' + data.duration +
3434                             ', frames: ' + data.frames +
3435                             ', fps: ' + data.fps
3436                             );
3437                 };
3438
3439                 isAnimated = false;
3440                 actualFrames = 0;
3441                 this.onComplete.fire(data);
3442             };
3443
3444
3445             this._onStart = new Roo.util.Event(this);
3446             this.onStart = new Roo.util.Event(this);
3447             this.onTween = new Roo.util.Event(this);
3448             this._onTween = new Roo.util.Event(this);
3449             this.onComplete = new Roo.util.Event(this);
3450             this._onComplete = new Roo.util.Event(this);
3451             this._onStart.addListener(onStart);
3452             this._onTween.addListener(onTween);
3453             this._onComplete.addListener(onComplete);
3454         }
3455     };
3456 })();
3457 /*
3458  * Portions of this file are based on pieces of Yahoo User Interface Library
3459  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3460  * YUI licensed under the BSD License:
3461  * http://developer.yahoo.net/yui/license.txt
3462  * <script type="text/javascript">
3463  *
3464  */
3465
3466 Roo.lib.AnimMgr = new function() {
3467
3468     var thread = null;
3469
3470
3471     var queue = [];
3472
3473
3474     var tweenCount = 0;
3475
3476
3477     this.fps = 1000;
3478
3479
3480     this.delay = 1;
3481
3482
3483     this.registerElement = function(tween) {
3484         queue[queue.length] = tween;
3485         tweenCount += 1;
3486         tween._onStart.fire();
3487         this.start();
3488     };
3489
3490
3491     this.unRegister = function(tween, index) {
3492         tween._onComplete.fire();
3493         index = index || getIndex(tween);
3494         if (index != -1) {
3495             queue.splice(index, 1);
3496         }
3497
3498         tweenCount -= 1;
3499         if (tweenCount <= 0) {
3500             this.stop();
3501         }
3502     };
3503
3504
3505     this.start = function() {
3506         if (thread === null) {
3507             thread = setInterval(this.run, this.delay);
3508         }
3509     };
3510
3511
3512     this.stop = function(tween) {
3513         if (!tween) {
3514             clearInterval(thread);
3515
3516             for (var i = 0, len = queue.length; i < len; ++i) {
3517                 if (queue[0].isAnimated()) {
3518                     this.unRegister(queue[0], 0);
3519                 }
3520             }
3521
3522             queue = [];
3523             thread = null;
3524             tweenCount = 0;
3525         }
3526         else {
3527             this.unRegister(tween);
3528         }
3529     };
3530
3531
3532     this.run = function() {
3533         for (var i = 0, len = queue.length; i < len; ++i) {
3534             var tween = queue[i];
3535             if (!tween || !tween.isAnimated()) {
3536                 continue;
3537             }
3538
3539             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3540             {
3541                 tween.currentFrame += 1;
3542
3543                 if (tween.useSeconds) {
3544                     correctFrame(tween);
3545                 }
3546                 tween._onTween.fire();
3547             }
3548             else {
3549                 Roo.lib.AnimMgr.stop(tween, i);
3550             }
3551         }
3552     };
3553
3554     var getIndex = function(anim) {
3555         for (var i = 0, len = queue.length; i < len; ++i) {
3556             if (queue[i] == anim) {
3557                 return i;
3558             }
3559         }
3560         return -1;
3561     };
3562
3563
3564     var correctFrame = function(tween) {
3565         var frames = tween.totalFrames;
3566         var frame = tween.currentFrame;
3567         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3568         var elapsed = (new Date() - tween.getStartTime());
3569         var tweak = 0;
3570
3571         if (elapsed < tween.duration * 1000) {
3572             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3573         } else {
3574             tweak = frames - (frame + 1);
3575         }
3576         if (tweak > 0 && isFinite(tweak)) {
3577             if (tween.currentFrame + tweak >= frames) {
3578                 tweak = frames - (frame + 1);
3579             }
3580
3581             tween.currentFrame += tweak;
3582         }
3583     };
3584 };
3585
3586     /*
3587  * Portions of this file are based on pieces of Yahoo User Interface Library
3588  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3589  * YUI licensed under the BSD License:
3590  * http://developer.yahoo.net/yui/license.txt
3591  * <script type="text/javascript">
3592  *
3593  */
3594 Roo.lib.Bezier = new function() {
3595
3596         this.getPosition = function(points, t) {
3597             var n = points.length;
3598             var tmp = [];
3599
3600             for (var i = 0; i < n; ++i) {
3601                 tmp[i] = [points[i][0], points[i][1]];
3602             }
3603
3604             for (var j = 1; j < n; ++j) {
3605                 for (i = 0; i < n - j; ++i) {
3606                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3607                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3608                 }
3609             }
3610
3611             return [ tmp[0][0], tmp[0][1] ];
3612
3613         };
3614     };/*
3615  * Portions of this file are based on pieces of Yahoo User Interface Library
3616  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3617  * YUI licensed under the BSD License:
3618  * http://developer.yahoo.net/yui/license.txt
3619  * <script type="text/javascript">
3620  *
3621  */
3622 (function() {
3623
3624     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3625         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3626     };
3627
3628     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3629
3630     var fly = Roo.lib.AnimBase.fly;
3631     var Y = Roo.lib;
3632     var superclass = Y.ColorAnim.superclass;
3633     var proto = Y.ColorAnim.prototype;
3634
3635     proto.toString = function() {
3636         var el = this.getEl();
3637         var id = el.id || el.tagName;
3638         return ("ColorAnim " + id);
3639     };
3640
3641     proto.patterns.color = /color$/i;
3642     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3643     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3644     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3645     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3646
3647
3648     proto.parseColor = function(s) {
3649         if (s.length == 3) {
3650             return s;
3651         }
3652
3653         var c = this.patterns.hex.exec(s);
3654         if (c && c.length == 4) {
3655             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3656         }
3657
3658         c = this.patterns.rgb.exec(s);
3659         if (c && c.length == 4) {
3660             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3661         }
3662
3663         c = this.patterns.hex3.exec(s);
3664         if (c && c.length == 4) {
3665             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3666         }
3667
3668         return null;
3669     };
3670     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3671     proto.getAttribute = function(attr) {
3672         var el = this.getEl();
3673         if (this.patterns.color.test(attr)) {
3674             var val = fly(el).getStyle(attr);
3675
3676             if (this.patterns.transparent.test(val)) {
3677                 var parent = el.parentNode;
3678                 val = fly(parent).getStyle(attr);
3679
3680                 while (parent && this.patterns.transparent.test(val)) {
3681                     parent = parent.parentNode;
3682                     val = fly(parent).getStyle(attr);
3683                     if (parent.tagName.toUpperCase() == 'HTML') {
3684                         val = '#fff';
3685                     }
3686                 }
3687             }
3688         } else {
3689             val = superclass.getAttribute.call(this, attr);
3690         }
3691
3692         return val;
3693     };
3694     proto.getAttribute = function(attr) {
3695         var el = this.getEl();
3696         if (this.patterns.color.test(attr)) {
3697             var val = fly(el).getStyle(attr);
3698
3699             if (this.patterns.transparent.test(val)) {
3700                 var parent = el.parentNode;
3701                 val = fly(parent).getStyle(attr);
3702
3703                 while (parent && this.patterns.transparent.test(val)) {
3704                     parent = parent.parentNode;
3705                     val = fly(parent).getStyle(attr);
3706                     if (parent.tagName.toUpperCase() == 'HTML') {
3707                         val = '#fff';
3708                     }
3709                 }
3710             }
3711         } else {
3712             val = superclass.getAttribute.call(this, attr);
3713         }
3714
3715         return val;
3716     };
3717
3718     proto.doMethod = function(attr, start, end) {
3719         var val;
3720
3721         if (this.patterns.color.test(attr)) {
3722             val = [];
3723             for (var i = 0, len = start.length; i < len; ++i) {
3724                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3725             }
3726
3727             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3728         }
3729         else {
3730             val = superclass.doMethod.call(this, attr, start, end);
3731         }
3732
3733         return val;
3734     };
3735
3736     proto.setRuntimeAttribute = function(attr) {
3737         superclass.setRuntimeAttribute.call(this, attr);
3738
3739         if (this.patterns.color.test(attr)) {
3740             var attributes = this.attributes;
3741             var start = this.parseColor(this.runtimeAttributes[attr].start);
3742             var end = this.parseColor(this.runtimeAttributes[attr].end);
3743
3744             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3745                 end = this.parseColor(attributes[attr].by);
3746
3747                 for (var i = 0, len = start.length; i < len; ++i) {
3748                     end[i] = start[i] + end[i];
3749                 }
3750             }
3751
3752             this.runtimeAttributes[attr].start = start;
3753             this.runtimeAttributes[attr].end = end;
3754         }
3755     };
3756 })();
3757
3758 /*
3759  * Portions of this file are based on pieces of Yahoo User Interface Library
3760  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3761  * YUI licensed under the BSD License:
3762  * http://developer.yahoo.net/yui/license.txt
3763  * <script type="text/javascript">
3764  *
3765  */
3766 Roo.lib.Easing = {
3767
3768
3769     easeNone: function (t, b, c, d) {
3770         return c * t / d + b;
3771     },
3772
3773
3774     easeIn: function (t, b, c, d) {
3775         return c * (t /= d) * t + b;
3776     },
3777
3778
3779     easeOut: function (t, b, c, d) {
3780         return -c * (t /= d) * (t - 2) + b;
3781     },
3782
3783
3784     easeBoth: function (t, b, c, d) {
3785         if ((t /= d / 2) < 1) {
3786             return c / 2 * t * t + b;
3787         }
3788
3789         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3790     },
3791
3792
3793     easeInStrong: function (t, b, c, d) {
3794         return c * (t /= d) * t * t * t + b;
3795     },
3796
3797
3798     easeOutStrong: function (t, b, c, d) {
3799         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3800     },
3801
3802
3803     easeBothStrong: function (t, b, c, d) {
3804         if ((t /= d / 2) < 1) {
3805             return c / 2 * t * t * t * t + b;
3806         }
3807
3808         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3809     },
3810
3811
3812
3813     elasticIn: function (t, b, c, d, a, p) {
3814         if (t == 0) {
3815             return b;
3816         }
3817         if ((t /= d) == 1) {
3818             return b + c;
3819         }
3820         if (!p) {
3821             p = d * .3;
3822         }
3823
3824         if (!a || a < Math.abs(c)) {
3825             a = c;
3826             var s = p / 4;
3827         }
3828         else {
3829             var s = p / (2 * Math.PI) * Math.asin(c / a);
3830         }
3831
3832         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3833     },
3834
3835
3836     elasticOut: function (t, b, c, d, a, p) {
3837         if (t == 0) {
3838             return b;
3839         }
3840         if ((t /= d) == 1) {
3841             return b + c;
3842         }
3843         if (!p) {
3844             p = d * .3;
3845         }
3846
3847         if (!a || a < Math.abs(c)) {
3848             a = c;
3849             var s = p / 4;
3850         }
3851         else {
3852             var s = p / (2 * Math.PI) * Math.asin(c / a);
3853         }
3854
3855         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3856     },
3857
3858
3859     elasticBoth: function (t, b, c, d, a, p) {
3860         if (t == 0) {
3861             return b;
3862         }
3863
3864         if ((t /= d / 2) == 2) {
3865             return b + c;
3866         }
3867
3868         if (!p) {
3869             p = d * (.3 * 1.5);
3870         }
3871
3872         if (!a || a < Math.abs(c)) {
3873             a = c;
3874             var s = p / 4;
3875         }
3876         else {
3877             var s = p / (2 * Math.PI) * Math.asin(c / a);
3878         }
3879
3880         if (t < 1) {
3881             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3882                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3883         }
3884         return a * Math.pow(2, -10 * (t -= 1)) *
3885                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3886     },
3887
3888
3889
3890     backIn: function (t, b, c, d, s) {
3891         if (typeof s == 'undefined') {
3892             s = 1.70158;
3893         }
3894         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3895     },
3896
3897
3898     backOut: function (t, b, c, d, s) {
3899         if (typeof s == 'undefined') {
3900             s = 1.70158;
3901         }
3902         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3903     },
3904
3905
3906     backBoth: function (t, b, c, d, s) {
3907         if (typeof s == 'undefined') {
3908             s = 1.70158;
3909         }
3910
3911         if ((t /= d / 2 ) < 1) {
3912             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3913         }
3914         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3915     },
3916
3917
3918     bounceIn: function (t, b, c, d) {
3919         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3920     },
3921
3922
3923     bounceOut: function (t, b, c, d) {
3924         if ((t /= d) < (1 / 2.75)) {
3925             return c * (7.5625 * t * t) + b;
3926         } else if (t < (2 / 2.75)) {
3927             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3928         } else if (t < (2.5 / 2.75)) {
3929             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3930         }
3931         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3932     },
3933
3934
3935     bounceBoth: function (t, b, c, d) {
3936         if (t < d / 2) {
3937             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3938         }
3939         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3940     }
3941 };/*
3942  * Portions of this file are based on pieces of Yahoo User Interface Library
3943  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3944  * YUI licensed under the BSD License:
3945  * http://developer.yahoo.net/yui/license.txt
3946  * <script type="text/javascript">
3947  *
3948  */
3949     (function() {
3950         Roo.lib.Motion = function(el, attributes, duration, method) {
3951             if (el) {
3952                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3953             }
3954         };
3955
3956         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3957
3958
3959         var Y = Roo.lib;
3960         var superclass = Y.Motion.superclass;
3961         var proto = Y.Motion.prototype;
3962
3963         proto.toString = function() {
3964             var el = this.getEl();
3965             var id = el.id || el.tagName;
3966             return ("Motion " + id);
3967         };
3968
3969         proto.patterns.points = /^points$/i;
3970
3971         proto.setAttribute = function(attr, val, unit) {
3972             if (this.patterns.points.test(attr)) {
3973                 unit = unit || 'px';
3974                 superclass.setAttribute.call(this, 'left', val[0], unit);
3975                 superclass.setAttribute.call(this, 'top', val[1], unit);
3976             } else {
3977                 superclass.setAttribute.call(this, attr, val, unit);
3978             }
3979         };
3980
3981         proto.getAttribute = function(attr) {
3982             if (this.patterns.points.test(attr)) {
3983                 var val = [
3984                         superclass.getAttribute.call(this, 'left'),
3985                         superclass.getAttribute.call(this, 'top')
3986                         ];
3987             } else {
3988                 val = superclass.getAttribute.call(this, attr);
3989             }
3990
3991             return val;
3992         };
3993
3994         proto.doMethod = function(attr, start, end) {
3995             var val = null;
3996
3997             if (this.patterns.points.test(attr)) {
3998                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3999                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
4000             } else {
4001                 val = superclass.doMethod.call(this, attr, start, end);
4002             }
4003             return val;
4004         };
4005
4006         proto.setRuntimeAttribute = function(attr) {
4007             if (this.patterns.points.test(attr)) {
4008                 var el = this.getEl();
4009                 var attributes = this.attributes;
4010                 var start;
4011                 var control = attributes['points']['control'] || [];
4012                 var end;
4013                 var i, len;
4014
4015                 if (control.length > 0 && !(control[0] instanceof Array)) {
4016                     control = [control];
4017                 } else {
4018                     var tmp = [];
4019                     for (i = 0,len = control.length; i < len; ++i) {
4020                         tmp[i] = control[i];
4021                     }
4022                     control = tmp;
4023                 }
4024
4025                 Roo.fly(el).position();
4026
4027                 if (isset(attributes['points']['from'])) {
4028                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
4029                 }
4030                 else {
4031                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
4032                 }
4033
4034                 start = this.getAttribute('points');
4035
4036
4037                 if (isset(attributes['points']['to'])) {
4038                     end = translateValues.call(this, attributes['points']['to'], start);
4039
4040                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4041                     for (i = 0,len = control.length; i < len; ++i) {
4042                         control[i] = translateValues.call(this, control[i], start);
4043                     }
4044
4045
4046                 } else if (isset(attributes['points']['by'])) {
4047                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4048
4049                     for (i = 0,len = control.length; i < len; ++i) {
4050                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4051                     }
4052                 }
4053
4054                 this.runtimeAttributes[attr] = [start];
4055
4056                 if (control.length > 0) {
4057                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4058                 }
4059
4060                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4061             }
4062             else {
4063                 superclass.setRuntimeAttribute.call(this, attr);
4064             }
4065         };
4066
4067         var translateValues = function(val, start) {
4068             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4069             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4070
4071             return val;
4072         };
4073
4074         var isset = function(prop) {
4075             return (typeof prop !== 'undefined');
4076         };
4077     })();
4078 /*
4079  * Portions of this file are based on pieces of Yahoo User Interface Library
4080  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4081  * YUI licensed under the BSD License:
4082  * http://developer.yahoo.net/yui/license.txt
4083  * <script type="text/javascript">
4084  *
4085  */
4086     (function() {
4087         Roo.lib.Scroll = function(el, attributes, duration, method) {
4088             if (el) {
4089                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4090             }
4091         };
4092
4093         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4094
4095
4096         var Y = Roo.lib;
4097         var superclass = Y.Scroll.superclass;
4098         var proto = Y.Scroll.prototype;
4099
4100         proto.toString = function() {
4101             var el = this.getEl();
4102             var id = el.id || el.tagName;
4103             return ("Scroll " + id);
4104         };
4105
4106         proto.doMethod = function(attr, start, end) {
4107             var val = null;
4108
4109             if (attr == 'scroll') {
4110                 val = [
4111                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4112                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4113                         ];
4114
4115             } else {
4116                 val = superclass.doMethod.call(this, attr, start, end);
4117             }
4118             return val;
4119         };
4120
4121         proto.getAttribute = function(attr) {
4122             var val = null;
4123             var el = this.getEl();
4124
4125             if (attr == 'scroll') {
4126                 val = [ el.scrollLeft, el.scrollTop ];
4127             } else {
4128                 val = superclass.getAttribute.call(this, attr);
4129             }
4130
4131             return val;
4132         };
4133
4134         proto.setAttribute = function(attr, val, unit) {
4135             var el = this.getEl();
4136
4137             if (attr == 'scroll') {
4138                 el.scrollLeft = val[0];
4139                 el.scrollTop = val[1];
4140             } else {
4141                 superclass.setAttribute.call(this, attr, val, unit);
4142             }
4143         };
4144     })();
4145 /*
4146  * Based on:
4147  * Ext JS Library 1.1.1
4148  * Copyright(c) 2006-2007, Ext JS, LLC.
4149  *
4150  * Originally Released Under LGPL - original licence link has changed is not relivant.
4151  *
4152  * Fork - LGPL
4153  * <script type="text/javascript">
4154  */
4155
4156
4157 // nasty IE9 hack - what a pile of crap that is..
4158
4159  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4160     Range.prototype.createContextualFragment = function (html) {
4161         var doc = window.document;
4162         var container = doc.createElement("div");
4163         container.innerHTML = html;
4164         var frag = doc.createDocumentFragment(), n;
4165         while ((n = container.firstChild)) {
4166             frag.appendChild(n);
4167         }
4168         return frag;
4169     };
4170 }
4171
4172 /**
4173  * @class Roo.DomHelper
4174  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4175  * 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>.
4176  * @singleton
4177  */
4178 Roo.DomHelper = function(){
4179     var tempTableEl = null;
4180     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4181     var tableRe = /^table|tbody|tr|td$/i;
4182     var xmlns = {};
4183     // build as innerHTML where available
4184     /** @ignore */
4185     var createHtml = function(o){
4186         if(typeof o == 'string'){
4187             return o;
4188         }
4189         var b = "";
4190         if(!o.tag){
4191             o.tag = "div";
4192         }
4193         b += "<" + o.tag;
4194         for(var attr in o){
4195             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
4196             if(attr == "style"){
4197                 var s = o["style"];
4198                 if(typeof s == "function"){
4199                     s = s.call();
4200                 }
4201                 if(typeof s == "string"){
4202                     b += ' style="' + s + '"';
4203                 }else if(typeof s == "object"){
4204                     b += ' style="';
4205                     for(var key in s){
4206                         if(typeof s[key] != "function"){
4207                             b += key + ":" + s[key] + ";";
4208                         }
4209                     }
4210                     b += '"';
4211                 }
4212             }else{
4213                 if(attr == "cls"){
4214                     b += ' class="' + o["cls"] + '"';
4215                 }else if(attr == "htmlFor"){
4216                     b += ' for="' + o["htmlFor"] + '"';
4217                 }else{
4218                     b += " " + attr + '="' + o[attr] + '"';
4219                 }
4220             }
4221         }
4222         if(emptyTags.test(o.tag)){
4223             b += "/>";
4224         }else{
4225             b += ">";
4226             var cn = o.children || o.cn;
4227             if(cn){
4228                 //http://bugs.kde.org/show_bug.cgi?id=71506
4229                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4230                     for(var i = 0, len = cn.length; i < len; i++) {
4231                         b += createHtml(cn[i], b);
4232                     }
4233                 }else{
4234                     b += createHtml(cn, b);
4235                 }
4236             }
4237             if(o.html){
4238                 b += o.html;
4239             }
4240             b += "</" + o.tag + ">";
4241         }
4242         return b;
4243     };
4244
4245     // build as dom
4246     /** @ignore */
4247     var createDom = function(o, parentNode){
4248          
4249         // defininition craeted..
4250         var ns = false;
4251         if (o.ns && o.ns != 'html') {
4252                
4253             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4254                 xmlns[o.ns] = o.xmlns;
4255                 ns = o.xmlns;
4256             }
4257             if (typeof(xmlns[o.ns]) == 'undefined') {
4258                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4259             }
4260             ns = xmlns[o.ns];
4261         }
4262         
4263         
4264         if (typeof(o) == 'string') {
4265             return parentNode.appendChild(document.createTextNode(o));
4266         }
4267         o.tag = o.tag || div;
4268         if (o.ns && Roo.isIE) {
4269             ns = false;
4270             o.tag = o.ns + ':' + o.tag;
4271             
4272         }
4273         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4274         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4275         for(var attr in o){
4276             
4277             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4278                     attr == "style" || typeof o[attr] == "function") { continue; }
4279                     
4280             if(attr=="cls" && Roo.isIE){
4281                 el.className = o["cls"];
4282             }else{
4283                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
4284                 else { 
4285                     el[attr] = o[attr];
4286                 }
4287             }
4288         }
4289         Roo.DomHelper.applyStyles(el, o.style);
4290         var cn = o.children || o.cn;
4291         if(cn){
4292             //http://bugs.kde.org/show_bug.cgi?id=71506
4293              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4294                 for(var i = 0, len = cn.length; i < len; i++) {
4295                     createDom(cn[i], el);
4296                 }
4297             }else{
4298                 createDom(cn, el);
4299             }
4300         }
4301         if(o.html){
4302             el.innerHTML = o.html;
4303         }
4304         if(parentNode){
4305            parentNode.appendChild(el);
4306         }
4307         return el;
4308     };
4309
4310     var ieTable = function(depth, s, h, e){
4311         tempTableEl.innerHTML = [s, h, e].join('');
4312         var i = -1, el = tempTableEl;
4313         while(++i < depth){
4314             el = el.firstChild;
4315         }
4316         return el;
4317     };
4318
4319     // kill repeat to save bytes
4320     var ts = '<table>',
4321         te = '</table>',
4322         tbs = ts+'<tbody>',
4323         tbe = '</tbody>'+te,
4324         trs = tbs + '<tr>',
4325         tre = '</tr>'+tbe;
4326
4327     /**
4328      * @ignore
4329      * Nasty code for IE's broken table implementation
4330      */
4331     var insertIntoTable = function(tag, where, el, html){
4332         if(!tempTableEl){
4333             tempTableEl = document.createElement('div');
4334         }
4335         var node;
4336         var before = null;
4337         if(tag == 'td'){
4338             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4339                 return;
4340             }
4341             if(where == 'beforebegin'){
4342                 before = el;
4343                 el = el.parentNode;
4344             } else{
4345                 before = el.nextSibling;
4346                 el = el.parentNode;
4347             }
4348             node = ieTable(4, trs, html, tre);
4349         }
4350         else if(tag == 'tr'){
4351             if(where == 'beforebegin'){
4352                 before = el;
4353                 el = el.parentNode;
4354                 node = ieTable(3, tbs, html, tbe);
4355             } else if(where == 'afterend'){
4356                 before = el.nextSibling;
4357                 el = el.parentNode;
4358                 node = ieTable(3, tbs, html, tbe);
4359             } else{ // INTO a TR
4360                 if(where == 'afterbegin'){
4361                     before = el.firstChild;
4362                 }
4363                 node = ieTable(4, trs, html, tre);
4364             }
4365         } else if(tag == 'tbody'){
4366             if(where == 'beforebegin'){
4367                 before = el;
4368                 el = el.parentNode;
4369                 node = ieTable(2, ts, html, te);
4370             } else if(where == 'afterend'){
4371                 before = el.nextSibling;
4372                 el = el.parentNode;
4373                 node = ieTable(2, ts, html, te);
4374             } else{
4375                 if(where == 'afterbegin'){
4376                     before = el.firstChild;
4377                 }
4378                 node = ieTable(3, tbs, html, tbe);
4379             }
4380         } else{ // TABLE
4381             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4382                 return;
4383             }
4384             if(where == 'afterbegin'){
4385                 before = el.firstChild;
4386             }
4387             node = ieTable(2, ts, html, te);
4388         }
4389         el.insertBefore(node, before);
4390         return node;
4391     };
4392
4393     return {
4394     /** True to force the use of DOM instead of html fragments @type Boolean */
4395     useDom : false,
4396
4397     /**
4398      * Returns the markup for the passed Element(s) config
4399      * @param {Object} o The Dom object spec (and children)
4400      * @return {String}
4401      */
4402     markup : function(o){
4403         return createHtml(o);
4404     },
4405
4406     /**
4407      * Applies a style specification to an element
4408      * @param {String/HTMLElement} el The element to apply styles to
4409      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4410      * a function which returns such a specification.
4411      */
4412     applyStyles : function(el, styles){
4413         if(styles){
4414            el = Roo.fly(el);
4415            if(typeof styles == "string"){
4416                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4417                var matches;
4418                while ((matches = re.exec(styles)) != null){
4419                    el.setStyle(matches[1], matches[2]);
4420                }
4421            }else if (typeof styles == "object"){
4422                for (var style in styles){
4423                   el.setStyle(style, styles[style]);
4424                }
4425            }else if (typeof styles == "function"){
4426                 Roo.DomHelper.applyStyles(el, styles.call());
4427            }
4428         }
4429     },
4430
4431     /**
4432      * Inserts an HTML fragment into the Dom
4433      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4434      * @param {HTMLElement} el The context element
4435      * @param {String} html The HTML fragmenet
4436      * @return {HTMLElement} The new node
4437      */
4438     insertHtml : function(where, el, html){
4439         where = where.toLowerCase();
4440         if(el.insertAdjacentHTML){
4441             if(tableRe.test(el.tagName)){
4442                 var rs;
4443                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4444                     return rs;
4445                 }
4446             }
4447             switch(where){
4448                 case "beforebegin":
4449                     el.insertAdjacentHTML('BeforeBegin', html);
4450                     return el.previousSibling;
4451                 case "afterbegin":
4452                     el.insertAdjacentHTML('AfterBegin', html);
4453                     return el.firstChild;
4454                 case "beforeend":
4455                     el.insertAdjacentHTML('BeforeEnd', html);
4456                     return el.lastChild;
4457                 case "afterend":
4458                     el.insertAdjacentHTML('AfterEnd', html);
4459                     return el.nextSibling;
4460             }
4461             throw 'Illegal insertion point -> "' + where + '"';
4462         }
4463         var range = el.ownerDocument.createRange();
4464         var frag;
4465         switch(where){
4466              case "beforebegin":
4467                 range.setStartBefore(el);
4468                 frag = range.createContextualFragment(html);
4469                 el.parentNode.insertBefore(frag, el);
4470                 return el.previousSibling;
4471              case "afterbegin":
4472                 if(el.firstChild){
4473                     range.setStartBefore(el.firstChild);
4474                     frag = range.createContextualFragment(html);
4475                     el.insertBefore(frag, el.firstChild);
4476                     return el.firstChild;
4477                 }else{
4478                     el.innerHTML = html;
4479                     return el.firstChild;
4480                 }
4481             case "beforeend":
4482                 if(el.lastChild){
4483                     range.setStartAfter(el.lastChild);
4484                     frag = range.createContextualFragment(html);
4485                     el.appendChild(frag);
4486                     return el.lastChild;
4487                 }else{
4488                     el.innerHTML = html;
4489                     return el.lastChild;
4490                 }
4491             case "afterend":
4492                 range.setStartAfter(el);
4493                 frag = range.createContextualFragment(html);
4494                 el.parentNode.insertBefore(frag, el.nextSibling);
4495                 return el.nextSibling;
4496             }
4497             throw 'Illegal insertion point -> "' + where + '"';
4498     },
4499
4500     /**
4501      * Creates new Dom element(s) and inserts them before el
4502      * @param {String/HTMLElement/Element} el The context element
4503      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4504      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4505      * @return {HTMLElement/Roo.Element} The new node
4506      */
4507     insertBefore : function(el, o, returnElement){
4508         return this.doInsert(el, o, returnElement, "beforeBegin");
4509     },
4510
4511     /**
4512      * Creates new Dom element(s) and inserts them after el
4513      * @param {String/HTMLElement/Element} el The context element
4514      * @param {Object} o The Dom object spec (and children)
4515      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4516      * @return {HTMLElement/Roo.Element} The new node
4517      */
4518     insertAfter : function(el, o, returnElement){
4519         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4520     },
4521
4522     /**
4523      * Creates new Dom element(s) and inserts them as the first child of el
4524      * @param {String/HTMLElement/Element} el The context element
4525      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4526      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4527      * @return {HTMLElement/Roo.Element} The new node
4528      */
4529     insertFirst : function(el, o, returnElement){
4530         return this.doInsert(el, o, returnElement, "afterBegin");
4531     },
4532
4533     // private
4534     doInsert : function(el, o, returnElement, pos, sibling){
4535         el = Roo.getDom(el);
4536         var newNode;
4537         if(this.useDom || o.ns){
4538             newNode = createDom(o, null);
4539             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4540         }else{
4541             var html = createHtml(o);
4542             newNode = this.insertHtml(pos, el, html);
4543         }
4544         return returnElement ? Roo.get(newNode, true) : newNode;
4545     },
4546
4547     /**
4548      * Creates new Dom element(s) and appends them to el
4549      * @param {String/HTMLElement/Element} el The context element
4550      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4551      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4552      * @return {HTMLElement/Roo.Element} The new node
4553      */
4554     append : function(el, o, returnElement){
4555         el = Roo.getDom(el);
4556         var newNode;
4557         if(this.useDom || o.ns){
4558             newNode = createDom(o, null);
4559             el.appendChild(newNode);
4560         }else{
4561             var html = createHtml(o);
4562             newNode = this.insertHtml("beforeEnd", el, html);
4563         }
4564         return returnElement ? Roo.get(newNode, true) : newNode;
4565     },
4566
4567     /**
4568      * Creates new Dom element(s) and overwrites the contents of el with them
4569      * @param {String/HTMLElement/Element} el The context element
4570      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4571      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4572      * @return {HTMLElement/Roo.Element} The new node
4573      */
4574     overwrite : function(el, o, returnElement){
4575         el = Roo.getDom(el);
4576         if (o.ns) {
4577           
4578             while (el.childNodes.length) {
4579                 el.removeChild(el.firstChild);
4580             }
4581             createDom(o, el);
4582         } else {
4583             el.innerHTML = createHtml(o);   
4584         }
4585         
4586         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4587     },
4588
4589     /**
4590      * Creates a new Roo.DomHelper.Template from the Dom object spec
4591      * @param {Object} o The Dom object spec (and children)
4592      * @return {Roo.DomHelper.Template} The new template
4593      */
4594     createTemplate : function(o){
4595         var html = createHtml(o);
4596         return new Roo.Template(html);
4597     }
4598     };
4599 }();
4600 /*
4601  * Based on:
4602  * Ext JS Library 1.1.1
4603  * Copyright(c) 2006-2007, Ext JS, LLC.
4604  *
4605  * Originally Released Under LGPL - original licence link has changed is not relivant.
4606  *
4607  * Fork - LGPL
4608  * <script type="text/javascript">
4609  */
4610  
4611 /**
4612 * @class Roo.Template
4613 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4614 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4615 * Usage:
4616 <pre><code>
4617 var t = new Roo.Template({
4618     html :  '&lt;div name="{id}"&gt;' + 
4619         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4620         '&lt;/div&gt;',
4621     myformat: function (value, allValues) {
4622         return 'XX' + value;
4623     }
4624 });
4625 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4626 </code></pre>
4627 * For more information see this blog post with examples:
4628 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4629      - Create Elements using DOM, HTML fragments and Templates</a>. 
4630 * @constructor
4631 * @param {Object} cfg - Configuration object.
4632 */
4633 Roo.Template = function(cfg){
4634     // BC!
4635     if(cfg instanceof Array){
4636         cfg = cfg.join("");
4637     }else if(arguments.length > 1){
4638         cfg = Array.prototype.join.call(arguments, "");
4639     }
4640     
4641     
4642     if (typeof(cfg) == 'object') {
4643         Roo.apply(this,cfg)
4644     } else {
4645         // bc
4646         this.html = cfg;
4647     }
4648     if (this.url) {
4649         this.load();
4650     }
4651     
4652 };
4653 Roo.Template.prototype = {
4654     
4655     /**
4656      * @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..
4657      *                    it should be fixed so that template is observable...
4658      */
4659     url : false,
4660     /**
4661      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4662      */
4663     html : '',
4664     /**
4665      * Returns an HTML fragment of this template with the specified values applied.
4666      * @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'})
4667      * @return {String} The HTML fragment
4668      */
4669     applyTemplate : function(values){
4670         //Roo.log(["applyTemplate", values]);
4671         try {
4672            
4673             if(this.compiled){
4674                 return this.compiled(values);
4675             }
4676             var useF = this.disableFormats !== true;
4677             var fm = Roo.util.Format, tpl = this;
4678             var fn = function(m, name, format, args){
4679                 if(format && useF){
4680                     if(format.substr(0, 5) == "this."){
4681                         return tpl.call(format.substr(5), values[name], values);
4682                     }else{
4683                         if(args){
4684                             // quoted values are required for strings in compiled templates, 
4685                             // but for non compiled we need to strip them
4686                             // quoted reversed for jsmin
4687                             var re = /^\s*['"](.*)["']\s*$/;
4688                             args = args.split(',');
4689                             for(var i = 0, len = args.length; i < len; i++){
4690                                 args[i] = args[i].replace(re, "$1");
4691                             }
4692                             args = [values[name]].concat(args);
4693                         }else{
4694                             args = [values[name]];
4695                         }
4696                         return fm[format].apply(fm, args);
4697                     }
4698                 }else{
4699                     return values[name] !== undefined ? values[name] : "";
4700                 }
4701             };
4702             return this.html.replace(this.re, fn);
4703         } catch (e) {
4704             Roo.log(e);
4705             throw e;
4706         }
4707          
4708     },
4709     
4710     loading : false,
4711       
4712     load : function ()
4713     {
4714          
4715         if (this.loading) {
4716             return;
4717         }
4718         var _t = this;
4719         
4720         this.loading = true;
4721         this.compiled = false;
4722         
4723         var cx = new Roo.data.Connection();
4724         cx.request({
4725             url : this.url,
4726             method : 'GET',
4727             success : function (response) {
4728                 _t.loading = false;
4729                 _t.html = response.responseText;
4730                 _t.url = false;
4731                 _t.compile();
4732              },
4733             failure : function(response) {
4734                 Roo.log("Template failed to load from " + _t.url);
4735                 _t.loading = false;
4736             }
4737         });
4738     },
4739
4740     /**
4741      * Sets the HTML used as the template and optionally compiles it.
4742      * @param {String} html
4743      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4744      * @return {Roo.Template} this
4745      */
4746     set : function(html, compile){
4747         this.html = html;
4748         this.compiled = null;
4749         if(compile){
4750             this.compile();
4751         }
4752         return this;
4753     },
4754     
4755     /**
4756      * True to disable format functions (defaults to false)
4757      * @type Boolean
4758      */
4759     disableFormats : false,
4760     
4761     /**
4762     * The regular expression used to match template variables 
4763     * @type RegExp
4764     * @property 
4765     */
4766     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4767     
4768     /**
4769      * Compiles the template into an internal function, eliminating the RegEx overhead.
4770      * @return {Roo.Template} this
4771      */
4772     compile : function(){
4773         var fm = Roo.util.Format;
4774         var useF = this.disableFormats !== true;
4775         var sep = Roo.isGecko ? "+" : ",";
4776         var fn = function(m, name, format, args){
4777             if(format && useF){
4778                 args = args ? ',' + args : "";
4779                 if(format.substr(0, 5) != "this."){
4780                     format = "fm." + format + '(';
4781                 }else{
4782                     format = 'this.call("'+ format.substr(5) + '", ';
4783                     args = ", values";
4784                 }
4785             }else{
4786                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4787             }
4788             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4789         };
4790         var body;
4791         // branched to use + in gecko and [].join() in others
4792         if(Roo.isGecko){
4793             body = "this.compiled = function(values){ return '" +
4794                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4795                     "';};";
4796         }else{
4797             body = ["this.compiled = function(values){ return ['"];
4798             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4799             body.push("'].join('');};");
4800             body = body.join('');
4801         }
4802         /**
4803          * eval:var:values
4804          * eval:var:fm
4805          */
4806         eval(body);
4807         return this;
4808     },
4809     
4810     // private function used to call members
4811     call : function(fnName, value, allValues){
4812         return this[fnName](value, allValues);
4813     },
4814     
4815     /**
4816      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4817      * @param {String/HTMLElement/Roo.Element} el The context element
4818      * @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'})
4819      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4820      * @return {HTMLElement/Roo.Element} The new node or Element
4821      */
4822     insertFirst: function(el, values, returnElement){
4823         return this.doInsert('afterBegin', el, values, returnElement);
4824     },
4825
4826     /**
4827      * Applies the supplied values to the template and inserts the new node(s) before el.
4828      * @param {String/HTMLElement/Roo.Element} el The context element
4829      * @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'})
4830      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4831      * @return {HTMLElement/Roo.Element} The new node or Element
4832      */
4833     insertBefore: function(el, values, returnElement){
4834         return this.doInsert('beforeBegin', el, values, returnElement);
4835     },
4836
4837     /**
4838      * Applies the supplied values to the template and inserts the new node(s) after el.
4839      * @param {String/HTMLElement/Roo.Element} el The context element
4840      * @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'})
4841      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4842      * @return {HTMLElement/Roo.Element} The new node or Element
4843      */
4844     insertAfter : function(el, values, returnElement){
4845         return this.doInsert('afterEnd', el, values, returnElement);
4846     },
4847     
4848     /**
4849      * Applies the supplied values to the template and appends the new node(s) to el.
4850      * @param {String/HTMLElement/Roo.Element} el The context element
4851      * @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'})
4852      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4853      * @return {HTMLElement/Roo.Element} The new node or Element
4854      */
4855     append : function(el, values, returnElement){
4856         return this.doInsert('beforeEnd', el, values, returnElement);
4857     },
4858
4859     doInsert : function(where, el, values, returnEl){
4860         el = Roo.getDom(el);
4861         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4862         return returnEl ? Roo.get(newNode, true) : newNode;
4863     },
4864
4865     /**
4866      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4867      * @param {String/HTMLElement/Roo.Element} el The context element
4868      * @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'})
4869      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4870      * @return {HTMLElement/Roo.Element} The new node or Element
4871      */
4872     overwrite : function(el, values, returnElement){
4873         el = Roo.getDom(el);
4874         el.innerHTML = this.applyTemplate(values);
4875         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4876     }
4877 };
4878 /**
4879  * Alias for {@link #applyTemplate}
4880  * @method
4881  */
4882 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4883
4884 // backwards compat
4885 Roo.DomHelper.Template = Roo.Template;
4886
4887 /**
4888  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4889  * @param {String/HTMLElement} el A DOM element or its id
4890  * @returns {Roo.Template} The created template
4891  * @static
4892  */
4893 Roo.Template.from = function(el){
4894     el = Roo.getDom(el);
4895     return new Roo.Template(el.value || el.innerHTML);
4896 };/*
4897  * Based on:
4898  * Ext JS Library 1.1.1
4899  * Copyright(c) 2006-2007, Ext JS, LLC.
4900  *
4901  * Originally Released Under LGPL - original licence link has changed is not relivant.
4902  *
4903  * Fork - LGPL
4904  * <script type="text/javascript">
4905  */
4906  
4907
4908 /*
4909  * This is code is also distributed under MIT license for use
4910  * with jQuery and prototype JavaScript libraries.
4911  */
4912 /**
4913  * @class Roo.DomQuery
4914 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).
4915 <p>
4916 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>
4917
4918 <p>
4919 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.
4920 </p>
4921 <h4>Element Selectors:</h4>
4922 <ul class="list">
4923     <li> <b>*</b> any element</li>
4924     <li> <b>E</b> an element with the tag E</li>
4925     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4926     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4927     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4928     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4929 </ul>
4930 <h4>Attribute Selectors:</h4>
4931 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4932 <ul class="list">
4933     <li> <b>E[foo]</b> has an attribute "foo"</li>
4934     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4935     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4936     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4937     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4938     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4939     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4940 </ul>
4941 <h4>Pseudo Classes:</h4>
4942 <ul class="list">
4943     <li> <b>E:first-child</b> E is the first child of its parent</li>
4944     <li> <b>E:last-child</b> E is the last child of its parent</li>
4945     <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>
4946     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4947     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4948     <li> <b>E:only-child</b> E is the only child of its parent</li>
4949     <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>
4950     <li> <b>E:first</b> the first E in the resultset</li>
4951     <li> <b>E:last</b> the last E in the resultset</li>
4952     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4953     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4954     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4955     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4956     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4957     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4958     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4959     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4960     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4961 </ul>
4962 <h4>CSS Value Selectors:</h4>
4963 <ul class="list">
4964     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4965     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4966     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4967     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4968     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4969     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4970 </ul>
4971  * @singleton
4972  */
4973 Roo.DomQuery = function(){
4974     var cache = {}, simpleCache = {}, valueCache = {};
4975     var nonSpace = /\S/;
4976     var trimRe = /^\s+|\s+$/g;
4977     var tplRe = /\{(\d+)\}/g;
4978     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4979     var tagTokenRe = /^(#)?([\w-\*]+)/;
4980     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4981
4982     function child(p, index){
4983         var i = 0;
4984         var n = p.firstChild;
4985         while(n){
4986             if(n.nodeType == 1){
4987                if(++i == index){
4988                    return n;
4989                }
4990             }
4991             n = n.nextSibling;
4992         }
4993         return null;
4994     };
4995
4996     function next(n){
4997         while((n = n.nextSibling) && n.nodeType != 1);
4998         return n;
4999     };
5000
5001     function prev(n){
5002         while((n = n.previousSibling) && n.nodeType != 1);
5003         return n;
5004     };
5005
5006     function children(d){
5007         var n = d.firstChild, ni = -1;
5008             while(n){
5009                 var nx = n.nextSibling;
5010                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
5011                     d.removeChild(n);
5012                 }else{
5013                     n.nodeIndex = ++ni;
5014                 }
5015                 n = nx;
5016             }
5017             return this;
5018         };
5019
5020     function byClassName(c, a, v){
5021         if(!v){
5022             return c;
5023         }
5024         var r = [], ri = -1, cn;
5025         for(var i = 0, ci; ci = c[i]; i++){
5026             if((' '+ci.className+' ').indexOf(v) != -1){
5027                 r[++ri] = ci;
5028             }
5029         }
5030         return r;
5031     };
5032
5033     function attrValue(n, attr){
5034         if(!n.tagName && typeof n.length != "undefined"){
5035             n = n[0];
5036         }
5037         if(!n){
5038             return null;
5039         }
5040         if(attr == "for"){
5041             return n.htmlFor;
5042         }
5043         if(attr == "class" || attr == "className"){
5044             return n.className;
5045         }
5046         return n.getAttribute(attr) || n[attr];
5047
5048     };
5049
5050     function getNodes(ns, mode, tagName){
5051         var result = [], ri = -1, cs;
5052         if(!ns){
5053             return result;
5054         }
5055         tagName = tagName || "*";
5056         if(typeof ns.getElementsByTagName != "undefined"){
5057             ns = [ns];
5058         }
5059         if(!mode){
5060             for(var i = 0, ni; ni = ns[i]; i++){
5061                 cs = ni.getElementsByTagName(tagName);
5062                 for(var j = 0, ci; ci = cs[j]; j++){
5063                     result[++ri] = ci;
5064                 }
5065             }
5066         }else if(mode == "/" || mode == ">"){
5067             var utag = tagName.toUpperCase();
5068             for(var i = 0, ni, cn; ni = ns[i]; i++){
5069                 cn = ni.children || ni.childNodes;
5070                 for(var j = 0, cj; cj = cn[j]; j++){
5071                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5072                         result[++ri] = cj;
5073                     }
5074                 }
5075             }
5076         }else if(mode == "+"){
5077             var utag = tagName.toUpperCase();
5078             for(var i = 0, n; n = ns[i]; i++){
5079                 while((n = n.nextSibling) && n.nodeType != 1);
5080                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5081                     result[++ri] = n;
5082                 }
5083             }
5084         }else if(mode == "~"){
5085             for(var i = 0, n; n = ns[i]; i++){
5086                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5087                 if(n){
5088                     result[++ri] = n;
5089                 }
5090             }
5091         }
5092         return result;
5093     };
5094
5095     function concat(a, b){
5096         if(b.slice){
5097             return a.concat(b);
5098         }
5099         for(var i = 0, l = b.length; i < l; i++){
5100             a[a.length] = b[i];
5101         }
5102         return a;
5103     }
5104
5105     function byTag(cs, tagName){
5106         if(cs.tagName || cs == document){
5107             cs = [cs];
5108         }
5109         if(!tagName){
5110             return cs;
5111         }
5112         var r = [], ri = -1;
5113         tagName = tagName.toLowerCase();
5114         for(var i = 0, ci; ci = cs[i]; i++){
5115             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5116                 r[++ri] = ci;
5117             }
5118         }
5119         return r;
5120     };
5121
5122     function byId(cs, attr, id){
5123         if(cs.tagName || cs == document){
5124             cs = [cs];
5125         }
5126         if(!id){
5127             return cs;
5128         }
5129         var r = [], ri = -1;
5130         for(var i = 0,ci; ci = cs[i]; i++){
5131             if(ci && ci.id == id){
5132                 r[++ri] = ci;
5133                 return r;
5134             }
5135         }
5136         return r;
5137     };
5138
5139     function byAttribute(cs, attr, value, op, custom){
5140         var r = [], ri = -1, st = custom=="{";
5141         var f = Roo.DomQuery.operators[op];
5142         for(var i = 0, ci; ci = cs[i]; i++){
5143             var a;
5144             if(st){
5145                 a = Roo.DomQuery.getStyle(ci, attr);
5146             }
5147             else if(attr == "class" || attr == "className"){
5148                 a = ci.className;
5149             }else if(attr == "for"){
5150                 a = ci.htmlFor;
5151             }else if(attr == "href"){
5152                 a = ci.getAttribute("href", 2);
5153             }else{
5154                 a = ci.getAttribute(attr);
5155             }
5156             if((f && f(a, value)) || (!f && a)){
5157                 r[++ri] = ci;
5158             }
5159         }
5160         return r;
5161     };
5162
5163     function byPseudo(cs, name, value){
5164         return Roo.DomQuery.pseudos[name](cs, value);
5165     };
5166
5167     // This is for IE MSXML which does not support expandos.
5168     // IE runs the same speed using setAttribute, however FF slows way down
5169     // and Safari completely fails so they need to continue to use expandos.
5170     var isIE = window.ActiveXObject ? true : false;
5171
5172     // this eval is stop the compressor from
5173     // renaming the variable to something shorter
5174     
5175     /** eval:var:batch */
5176     var batch = 30803; 
5177
5178     var key = 30803;
5179
5180     function nodupIEXml(cs){
5181         var d = ++key;
5182         cs[0].setAttribute("_nodup", d);
5183         var r = [cs[0]];
5184         for(var i = 1, len = cs.length; i < len; i++){
5185             var c = cs[i];
5186             if(!c.getAttribute("_nodup") != d){
5187                 c.setAttribute("_nodup", d);
5188                 r[r.length] = c;
5189             }
5190         }
5191         for(var i = 0, len = cs.length; i < len; i++){
5192             cs[i].removeAttribute("_nodup");
5193         }
5194         return r;
5195     }
5196
5197     function nodup(cs){
5198         if(!cs){
5199             return [];
5200         }
5201         var len = cs.length, c, i, r = cs, cj, ri = -1;
5202         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5203             return cs;
5204         }
5205         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5206             return nodupIEXml(cs);
5207         }
5208         var d = ++key;
5209         cs[0]._nodup = d;
5210         for(i = 1; c = cs[i]; i++){
5211             if(c._nodup != d){
5212                 c._nodup = d;
5213             }else{
5214                 r = [];
5215                 for(var j = 0; j < i; j++){
5216                     r[++ri] = cs[j];
5217                 }
5218                 for(j = i+1; cj = cs[j]; j++){
5219                     if(cj._nodup != d){
5220                         cj._nodup = d;
5221                         r[++ri] = cj;
5222                     }
5223                 }
5224                 return r;
5225             }
5226         }
5227         return r;
5228     }
5229
5230     function quickDiffIEXml(c1, c2){
5231         var d = ++key;
5232         for(var i = 0, len = c1.length; i < len; i++){
5233             c1[i].setAttribute("_qdiff", d);
5234         }
5235         var r = [];
5236         for(var i = 0, len = c2.length; i < len; i++){
5237             if(c2[i].getAttribute("_qdiff") != d){
5238                 r[r.length] = c2[i];
5239             }
5240         }
5241         for(var i = 0, len = c1.length; i < len; i++){
5242            c1[i].removeAttribute("_qdiff");
5243         }
5244         return r;
5245     }
5246
5247     function quickDiff(c1, c2){
5248         var len1 = c1.length;
5249         if(!len1){
5250             return c2;
5251         }
5252         if(isIE && c1[0].selectSingleNode){
5253             return quickDiffIEXml(c1, c2);
5254         }
5255         var d = ++key;
5256         for(var i = 0; i < len1; i++){
5257             c1[i]._qdiff = d;
5258         }
5259         var r = [];
5260         for(var i = 0, len = c2.length; i < len; i++){
5261             if(c2[i]._qdiff != d){
5262                 r[r.length] = c2[i];
5263             }
5264         }
5265         return r;
5266     }
5267
5268     function quickId(ns, mode, root, id){
5269         if(ns == root){
5270            var d = root.ownerDocument || root;
5271            return d.getElementById(id);
5272         }
5273         ns = getNodes(ns, mode, "*");
5274         return byId(ns, null, id);
5275     }
5276
5277     return {
5278         getStyle : function(el, name){
5279             return Roo.fly(el).getStyle(name);
5280         },
5281         /**
5282          * Compiles a selector/xpath query into a reusable function. The returned function
5283          * takes one parameter "root" (optional), which is the context node from where the query should start.
5284          * @param {String} selector The selector/xpath query
5285          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5286          * @return {Function}
5287          */
5288         compile : function(path, type){
5289             type = type || "select";
5290             
5291             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5292             var q = path, mode, lq;
5293             var tk = Roo.DomQuery.matchers;
5294             var tklen = tk.length;
5295             var mm;
5296
5297             // accept leading mode switch
5298             var lmode = q.match(modeRe);
5299             if(lmode && lmode[1]){
5300                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5301                 q = q.replace(lmode[1], "");
5302             }
5303             // strip leading slashes
5304             while(path.substr(0, 1)=="/"){
5305                 path = path.substr(1);
5306             }
5307
5308             while(q && lq != q){
5309                 lq = q;
5310                 var tm = q.match(tagTokenRe);
5311                 if(type == "select"){
5312                     if(tm){
5313                         if(tm[1] == "#"){
5314                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5315                         }else{
5316                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5317                         }
5318                         q = q.replace(tm[0], "");
5319                     }else if(q.substr(0, 1) != '@'){
5320                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5321                     }
5322                 }else{
5323                     if(tm){
5324                         if(tm[1] == "#"){
5325                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5326                         }else{
5327                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5328                         }
5329                         q = q.replace(tm[0], "");
5330                     }
5331                 }
5332                 while(!(mm = q.match(modeRe))){
5333                     var matched = false;
5334                     for(var j = 0; j < tklen; j++){
5335                         var t = tk[j];
5336                         var m = q.match(t.re);
5337                         if(m){
5338                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5339                                                     return m[i];
5340                                                 });
5341                             q = q.replace(m[0], "");
5342                             matched = true;
5343                             break;
5344                         }
5345                     }
5346                     // prevent infinite loop on bad selector
5347                     if(!matched){
5348                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5349                     }
5350                 }
5351                 if(mm[1]){
5352                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5353                     q = q.replace(mm[1], "");
5354                 }
5355             }
5356             fn[fn.length] = "return nodup(n);\n}";
5357             
5358              /** 
5359               * list of variables that need from compression as they are used by eval.
5360              *  eval:var:batch 
5361              *  eval:var:nodup
5362              *  eval:var:byTag
5363              *  eval:var:ById
5364              *  eval:var:getNodes
5365              *  eval:var:quickId
5366              *  eval:var:mode
5367              *  eval:var:root
5368              *  eval:var:n
5369              *  eval:var:byClassName
5370              *  eval:var:byPseudo
5371              *  eval:var:byAttribute
5372              *  eval:var:attrValue
5373              * 
5374              **/ 
5375             eval(fn.join(""));
5376             return f;
5377         },
5378
5379         /**
5380          * Selects a group of elements.
5381          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5382          * @param {Node} root (optional) The start of the query (defaults to document).
5383          * @return {Array}
5384          */
5385         select : function(path, root, type){
5386             if(!root || root == document){
5387                 root = document;
5388             }
5389             if(typeof root == "string"){
5390                 root = document.getElementById(root);
5391             }
5392             var paths = path.split(",");
5393             var results = [];
5394             for(var i = 0, len = paths.length; i < len; i++){
5395                 var p = paths[i].replace(trimRe, "");
5396                 if(!cache[p]){
5397                     cache[p] = Roo.DomQuery.compile(p);
5398                     if(!cache[p]){
5399                         throw p + " is not a valid selector";
5400                     }
5401                 }
5402                 var result = cache[p](root);
5403                 if(result && result != document){
5404                     results = results.concat(result);
5405                 }
5406             }
5407             if(paths.length > 1){
5408                 return nodup(results);
5409             }
5410             return results;
5411         },
5412
5413         /**
5414          * Selects a single element.
5415          * @param {String} selector The selector/xpath query
5416          * @param {Node} root (optional) The start of the query (defaults to document).
5417          * @return {Element}
5418          */
5419         selectNode : function(path, root){
5420             return Roo.DomQuery.select(path, root)[0];
5421         },
5422
5423         /**
5424          * Selects the value of a node, optionally replacing null with the defaultValue.
5425          * @param {String} selector The selector/xpath query
5426          * @param {Node} root (optional) The start of the query (defaults to document).
5427          * @param {String} defaultValue
5428          */
5429         selectValue : function(path, root, defaultValue){
5430             path = path.replace(trimRe, "");
5431             if(!valueCache[path]){
5432                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5433             }
5434             var n = valueCache[path](root);
5435             n = n[0] ? n[0] : n;
5436             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5437             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5438         },
5439
5440         /**
5441          * Selects the value of a node, parsing integers and floats.
5442          * @param {String} selector The selector/xpath query
5443          * @param {Node} root (optional) The start of the query (defaults to document).
5444          * @param {Number} defaultValue
5445          * @return {Number}
5446          */
5447         selectNumber : function(path, root, defaultValue){
5448             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5449             return parseFloat(v);
5450         },
5451
5452         /**
5453          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5454          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5455          * @param {String} selector The simple selector to test
5456          * @return {Boolean}
5457          */
5458         is : function(el, ss){
5459             if(typeof el == "string"){
5460                 el = document.getElementById(el);
5461             }
5462             var isArray = (el instanceof Array);
5463             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5464             return isArray ? (result.length == el.length) : (result.length > 0);
5465         },
5466
5467         /**
5468          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5469          * @param {Array} el An array of elements to filter
5470          * @param {String} selector The simple selector to test
5471          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5472          * the selector instead of the ones that match
5473          * @return {Array}
5474          */
5475         filter : function(els, ss, nonMatches){
5476             ss = ss.replace(trimRe, "");
5477             if(!simpleCache[ss]){
5478                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5479             }
5480             var result = simpleCache[ss](els);
5481             return nonMatches ? quickDiff(result, els) : result;
5482         },
5483
5484         /**
5485          * Collection of matching regular expressions and code snippets.
5486          */
5487         matchers : [{
5488                 re: /^\.([\w-]+)/,
5489                 select: 'n = byClassName(n, null, " {1} ");'
5490             }, {
5491                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5492                 select: 'n = byPseudo(n, "{1}", "{2}");'
5493             },{
5494                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5495                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5496             }, {
5497                 re: /^#([\w-]+)/,
5498                 select: 'n = byId(n, null, "{1}");'
5499             },{
5500                 re: /^@([\w-]+)/,
5501                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5502             }
5503         ],
5504
5505         /**
5506          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5507          * 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;.
5508          */
5509         operators : {
5510             "=" : function(a, v){
5511                 return a == v;
5512             },
5513             "!=" : function(a, v){
5514                 return a != v;
5515             },
5516             "^=" : function(a, v){
5517                 return a && a.substr(0, v.length) == v;
5518             },
5519             "$=" : function(a, v){
5520                 return a && a.substr(a.length-v.length) == v;
5521             },
5522             "*=" : function(a, v){
5523                 return a && a.indexOf(v) !== -1;
5524             },
5525             "%=" : function(a, v){
5526                 return (a % v) == 0;
5527             },
5528             "|=" : function(a, v){
5529                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5530             },
5531             "~=" : function(a, v){
5532                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5533             }
5534         },
5535
5536         /**
5537          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5538          * and the argument (if any) supplied in the selector.
5539          */
5540         pseudos : {
5541             "first-child" : function(c){
5542                 var r = [], ri = -1, n;
5543                 for(var i = 0, ci; ci = n = c[i]; i++){
5544                     while((n = n.previousSibling) && n.nodeType != 1);
5545                     if(!n){
5546                         r[++ri] = ci;
5547                     }
5548                 }
5549                 return r;
5550             },
5551
5552             "last-child" : function(c){
5553                 var r = [], ri = -1, n;
5554                 for(var i = 0, ci; ci = n = c[i]; i++){
5555                     while((n = n.nextSibling) && n.nodeType != 1);
5556                     if(!n){
5557                         r[++ri] = ci;
5558                     }
5559                 }
5560                 return r;
5561             },
5562
5563             "nth-child" : function(c, a) {
5564                 var r = [], ri = -1;
5565                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5566                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5567                 for(var i = 0, n; n = c[i]; i++){
5568                     var pn = n.parentNode;
5569                     if (batch != pn._batch) {
5570                         var j = 0;
5571                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5572                             if(cn.nodeType == 1){
5573                                cn.nodeIndex = ++j;
5574                             }
5575                         }
5576                         pn._batch = batch;
5577                     }
5578                     if (f == 1) {
5579                         if (l == 0 || n.nodeIndex == l){
5580                             r[++ri] = n;
5581                         }
5582                     } else if ((n.nodeIndex + l) % f == 0){
5583                         r[++ri] = n;
5584                     }
5585                 }
5586
5587                 return r;
5588             },
5589
5590             "only-child" : function(c){
5591                 var r = [], ri = -1;;
5592                 for(var i = 0, ci; ci = c[i]; i++){
5593                     if(!prev(ci) && !next(ci)){
5594                         r[++ri] = ci;
5595                     }
5596                 }
5597                 return r;
5598             },
5599
5600             "empty" : function(c){
5601                 var r = [], ri = -1;
5602                 for(var i = 0, ci; ci = c[i]; i++){
5603                     var cns = ci.childNodes, j = 0, cn, empty = true;
5604                     while(cn = cns[j]){
5605                         ++j;
5606                         if(cn.nodeType == 1 || cn.nodeType == 3){
5607                             empty = false;
5608                             break;
5609                         }
5610                     }
5611                     if(empty){
5612                         r[++ri] = ci;
5613                     }
5614                 }
5615                 return r;
5616             },
5617
5618             "contains" : function(c, v){
5619                 var r = [], ri = -1;
5620                 for(var i = 0, ci; ci = c[i]; i++){
5621                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5622                         r[++ri] = ci;
5623                     }
5624                 }
5625                 return r;
5626             },
5627
5628             "nodeValue" : function(c, v){
5629                 var r = [], ri = -1;
5630                 for(var i = 0, ci; ci = c[i]; i++){
5631                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5632                         r[++ri] = ci;
5633                     }
5634                 }
5635                 return r;
5636             },
5637
5638             "checked" : function(c){
5639                 var r = [], ri = -1;
5640                 for(var i = 0, ci; ci = c[i]; i++){
5641                     if(ci.checked == true){
5642                         r[++ri] = ci;
5643                     }
5644                 }
5645                 return r;
5646             },
5647
5648             "not" : function(c, ss){
5649                 return Roo.DomQuery.filter(c, ss, true);
5650             },
5651
5652             "odd" : function(c){
5653                 return this["nth-child"](c, "odd");
5654             },
5655
5656             "even" : function(c){
5657                 return this["nth-child"](c, "even");
5658             },
5659
5660             "nth" : function(c, a){
5661                 return c[a-1] || [];
5662             },
5663
5664             "first" : function(c){
5665                 return c[0] || [];
5666             },
5667
5668             "last" : function(c){
5669                 return c[c.length-1] || [];
5670             },
5671
5672             "has" : function(c, ss){
5673                 var s = Roo.DomQuery.select;
5674                 var r = [], ri = -1;
5675                 for(var i = 0, ci; ci = c[i]; i++){
5676                     if(s(ss, ci).length > 0){
5677                         r[++ri] = ci;
5678                     }
5679                 }
5680                 return r;
5681             },
5682
5683             "next" : function(c, ss){
5684                 var is = Roo.DomQuery.is;
5685                 var r = [], ri = -1;
5686                 for(var i = 0, ci; ci = c[i]; i++){
5687                     var n = next(ci);
5688                     if(n && is(n, ss)){
5689                         r[++ri] = ci;
5690                     }
5691                 }
5692                 return r;
5693             },
5694
5695             "prev" : function(c, ss){
5696                 var is = Roo.DomQuery.is;
5697                 var r = [], ri = -1;
5698                 for(var i = 0, ci; ci = c[i]; i++){
5699                     var n = prev(ci);
5700                     if(n && is(n, ss)){
5701                         r[++ri] = ci;
5702                     }
5703                 }
5704                 return r;
5705             }
5706         }
5707     };
5708 }();
5709
5710 /**
5711  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5712  * @param {String} path The selector/xpath query
5713  * @param {Node} root (optional) The start of the query (defaults to document).
5714  * @return {Array}
5715  * @member Roo
5716  * @method query
5717  */
5718 Roo.query = Roo.DomQuery.select;
5719 /*
5720  * Based on:
5721  * Ext JS Library 1.1.1
5722  * Copyright(c) 2006-2007, Ext JS, LLC.
5723  *
5724  * Originally Released Under LGPL - original licence link has changed is not relivant.
5725  *
5726  * Fork - LGPL
5727  * <script type="text/javascript">
5728  */
5729
5730 /**
5731  * @class Roo.util.Observable
5732  * Base class that provides a common interface for publishing events. Subclasses are expected to
5733  * to have a property "events" with all the events defined.<br>
5734  * For example:
5735  * <pre><code>
5736  Employee = function(name){
5737     this.name = name;
5738     this.addEvents({
5739         "fired" : true,
5740         "quit" : true
5741     });
5742  }
5743  Roo.extend(Employee, Roo.util.Observable);
5744 </code></pre>
5745  * @param {Object} config properties to use (incuding events / listeners)
5746  */
5747
5748 Roo.util.Observable = function(cfg){
5749     
5750     cfg = cfg|| {};
5751     this.addEvents(cfg.events || {});
5752     if (cfg.events) {
5753         delete cfg.events; // make sure
5754     }
5755      
5756     Roo.apply(this, cfg);
5757     
5758     if(this.listeners){
5759         this.on(this.listeners);
5760         delete this.listeners;
5761     }
5762 };
5763 Roo.util.Observable.prototype = {
5764     /** 
5765  * @cfg {Object} listeners  list of events and functions to call for this object, 
5766  * For example :
5767  * <pre><code>
5768     listeners :  { 
5769        'click' : function(e) {
5770            ..... 
5771         } ,
5772         .... 
5773     } 
5774   </code></pre>
5775  */
5776     
5777     
5778     /**
5779      * Fires the specified event with the passed parameters (minus the event name).
5780      * @param {String} eventName
5781      * @param {Object...} args Variable number of parameters are passed to handlers
5782      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5783      */
5784     fireEvent : function(){
5785         var ce = this.events[arguments[0].toLowerCase()];
5786         if(typeof ce == "object"){
5787             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5788         }else{
5789             return true;
5790         }
5791     },
5792
5793     // private
5794     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5795
5796     /**
5797      * Appends an event handler to this component
5798      * @param {String}   eventName The type of event to listen for
5799      * @param {Function} handler The method the event invokes
5800      * @param {Object}   scope (optional) The scope in which to execute the handler
5801      * function. The handler function's "this" context.
5802      * @param {Object}   options (optional) An object containing handler configuration
5803      * properties. This may contain any of the following properties:<ul>
5804      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5805      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5806      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5807      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5808      * by the specified number of milliseconds. If the event fires again within that time, the original
5809      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5810      * </ul><br>
5811      * <p>
5812      * <b>Combining Options</b><br>
5813      * Using the options argument, it is possible to combine different types of listeners:<br>
5814      * <br>
5815      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5816                 <pre><code>
5817                 el.on('click', this.onClick, this, {
5818                         single: true,
5819                 delay: 100,
5820                 forumId: 4
5821                 });
5822                 </code></pre>
5823      * <p>
5824      * <b>Attaching multiple handlers in 1 call</b><br>
5825      * The method also allows for a single argument to be passed which is a config object containing properties
5826      * which specify multiple handlers.
5827      * <pre><code>
5828                 el.on({
5829                         'click': {
5830                         fn: this.onClick,
5831                         scope: this,
5832                         delay: 100
5833                 }, 
5834                 'mouseover': {
5835                         fn: this.onMouseOver,
5836                         scope: this
5837                 },
5838                 'mouseout': {
5839                         fn: this.onMouseOut,
5840                         scope: this
5841                 }
5842                 });
5843                 </code></pre>
5844      * <p>
5845      * Or a shorthand syntax which passes the same scope object to all handlers:
5846         <pre><code>
5847                 el.on({
5848                         'click': this.onClick,
5849                 'mouseover': this.onMouseOver,
5850                 'mouseout': this.onMouseOut,
5851                 scope: this
5852                 });
5853                 </code></pre>
5854      */
5855     addListener : function(eventName, fn, scope, o){
5856         if(typeof eventName == "object"){
5857             o = eventName;
5858             for(var e in o){
5859                 if(this.filterOptRe.test(e)){
5860                     continue;
5861                 }
5862                 if(typeof o[e] == "function"){
5863                     // shared options
5864                     this.addListener(e, o[e], o.scope,  o);
5865                 }else{
5866                     // individual options
5867                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5868                 }
5869             }
5870             return;
5871         }
5872         o = (!o || typeof o == "boolean") ? {} : o;
5873         eventName = eventName.toLowerCase();
5874         var ce = this.events[eventName] || true;
5875         if(typeof ce == "boolean"){
5876             ce = new Roo.util.Event(this, eventName);
5877             this.events[eventName] = ce;
5878         }
5879         ce.addListener(fn, scope, o);
5880     },
5881
5882     /**
5883      * Removes a listener
5884      * @param {String}   eventName     The type of event to listen for
5885      * @param {Function} handler        The handler to remove
5886      * @param {Object}   scope  (optional) The scope (this object) for the handler
5887      */
5888     removeListener : function(eventName, fn, scope){
5889         var ce = this.events[eventName.toLowerCase()];
5890         if(typeof ce == "object"){
5891             ce.removeListener(fn, scope);
5892         }
5893     },
5894
5895     /**
5896      * Removes all listeners for this object
5897      */
5898     purgeListeners : function(){
5899         for(var evt in this.events){
5900             if(typeof this.events[evt] == "object"){
5901                  this.events[evt].clearListeners();
5902             }
5903         }
5904     },
5905
5906     relayEvents : function(o, events){
5907         var createHandler = function(ename){
5908             return function(){
5909                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5910             };
5911         };
5912         for(var i = 0, len = events.length; i < len; i++){
5913             var ename = events[i];
5914             if(!this.events[ename]){ this.events[ename] = true; };
5915             o.on(ename, createHandler(ename), this);
5916         }
5917     },
5918
5919     /**
5920      * Used to define events on this Observable
5921      * @param {Object} object The object with the events defined
5922      */
5923     addEvents : function(o){
5924         if(!this.events){
5925             this.events = {};
5926         }
5927         Roo.applyIf(this.events, o);
5928     },
5929
5930     /**
5931      * Checks to see if this object has any listeners for a specified event
5932      * @param {String} eventName The name of the event to check for
5933      * @return {Boolean} True if the event is being listened for, else false
5934      */
5935     hasListener : function(eventName){
5936         var e = this.events[eventName];
5937         return typeof e == "object" && e.listeners.length > 0;
5938     }
5939 };
5940 /**
5941  * Appends an event handler to this element (shorthand for addListener)
5942  * @param {String}   eventName     The type of event to listen for
5943  * @param {Function} handler        The method the event invokes
5944  * @param {Object}   scope (optional) The scope in which to execute the handler
5945  * function. The handler function's "this" context.
5946  * @param {Object}   options  (optional)
5947  * @method
5948  */
5949 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5950 /**
5951  * Removes a listener (shorthand for removeListener)
5952  * @param {String}   eventName     The type of event to listen for
5953  * @param {Function} handler        The handler to remove
5954  * @param {Object}   scope  (optional) The scope (this object) for the handler
5955  * @method
5956  */
5957 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5958
5959 /**
5960  * Starts capture on the specified Observable. All events will be passed
5961  * to the supplied function with the event name + standard signature of the event
5962  * <b>before</b> the event is fired. If the supplied function returns false,
5963  * the event will not fire.
5964  * @param {Observable} o The Observable to capture
5965  * @param {Function} fn The function to call
5966  * @param {Object} scope (optional) The scope (this object) for the fn
5967  * @static
5968  */
5969 Roo.util.Observable.capture = function(o, fn, scope){
5970     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5971 };
5972
5973 /**
5974  * Removes <b>all</b> added captures from the Observable.
5975  * @param {Observable} o The Observable to release
5976  * @static
5977  */
5978 Roo.util.Observable.releaseCapture = function(o){
5979     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5980 };
5981
5982 (function(){
5983
5984     var createBuffered = function(h, o, scope){
5985         var task = new Roo.util.DelayedTask();
5986         return function(){
5987             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5988         };
5989     };
5990
5991     var createSingle = function(h, e, fn, scope){
5992         return function(){
5993             e.removeListener(fn, scope);
5994             return h.apply(scope, arguments);
5995         };
5996     };
5997
5998     var createDelayed = function(h, o, scope){
5999         return function(){
6000             var args = Array.prototype.slice.call(arguments, 0);
6001             setTimeout(function(){
6002                 h.apply(scope, args);
6003             }, o.delay || 10);
6004         };
6005     };
6006
6007     Roo.util.Event = function(obj, name){
6008         this.name = name;
6009         this.obj = obj;
6010         this.listeners = [];
6011     };
6012
6013     Roo.util.Event.prototype = {
6014         addListener : function(fn, scope, options){
6015             var o = options || {};
6016             scope = scope || this.obj;
6017             if(!this.isListening(fn, scope)){
6018                 var l = {fn: fn, scope: scope, options: o};
6019                 var h = fn;
6020                 if(o.delay){
6021                     h = createDelayed(h, o, scope);
6022                 }
6023                 if(o.single){
6024                     h = createSingle(h, this, fn, scope);
6025                 }
6026                 if(o.buffer){
6027                     h = createBuffered(h, o, scope);
6028                 }
6029                 l.fireFn = h;
6030                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
6031                     this.listeners.push(l);
6032                 }else{
6033                     this.listeners = this.listeners.slice(0);
6034                     this.listeners.push(l);
6035                 }
6036             }
6037         },
6038
6039         findListener : function(fn, scope){
6040             scope = scope || this.obj;
6041             var ls = this.listeners;
6042             for(var i = 0, len = ls.length; i < len; i++){
6043                 var l = ls[i];
6044                 if(l.fn == fn && l.scope == scope){
6045                     return i;
6046                 }
6047             }
6048             return -1;
6049         },
6050
6051         isListening : function(fn, scope){
6052             return this.findListener(fn, scope) != -1;
6053         },
6054
6055         removeListener : function(fn, scope){
6056             var index;
6057             if((index = this.findListener(fn, scope)) != -1){
6058                 if(!this.firing){
6059                     this.listeners.splice(index, 1);
6060                 }else{
6061                     this.listeners = this.listeners.slice(0);
6062                     this.listeners.splice(index, 1);
6063                 }
6064                 return true;
6065             }
6066             return false;
6067         },
6068
6069         clearListeners : function(){
6070             this.listeners = [];
6071         },
6072
6073         fire : function(){
6074             var ls = this.listeners, scope, len = ls.length;
6075             if(len > 0){
6076                 this.firing = true;
6077                 var args = Array.prototype.slice.call(arguments, 0);                
6078                 for(var i = 0; i < len; i++){
6079                     var l = ls[i];
6080                     if(l.fireFn.apply(l.scope||this.obj||window, args) === false){
6081                         this.firing = false;
6082                         return false;
6083                     }
6084                 }
6085                 this.firing = false;
6086             }
6087             return true;
6088         }
6089     };
6090 })();/*
6091  * RooJS Library 
6092  * Copyright(c) 2007-2017, Roo J Solutions Ltd
6093  *
6094  * Licence LGPL 
6095  *
6096  */
6097  
6098 /**
6099  * @class Roo.Document
6100  * @extends Roo.util.Observable
6101  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
6102  * 
6103  * @param {Object} config the methods and properties of the 'base' class for the application.
6104  * 
6105  *  Generic Page handler - implement this to start your app..
6106  * 
6107  * eg.
6108  *  MyProject = new Roo.Document({
6109         events : {
6110             'load' : true // your events..
6111         },
6112         listeners : {
6113             'ready' : function() {
6114                 // fired on Roo.onReady()
6115             }
6116         }
6117  * 
6118  */
6119 Roo.Document = function(cfg) {
6120      
6121     this.addEvents({ 
6122         'ready' : true
6123     });
6124     Roo.util.Observable.call(this,cfg);
6125     
6126     var _this = this;
6127     
6128     Roo.onReady(function() {
6129         _this.fireEvent('ready');
6130     },null,false);
6131     
6132     
6133 }
6134
6135 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
6136  * Based on:
6137  * Ext JS Library 1.1.1
6138  * Copyright(c) 2006-2007, Ext JS, LLC.
6139  *
6140  * Originally Released Under LGPL - original licence link has changed is not relivant.
6141  *
6142  * Fork - LGPL
6143  * <script type="text/javascript">
6144  */
6145
6146 /**
6147  * @class Roo.EventManager
6148  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6149  * several useful events directly.
6150  * See {@link Roo.EventObject} for more details on normalized event objects.
6151  * @singleton
6152  */
6153 Roo.EventManager = function(){
6154     var docReadyEvent, docReadyProcId, docReadyState = false;
6155     var resizeEvent, resizeTask, textEvent, textSize;
6156     var E = Roo.lib.Event;
6157     var D = Roo.lib.Dom;
6158
6159     
6160     
6161
6162     var fireDocReady = function(){
6163         if(!docReadyState){
6164             docReadyState = true;
6165             Roo.isReady = true;
6166             if(docReadyProcId){
6167                 clearInterval(docReadyProcId);
6168             }
6169             if(Roo.isGecko || Roo.isOpera) {
6170                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6171             }
6172             if(Roo.isIE){
6173                 var defer = document.getElementById("ie-deferred-loader");
6174                 if(defer){
6175                     defer.onreadystatechange = null;
6176                     defer.parentNode.removeChild(defer);
6177                 }
6178             }
6179             if(docReadyEvent){
6180                 docReadyEvent.fire();
6181                 docReadyEvent.clearListeners();
6182             }
6183         }
6184     };
6185     
6186     var initDocReady = function(){
6187         docReadyEvent = new Roo.util.Event();
6188         if(Roo.isGecko || Roo.isOpera) {
6189             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6190         }else if(Roo.isIE){
6191             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6192             var defer = document.getElementById("ie-deferred-loader");
6193             defer.onreadystatechange = function(){
6194                 if(this.readyState == "complete"){
6195                     fireDocReady();
6196                 }
6197             };
6198         }else if(Roo.isSafari){ 
6199             docReadyProcId = setInterval(function(){
6200                 var rs = document.readyState;
6201                 if(rs == "complete") {
6202                     fireDocReady();     
6203                  }
6204             }, 10);
6205         }
6206         // no matter what, make sure it fires on load
6207         E.on(window, "load", fireDocReady);
6208     };
6209
6210     var createBuffered = function(h, o){
6211         var task = new Roo.util.DelayedTask(h);
6212         return function(e){
6213             // create new event object impl so new events don't wipe out properties
6214             e = new Roo.EventObjectImpl(e);
6215             task.delay(o.buffer, h, null, [e]);
6216         };
6217     };
6218
6219     var createSingle = function(h, el, ename, fn){
6220         return function(e){
6221             Roo.EventManager.removeListener(el, ename, fn);
6222             h(e);
6223         };
6224     };
6225
6226     var createDelayed = function(h, o){
6227         return function(e){
6228             // create new event object impl so new events don't wipe out properties
6229             e = new Roo.EventObjectImpl(e);
6230             setTimeout(function(){
6231                 h(e);
6232             }, o.delay || 10);
6233         };
6234     };
6235     var transitionEndVal = false;
6236     
6237     var transitionEnd = function()
6238     {
6239         if (transitionEndVal) {
6240             return transitionEndVal;
6241         }
6242         var el = document.createElement('div');
6243
6244         var transEndEventNames = {
6245             WebkitTransition : 'webkitTransitionEnd',
6246             MozTransition    : 'transitionend',
6247             OTransition      : 'oTransitionEnd otransitionend',
6248             transition       : 'transitionend'
6249         };
6250     
6251         for (var name in transEndEventNames) {
6252             if (el.style[name] !== undefined) {
6253                 transitionEndVal = transEndEventNames[name];
6254                 return  transitionEndVal ;
6255             }
6256         }
6257     }
6258     
6259
6260     var listen = function(element, ename, opt, fn, scope){
6261         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6262         fn = fn || o.fn; scope = scope || o.scope;
6263         var el = Roo.getDom(element);
6264         
6265         
6266         if(!el){
6267             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6268         }
6269         
6270         if (ename == 'transitionend') {
6271             ename = transitionEnd();
6272         }
6273         var h = function(e){
6274             e = Roo.EventObject.setEvent(e);
6275             var t;
6276             if(o.delegate){
6277                 t = e.getTarget(o.delegate, el);
6278                 if(!t){
6279                     return;
6280                 }
6281             }else{
6282                 t = e.target;
6283             }
6284             if(o.stopEvent === true){
6285                 e.stopEvent();
6286             }
6287             if(o.preventDefault === true){
6288                e.preventDefault();
6289             }
6290             if(o.stopPropagation === true){
6291                 e.stopPropagation();
6292             }
6293
6294             if(o.normalized === false){
6295                 e = e.browserEvent;
6296             }
6297
6298             fn.call(scope || el, e, t, o);
6299         };
6300         if(o.delay){
6301             h = createDelayed(h, o);
6302         }
6303         if(o.single){
6304             h = createSingle(h, el, ename, fn);
6305         }
6306         if(o.buffer){
6307             h = createBuffered(h, o);
6308         }
6309         
6310         fn._handlers = fn._handlers || [];
6311         
6312         
6313         fn._handlers.push([Roo.id(el), ename, h]);
6314         
6315         
6316          
6317         E.on(el, ename, h);
6318         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6319             el.addEventListener("DOMMouseScroll", h, false);
6320             E.on(window, 'unload', function(){
6321                 el.removeEventListener("DOMMouseScroll", h, false);
6322             });
6323         }
6324         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6325             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6326         }
6327         return h;
6328     };
6329
6330     var stopListening = function(el, ename, fn){
6331         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6332         if(hds){
6333             for(var i = 0, len = hds.length; i < len; i++){
6334                 var h = hds[i];
6335                 if(h[0] == id && h[1] == ename){
6336                     hd = h[2];
6337                     hds.splice(i, 1);
6338                     break;
6339                 }
6340             }
6341         }
6342         E.un(el, ename, hd);
6343         el = Roo.getDom(el);
6344         if(ename == "mousewheel" && el.addEventListener){
6345             el.removeEventListener("DOMMouseScroll", hd, false);
6346         }
6347         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6348             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6349         }
6350     };
6351
6352     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6353     
6354     var pub = {
6355         
6356         
6357         /** 
6358          * Fix for doc tools
6359          * @scope Roo.EventManager
6360          */
6361         
6362         
6363         /** 
6364          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6365          * object with a Roo.EventObject
6366          * @param {Function} fn        The method the event invokes
6367          * @param {Object}   scope    An object that becomes the scope of the handler
6368          * @param {boolean}  override If true, the obj passed in becomes
6369          *                             the execution scope of the listener
6370          * @return {Function} The wrapped function
6371          * @deprecated
6372          */
6373         wrap : function(fn, scope, override){
6374             return function(e){
6375                 Roo.EventObject.setEvent(e);
6376                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6377             };
6378         },
6379         
6380         /**
6381      * Appends an event handler to an element (shorthand for addListener)
6382      * @param {String/HTMLElement}   element        The html element or id to assign the
6383      * @param {String}   eventName The type of event to listen for
6384      * @param {Function} handler The method the event invokes
6385      * @param {Object}   scope (optional) The scope in which to execute the handler
6386      * function. The handler function's "this" context.
6387      * @param {Object}   options (optional) An object containing handler configuration
6388      * properties. This may contain any of the following properties:<ul>
6389      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6390      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6391      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6392      * <li>preventDefault {Boolean} True to prevent the default action</li>
6393      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6394      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6395      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6396      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6397      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6398      * by the specified number of milliseconds. If the event fires again within that time, the original
6399      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6400      * </ul><br>
6401      * <p>
6402      * <b>Combining Options</b><br>
6403      * Using the options argument, it is possible to combine different types of listeners:<br>
6404      * <br>
6405      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6406      * Code:<pre><code>
6407 el.on('click', this.onClick, this, {
6408     single: true,
6409     delay: 100,
6410     stopEvent : true,
6411     forumId: 4
6412 });</code></pre>
6413      * <p>
6414      * <b>Attaching multiple handlers in 1 call</b><br>
6415       * The method also allows for a single argument to be passed which is a config object containing properties
6416      * which specify multiple handlers.
6417      * <p>
6418      * Code:<pre><code>
6419 el.on({
6420     'click' : {
6421         fn: this.onClick
6422         scope: this,
6423         delay: 100
6424     },
6425     'mouseover' : {
6426         fn: this.onMouseOver
6427         scope: this
6428     },
6429     'mouseout' : {
6430         fn: this.onMouseOut
6431         scope: this
6432     }
6433 });</code></pre>
6434      * <p>
6435      * Or a shorthand syntax:<br>
6436      * Code:<pre><code>
6437 el.on({
6438     'click' : this.onClick,
6439     'mouseover' : this.onMouseOver,
6440     'mouseout' : this.onMouseOut
6441     scope: this
6442 });</code></pre>
6443      */
6444         addListener : function(element, eventName, fn, scope, options){
6445             if(typeof eventName == "object"){
6446                 var o = eventName;
6447                 for(var e in o){
6448                     if(propRe.test(e)){
6449                         continue;
6450                     }
6451                     if(typeof o[e] == "function"){
6452                         // shared options
6453                         listen(element, e, o, o[e], o.scope);
6454                     }else{
6455                         // individual options
6456                         listen(element, e, o[e]);
6457                     }
6458                 }
6459                 return;
6460             }
6461             return listen(element, eventName, options, fn, scope);
6462         },
6463         
6464         /**
6465          * Removes an event handler
6466          *
6467          * @param {String/HTMLElement}   element        The id or html element to remove the 
6468          *                             event from
6469          * @param {String}   eventName     The type of event
6470          * @param {Function} fn
6471          * @return {Boolean} True if a listener was actually removed
6472          */
6473         removeListener : function(element, eventName, fn){
6474             return stopListening(element, eventName, fn);
6475         },
6476         
6477         /**
6478          * Fires when the document is ready (before onload and before images are loaded). Can be 
6479          * accessed shorthanded Roo.onReady().
6480          * @param {Function} fn        The method the event invokes
6481          * @param {Object}   scope    An  object that becomes the scope of the handler
6482          * @param {boolean}  options
6483          */
6484         onDocumentReady : function(fn, scope, options){
6485             if(docReadyState){ // if it already fired
6486                 docReadyEvent.addListener(fn, scope, options);
6487                 docReadyEvent.fire();
6488                 docReadyEvent.clearListeners();
6489                 return;
6490             }
6491             if(!docReadyEvent){
6492                 initDocReady();
6493             }
6494             docReadyEvent.addListener(fn, scope, options);
6495         },
6496         
6497         /**
6498          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6499          * @param {Function} fn        The method the event invokes
6500          * @param {Object}   scope    An object that becomes the scope of the handler
6501          * @param {boolean}  options
6502          */
6503         onWindowResize : function(fn, scope, options){
6504             if(!resizeEvent){
6505                 resizeEvent = new Roo.util.Event();
6506                 resizeTask = new Roo.util.DelayedTask(function(){
6507                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6508                 });
6509                 E.on(window, "resize", function(){
6510                     if(Roo.isIE){
6511                         resizeTask.delay(50);
6512                     }else{
6513                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6514                     }
6515                 });
6516             }
6517             resizeEvent.addListener(fn, scope, options);
6518         },
6519
6520         /**
6521          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6522          * @param {Function} fn        The method the event invokes
6523          * @param {Object}   scope    An object that becomes the scope of the handler
6524          * @param {boolean}  options
6525          */
6526         onTextResize : function(fn, scope, options){
6527             if(!textEvent){
6528                 textEvent = new Roo.util.Event();
6529                 var textEl = new Roo.Element(document.createElement('div'));
6530                 textEl.dom.className = 'x-text-resize';
6531                 textEl.dom.innerHTML = 'X';
6532                 textEl.appendTo(document.body);
6533                 textSize = textEl.dom.offsetHeight;
6534                 setInterval(function(){
6535                     if(textEl.dom.offsetHeight != textSize){
6536                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6537                     }
6538                 }, this.textResizeInterval);
6539             }
6540             textEvent.addListener(fn, scope, options);
6541         },
6542
6543         /**
6544          * Removes the passed window resize listener.
6545          * @param {Function} fn        The method the event invokes
6546          * @param {Object}   scope    The scope of handler
6547          */
6548         removeResizeListener : function(fn, scope){
6549             if(resizeEvent){
6550                 resizeEvent.removeListener(fn, scope);
6551             }
6552         },
6553
6554         // private
6555         fireResize : function(){
6556             if(resizeEvent){
6557                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6558             }   
6559         },
6560         /**
6561          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6562          */
6563         ieDeferSrc : false,
6564         /**
6565          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6566          */
6567         textResizeInterval : 50
6568     };
6569     
6570     /**
6571      * Fix for doc tools
6572      * @scopeAlias pub=Roo.EventManager
6573      */
6574     
6575      /**
6576      * Appends an event handler to an element (shorthand for addListener)
6577      * @param {String/HTMLElement}   element        The html element or id to assign the
6578      * @param {String}   eventName The type of event to listen for
6579      * @param {Function} handler The method the event invokes
6580      * @param {Object}   scope (optional) The scope in which to execute the handler
6581      * function. The handler function's "this" context.
6582      * @param {Object}   options (optional) An object containing handler configuration
6583      * properties. This may contain any of the following properties:<ul>
6584      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6585      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6586      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6587      * <li>preventDefault {Boolean} True to prevent the default action</li>
6588      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6589      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6590      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6591      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6592      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6593      * by the specified number of milliseconds. If the event fires again within that time, the original
6594      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6595      * </ul><br>
6596      * <p>
6597      * <b>Combining Options</b><br>
6598      * Using the options argument, it is possible to combine different types of listeners:<br>
6599      * <br>
6600      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6601      * Code:<pre><code>
6602 el.on('click', this.onClick, this, {
6603     single: true,
6604     delay: 100,
6605     stopEvent : true,
6606     forumId: 4
6607 });</code></pre>
6608      * <p>
6609      * <b>Attaching multiple handlers in 1 call</b><br>
6610       * The method also allows for a single argument to be passed which is a config object containing properties
6611      * which specify multiple handlers.
6612      * <p>
6613      * Code:<pre><code>
6614 el.on({
6615     'click' : {
6616         fn: this.onClick
6617         scope: this,
6618         delay: 100
6619     },
6620     'mouseover' : {
6621         fn: this.onMouseOver
6622         scope: this
6623     },
6624     'mouseout' : {
6625         fn: this.onMouseOut
6626         scope: this
6627     }
6628 });</code></pre>
6629      * <p>
6630      * Or a shorthand syntax:<br>
6631      * Code:<pre><code>
6632 el.on({
6633     'click' : this.onClick,
6634     'mouseover' : this.onMouseOver,
6635     'mouseout' : this.onMouseOut
6636     scope: this
6637 });</code></pre>
6638      */
6639     pub.on = pub.addListener;
6640     pub.un = pub.removeListener;
6641
6642     pub.stoppedMouseDownEvent = new Roo.util.Event();
6643     return pub;
6644 }();
6645 /**
6646   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6647   * @param {Function} fn        The method the event invokes
6648   * @param {Object}   scope    An  object that becomes the scope of the handler
6649   * @param {boolean}  override If true, the obj passed in becomes
6650   *                             the execution scope of the listener
6651   * @member Roo
6652   * @method onReady
6653  */
6654 Roo.onReady = Roo.EventManager.onDocumentReady;
6655
6656 Roo.onReady(function(){
6657     var bd = Roo.get(document.body);
6658     if(!bd){ return; }
6659
6660     var cls = [
6661             Roo.isIE ? "roo-ie"
6662             : Roo.isIE11 ? "roo-ie11"
6663             : Roo.isEdge ? "roo-edge"
6664             : Roo.isGecko ? "roo-gecko"
6665             : Roo.isOpera ? "roo-opera"
6666             : Roo.isSafari ? "roo-safari" : ""];
6667
6668     if(Roo.isMac){
6669         cls.push("roo-mac");
6670     }
6671     if(Roo.isLinux){
6672         cls.push("roo-linux");
6673     }
6674     if(Roo.isIOS){
6675         cls.push("roo-ios");
6676     }
6677     if(Roo.isTouch){
6678         cls.push("roo-touch");
6679     }
6680     if(Roo.isBorderBox){
6681         cls.push('roo-border-box');
6682     }
6683     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6684         var p = bd.dom.parentNode;
6685         if(p){
6686             p.className += ' roo-strict';
6687         }
6688     }
6689     bd.addClass(cls.join(' '));
6690 });
6691
6692 /**
6693  * @class Roo.EventObject
6694  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6695  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6696  * Example:
6697  * <pre><code>
6698  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6699     e.preventDefault();
6700     var target = e.getTarget();
6701     ...
6702  }
6703  var myDiv = Roo.get("myDiv");
6704  myDiv.on("click", handleClick);
6705  //or
6706  Roo.EventManager.on("myDiv", 'click', handleClick);
6707  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6708  </code></pre>
6709  * @singleton
6710  */
6711 Roo.EventObject = function(){
6712     
6713     var E = Roo.lib.Event;
6714     
6715     // safari keypress events for special keys return bad keycodes
6716     var safariKeys = {
6717         63234 : 37, // left
6718         63235 : 39, // right
6719         63232 : 38, // up
6720         63233 : 40, // down
6721         63276 : 33, // page up
6722         63277 : 34, // page down
6723         63272 : 46, // delete
6724         63273 : 36, // home
6725         63275 : 35  // end
6726     };
6727
6728     // normalize button clicks
6729     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6730                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6731
6732     Roo.EventObjectImpl = function(e){
6733         if(e){
6734             this.setEvent(e.browserEvent || e);
6735         }
6736     };
6737     Roo.EventObjectImpl.prototype = {
6738         /**
6739          * Used to fix doc tools.
6740          * @scope Roo.EventObject.prototype
6741          */
6742             
6743
6744         
6745         
6746         /** The normal browser event */
6747         browserEvent : null,
6748         /** The button pressed in a mouse event */
6749         button : -1,
6750         /** True if the shift key was down during the event */
6751         shiftKey : false,
6752         /** True if the control key was down during the event */
6753         ctrlKey : false,
6754         /** True if the alt key was down during the event */
6755         altKey : false,
6756
6757         /** Key constant 
6758         * @type Number */
6759         BACKSPACE : 8,
6760         /** Key constant 
6761         * @type Number */
6762         TAB : 9,
6763         /** Key constant 
6764         * @type Number */
6765         RETURN : 13,
6766         /** Key constant 
6767         * @type Number */
6768         ENTER : 13,
6769         /** Key constant 
6770         * @type Number */
6771         SHIFT : 16,
6772         /** Key constant 
6773         * @type Number */
6774         CONTROL : 17,
6775         /** Key constant 
6776         * @type Number */
6777         ESC : 27,
6778         /** Key constant 
6779         * @type Number */
6780         SPACE : 32,
6781         /** Key constant 
6782         * @type Number */
6783         PAGEUP : 33,
6784         /** Key constant 
6785         * @type Number */
6786         PAGEDOWN : 34,
6787         /** Key constant 
6788         * @type Number */
6789         END : 35,
6790         /** Key constant 
6791         * @type Number */
6792         HOME : 36,
6793         /** Key constant 
6794         * @type Number */
6795         LEFT : 37,
6796         /** Key constant 
6797         * @type Number */
6798         UP : 38,
6799         /** Key constant 
6800         * @type Number */
6801         RIGHT : 39,
6802         /** Key constant 
6803         * @type Number */
6804         DOWN : 40,
6805         /** Key constant 
6806         * @type Number */
6807         DELETE : 46,
6808         /** Key constant 
6809         * @type Number */
6810         F5 : 116,
6811
6812            /** @private */
6813         setEvent : function(e){
6814             if(e == this || (e && e.browserEvent)){ // already wrapped
6815                 return e;
6816             }
6817             this.browserEvent = e;
6818             if(e){
6819                 // normalize buttons
6820                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6821                 if(e.type == 'click' && this.button == -1){
6822                     this.button = 0;
6823                 }
6824                 this.type = e.type;
6825                 this.shiftKey = e.shiftKey;
6826                 // mac metaKey behaves like ctrlKey
6827                 this.ctrlKey = e.ctrlKey || e.metaKey;
6828                 this.altKey = e.altKey;
6829                 // in getKey these will be normalized for the mac
6830                 this.keyCode = e.keyCode;
6831                 // keyup warnings on firefox.
6832                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6833                 // cache the target for the delayed and or buffered events
6834                 this.target = E.getTarget(e);
6835                 // same for XY
6836                 this.xy = E.getXY(e);
6837             }else{
6838                 this.button = -1;
6839                 this.shiftKey = false;
6840                 this.ctrlKey = false;
6841                 this.altKey = false;
6842                 this.keyCode = 0;
6843                 this.charCode =0;
6844                 this.target = null;
6845                 this.xy = [0, 0];
6846             }
6847             return this;
6848         },
6849
6850         /**
6851          * Stop the event (preventDefault and stopPropagation)
6852          */
6853         stopEvent : function(){
6854             if(this.browserEvent){
6855                 if(this.browserEvent.type == 'mousedown'){
6856                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6857                 }
6858                 E.stopEvent(this.browserEvent);
6859             }
6860         },
6861
6862         /**
6863          * Prevents the browsers default handling of the event.
6864          */
6865         preventDefault : function(){
6866             if(this.browserEvent){
6867                 E.preventDefault(this.browserEvent);
6868             }
6869         },
6870
6871         /** @private */
6872         isNavKeyPress : function(){
6873             var k = this.keyCode;
6874             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6875             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6876         },
6877
6878         isSpecialKey : function(){
6879             var k = this.keyCode;
6880             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6881             (k == 16) || (k == 17) ||
6882             (k >= 18 && k <= 20) ||
6883             (k >= 33 && k <= 35) ||
6884             (k >= 36 && k <= 39) ||
6885             (k >= 44 && k <= 45);
6886         },
6887         /**
6888          * Cancels bubbling of the event.
6889          */
6890         stopPropagation : function(){
6891             if(this.browserEvent){
6892                 if(this.type == 'mousedown'){
6893                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6894                 }
6895                 E.stopPropagation(this.browserEvent);
6896             }
6897         },
6898
6899         /**
6900          * Gets the key code for the event.
6901          * @return {Number}
6902          */
6903         getCharCode : function(){
6904             return this.charCode || this.keyCode;
6905         },
6906
6907         /**
6908          * Returns a normalized keyCode for the event.
6909          * @return {Number} The key code
6910          */
6911         getKey : function(){
6912             var k = this.keyCode || this.charCode;
6913             return Roo.isSafari ? (safariKeys[k] || k) : k;
6914         },
6915
6916         /**
6917          * Gets the x coordinate of the event.
6918          * @return {Number}
6919          */
6920         getPageX : function(){
6921             return this.xy[0];
6922         },
6923
6924         /**
6925          * Gets the y coordinate of the event.
6926          * @return {Number}
6927          */
6928         getPageY : function(){
6929             return this.xy[1];
6930         },
6931
6932         /**
6933          * Gets the time of the event.
6934          * @return {Number}
6935          */
6936         getTime : function(){
6937             if(this.browserEvent){
6938                 return E.getTime(this.browserEvent);
6939             }
6940             return null;
6941         },
6942
6943         /**
6944          * Gets the page coordinates of the event.
6945          * @return {Array} The xy values like [x, y]
6946          */
6947         getXY : function(){
6948             return this.xy;
6949         },
6950
6951         /**
6952          * Gets the target for the event.
6953          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6954          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6955                 search as a number or element (defaults to 10 || document.body)
6956          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6957          * @return {HTMLelement}
6958          */
6959         getTarget : function(selector, maxDepth, returnEl){
6960             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6961         },
6962         /**
6963          * Gets the related target.
6964          * @return {HTMLElement}
6965          */
6966         getRelatedTarget : function(){
6967             if(this.browserEvent){
6968                 return E.getRelatedTarget(this.browserEvent);
6969             }
6970             return null;
6971         },
6972
6973         /**
6974          * Normalizes mouse wheel delta across browsers
6975          * @return {Number} The delta
6976          */
6977         getWheelDelta : function(){
6978             var e = this.browserEvent;
6979             var delta = 0;
6980             if(e.wheelDelta){ /* IE/Opera. */
6981                 delta = e.wheelDelta/120;
6982             }else if(e.detail){ /* Mozilla case. */
6983                 delta = -e.detail/3;
6984             }
6985             return delta;
6986         },
6987
6988         /**
6989          * Returns true if the control, meta, shift or alt key was pressed during this event.
6990          * @return {Boolean}
6991          */
6992         hasModifier : function(){
6993             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6994         },
6995
6996         /**
6997          * Returns true if the target of this event equals el or is a child of el
6998          * @param {String/HTMLElement/Element} el
6999          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
7000          * @return {Boolean}
7001          */
7002         within : function(el, related){
7003             var t = this[related ? "getRelatedTarget" : "getTarget"]();
7004             return t && Roo.fly(el).contains(t);
7005         },
7006
7007         getPoint : function(){
7008             return new Roo.lib.Point(this.xy[0], this.xy[1]);
7009         }
7010     };
7011
7012     return new Roo.EventObjectImpl();
7013 }();
7014             
7015     /*
7016  * Based on:
7017  * Ext JS Library 1.1.1
7018  * Copyright(c) 2006-2007, Ext JS, LLC.
7019  *
7020  * Originally Released Under LGPL - original licence link has changed is not relivant.
7021  *
7022  * Fork - LGPL
7023  * <script type="text/javascript">
7024  */
7025
7026  
7027 // was in Composite Element!??!?!
7028  
7029 (function(){
7030     var D = Roo.lib.Dom;
7031     var E = Roo.lib.Event;
7032     var A = Roo.lib.Anim;
7033
7034     // local style camelizing for speed
7035     var propCache = {};
7036     var camelRe = /(-[a-z])/gi;
7037     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
7038     var view = document.defaultView;
7039
7040 /**
7041  * @class Roo.Element
7042  * Represents an Element in the DOM.<br><br>
7043  * Usage:<br>
7044 <pre><code>
7045 var el = Roo.get("my-div");
7046
7047 // or with getEl
7048 var el = getEl("my-div");
7049
7050 // or with a DOM element
7051 var el = Roo.get(myDivElement);
7052 </code></pre>
7053  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
7054  * each call instead of constructing a new one.<br><br>
7055  * <b>Animations</b><br />
7056  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
7057  * should either be a boolean (true) or an object literal with animation options. The animation options are:
7058 <pre>
7059 Option    Default   Description
7060 --------- --------  ---------------------------------------------
7061 duration  .35       The duration of the animation in seconds
7062 easing    easeOut   The YUI easing method
7063 callback  none      A function to execute when the anim completes
7064 scope     this      The scope (this) of the callback function
7065 </pre>
7066 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
7067 * manipulate the animation. Here's an example:
7068 <pre><code>
7069 var el = Roo.get("my-div");
7070
7071 // no animation
7072 el.setWidth(100);
7073
7074 // default animation
7075 el.setWidth(100, true);
7076
7077 // animation with some options set
7078 el.setWidth(100, {
7079     duration: 1,
7080     callback: this.foo,
7081     scope: this
7082 });
7083
7084 // using the "anim" property to get the Anim object
7085 var opt = {
7086     duration: 1,
7087     callback: this.foo,
7088     scope: this
7089 };
7090 el.setWidth(100, opt);
7091 ...
7092 if(opt.anim.isAnimated()){
7093     opt.anim.stop();
7094 }
7095 </code></pre>
7096 * <b> Composite (Collections of) Elements</b><br />
7097  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7098  * @constructor Create a new Element directly.
7099  * @param {String/HTMLElement} element
7100  * @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).
7101  */
7102     Roo.Element = function(element, forceNew){
7103         var dom = typeof element == "string" ?
7104                 document.getElementById(element) : element;
7105         if(!dom){ // invalid id/element
7106             return null;
7107         }
7108         var id = dom.id;
7109         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7110             return Roo.Element.cache[id];
7111         }
7112
7113         /**
7114          * The DOM element
7115          * @type HTMLElement
7116          */
7117         this.dom = dom;
7118
7119         /**
7120          * The DOM element ID
7121          * @type String
7122          */
7123         this.id = id || Roo.id(dom);
7124     };
7125
7126     var El = Roo.Element;
7127
7128     El.prototype = {
7129         /**
7130          * The element's default display mode  (defaults to "")
7131          * @type String
7132          */
7133         originalDisplay : "",
7134
7135         visibilityMode : 1,
7136         /**
7137          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7138          * @type String
7139          */
7140         defaultUnit : "px",
7141         
7142         /**
7143          * Sets the element's visibility mode. When setVisible() is called it
7144          * will use this to determine whether to set the visibility or the display property.
7145          * @param visMode Element.VISIBILITY or Element.DISPLAY
7146          * @return {Roo.Element} this
7147          */
7148         setVisibilityMode : function(visMode){
7149             this.visibilityMode = visMode;
7150             return this;
7151         },
7152         /**
7153          * Convenience method for setVisibilityMode(Element.DISPLAY)
7154          * @param {String} display (optional) What to set display to when visible
7155          * @return {Roo.Element} this
7156          */
7157         enableDisplayMode : function(display){
7158             this.setVisibilityMode(El.DISPLAY);
7159             if(typeof display != "undefined") { this.originalDisplay = display; }
7160             return this;
7161         },
7162
7163         /**
7164          * 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)
7165          * @param {String} selector The simple selector to test
7166          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7167                 search as a number or element (defaults to 10 || document.body)
7168          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7169          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7170          */
7171         findParent : function(simpleSelector, maxDepth, returnEl){
7172             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7173             maxDepth = maxDepth || 50;
7174             if(typeof maxDepth != "number"){
7175                 stopEl = Roo.getDom(maxDepth);
7176                 maxDepth = 10;
7177             }
7178             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7179                 if(dq.is(p, simpleSelector)){
7180                     return returnEl ? Roo.get(p) : p;
7181                 }
7182                 depth++;
7183                 p = p.parentNode;
7184             }
7185             return null;
7186         },
7187
7188
7189         /**
7190          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7191          * @param {String} selector The simple selector to test
7192          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7193                 search as a number or element (defaults to 10 || document.body)
7194          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7195          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7196          */
7197         findParentNode : function(simpleSelector, maxDepth, returnEl){
7198             var p = Roo.fly(this.dom.parentNode, '_internal');
7199             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7200         },
7201         
7202         /**
7203          * Looks at  the scrollable parent element
7204          */
7205         findScrollableParent : function()
7206         {
7207             var overflowRegex = /(auto|scroll)/;
7208             
7209             if(this.getStyle('position') === 'fixed'){
7210                 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7211             }
7212             
7213             var excludeStaticParent = this.getStyle('position') === "absolute";
7214             
7215             for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
7216                 
7217                 if (excludeStaticParent && parent.getStyle('position') === "static") {
7218                     continue;
7219                 }
7220                 
7221                 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
7222                     return parent;
7223                 }
7224                 
7225                 if(parent.dom.nodeName.toLowerCase() == 'body'){
7226                     return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7227                 }
7228             }
7229             
7230             return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7231         },
7232
7233         /**
7234          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7235          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7236          * @param {String} selector The simple selector to test
7237          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7238                 search as a number or element (defaults to 10 || document.body)
7239          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7240          */
7241         up : function(simpleSelector, maxDepth){
7242             return this.findParentNode(simpleSelector, maxDepth, true);
7243         },
7244
7245
7246
7247         /**
7248          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7249          * @param {String} selector The simple selector to test
7250          * @return {Boolean} True if this element matches the selector, else false
7251          */
7252         is : function(simpleSelector){
7253             return Roo.DomQuery.is(this.dom, simpleSelector);
7254         },
7255
7256         /**
7257          * Perform animation on this element.
7258          * @param {Object} args The YUI animation control args
7259          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7260          * @param {Function} onComplete (optional) Function to call when animation completes
7261          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7262          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7263          * @return {Roo.Element} this
7264          */
7265         animate : function(args, duration, onComplete, easing, animType){
7266             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7267             return this;
7268         },
7269
7270         /*
7271          * @private Internal animation call
7272          */
7273         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7274             animType = animType || 'run';
7275             opt = opt || {};
7276             var anim = Roo.lib.Anim[animType](
7277                 this.dom, args,
7278                 (opt.duration || defaultDur) || .35,
7279                 (opt.easing || defaultEase) || 'easeOut',
7280                 function(){
7281                     Roo.callback(cb, this);
7282                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7283                 },
7284                 this
7285             );
7286             opt.anim = anim;
7287             return anim;
7288         },
7289
7290         // private legacy anim prep
7291         preanim : function(a, i){
7292             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7293         },
7294
7295         /**
7296          * Removes worthless text nodes
7297          * @param {Boolean} forceReclean (optional) By default the element
7298          * keeps track if it has been cleaned already so
7299          * you can call this over and over. However, if you update the element and
7300          * need to force a reclean, you can pass true.
7301          */
7302         clean : function(forceReclean){
7303             if(this.isCleaned && forceReclean !== true){
7304                 return this;
7305             }
7306             var ns = /\S/;
7307             var d = this.dom, n = d.firstChild, ni = -1;
7308             while(n){
7309                 var nx = n.nextSibling;
7310                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7311                     d.removeChild(n);
7312                 }else{
7313                     n.nodeIndex = ++ni;
7314                 }
7315                 n = nx;
7316             }
7317             this.isCleaned = true;
7318             return this;
7319         },
7320
7321         // private
7322         calcOffsetsTo : function(el){
7323             el = Roo.get(el);
7324             var d = el.dom;
7325             var restorePos = false;
7326             if(el.getStyle('position') == 'static'){
7327                 el.position('relative');
7328                 restorePos = true;
7329             }
7330             var x = 0, y =0;
7331             var op = this.dom;
7332             while(op && op != d && op.tagName != 'HTML'){
7333                 x+= op.offsetLeft;
7334                 y+= op.offsetTop;
7335                 op = op.offsetParent;
7336             }
7337             if(restorePos){
7338                 el.position('static');
7339             }
7340             return [x, y];
7341         },
7342
7343         /**
7344          * Scrolls this element into view within the passed container.
7345          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7346          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7347          * @return {Roo.Element} this
7348          */
7349         scrollIntoView : function(container, hscroll){
7350             var c = Roo.getDom(container) || document.body;
7351             var el = this.dom;
7352
7353             var o = this.calcOffsetsTo(c),
7354                 l = o[0],
7355                 t = o[1],
7356                 b = t+el.offsetHeight,
7357                 r = l+el.offsetWidth;
7358
7359             var ch = c.clientHeight;
7360             var ct = parseInt(c.scrollTop, 10);
7361             var cl = parseInt(c.scrollLeft, 10);
7362             var cb = ct + ch;
7363             var cr = cl + c.clientWidth;
7364
7365             if(t < ct){
7366                 c.scrollTop = t;
7367             }else if(b > cb){
7368                 c.scrollTop = b-ch;
7369             }
7370
7371             if(hscroll !== false){
7372                 if(l < cl){
7373                     c.scrollLeft = l;
7374                 }else if(r > cr){
7375                     c.scrollLeft = r-c.clientWidth;
7376                 }
7377             }
7378             return this;
7379         },
7380
7381         // private
7382         scrollChildIntoView : function(child, hscroll){
7383             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7384         },
7385
7386         /**
7387          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7388          * the new height may not be available immediately.
7389          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7390          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7391          * @param {Function} onComplete (optional) Function to call when animation completes
7392          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7393          * @return {Roo.Element} this
7394          */
7395         autoHeight : function(animate, duration, onComplete, easing){
7396             var oldHeight = this.getHeight();
7397             this.clip();
7398             this.setHeight(1); // force clipping
7399             setTimeout(function(){
7400                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7401                 if(!animate){
7402                     this.setHeight(height);
7403                     this.unclip();
7404                     if(typeof onComplete == "function"){
7405                         onComplete();
7406                     }
7407                 }else{
7408                     this.setHeight(oldHeight); // restore original height
7409                     this.setHeight(height, animate, duration, function(){
7410                         this.unclip();
7411                         if(typeof onComplete == "function") { onComplete(); }
7412                     }.createDelegate(this), easing);
7413                 }
7414             }.createDelegate(this), 0);
7415             return this;
7416         },
7417
7418         /**
7419          * Returns true if this element is an ancestor of the passed element
7420          * @param {HTMLElement/String} el The element to check
7421          * @return {Boolean} True if this element is an ancestor of el, else false
7422          */
7423         contains : function(el){
7424             if(!el){return false;}
7425             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7426         },
7427
7428         /**
7429          * Checks whether the element is currently visible using both visibility and display properties.
7430          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7431          * @return {Boolean} True if the element is currently visible, else false
7432          */
7433         isVisible : function(deep) {
7434             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7435             if(deep !== true || !vis){
7436                 return vis;
7437             }
7438             var p = this.dom.parentNode;
7439             while(p && p.tagName.toLowerCase() != "body"){
7440                 if(!Roo.fly(p, '_isVisible').isVisible()){
7441                     return false;
7442                 }
7443                 p = p.parentNode;
7444             }
7445             return true;
7446         },
7447
7448         /**
7449          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7450          * @param {String} selector The CSS selector
7451          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7452          * @return {CompositeElement/CompositeElementLite} The composite element
7453          */
7454         select : function(selector, unique){
7455             return El.select(selector, unique, this.dom);
7456         },
7457
7458         /**
7459          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7460          * @param {String} selector The CSS selector
7461          * @return {Array} An array of the matched nodes
7462          */
7463         query : function(selector, unique){
7464             return Roo.DomQuery.select(selector, this.dom);
7465         },
7466
7467         /**
7468          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7469          * @param {String} selector The CSS selector
7470          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7471          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7472          */
7473         child : function(selector, returnDom){
7474             var n = Roo.DomQuery.selectNode(selector, this.dom);
7475             return returnDom ? n : Roo.get(n);
7476         },
7477
7478         /**
7479          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7480          * @param {String} selector The CSS selector
7481          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7482          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7483          */
7484         down : function(selector, returnDom){
7485             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7486             return returnDom ? n : Roo.get(n);
7487         },
7488
7489         /**
7490          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7491          * @param {String} group The group the DD object is member of
7492          * @param {Object} config The DD config object
7493          * @param {Object} overrides An object containing methods to override/implement on the DD object
7494          * @return {Roo.dd.DD} The DD object
7495          */
7496         initDD : function(group, config, overrides){
7497             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7498             return Roo.apply(dd, overrides);
7499         },
7500
7501         /**
7502          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7503          * @param {String} group The group the DDProxy object is member of
7504          * @param {Object} config The DDProxy config object
7505          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7506          * @return {Roo.dd.DDProxy} The DDProxy object
7507          */
7508         initDDProxy : function(group, config, overrides){
7509             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7510             return Roo.apply(dd, overrides);
7511         },
7512
7513         /**
7514          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7515          * @param {String} group The group the DDTarget object is member of
7516          * @param {Object} config The DDTarget config object
7517          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7518          * @return {Roo.dd.DDTarget} The DDTarget object
7519          */
7520         initDDTarget : function(group, config, overrides){
7521             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7522             return Roo.apply(dd, overrides);
7523         },
7524
7525         /**
7526          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7527          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7528          * @param {Boolean} visible Whether the element is visible
7529          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7530          * @return {Roo.Element} this
7531          */
7532          setVisible : function(visible, animate){
7533             if(!animate || !A){
7534                 if(this.visibilityMode == El.DISPLAY){
7535                     this.setDisplayed(visible);
7536                 }else{
7537                     this.fixDisplay();
7538                     this.dom.style.visibility = visible ? "visible" : "hidden";
7539                 }
7540             }else{
7541                 // closure for composites
7542                 var dom = this.dom;
7543                 var visMode = this.visibilityMode;
7544                 if(visible){
7545                     this.setOpacity(.01);
7546                     this.setVisible(true);
7547                 }
7548                 this.anim({opacity: { to: (visible?1:0) }},
7549                       this.preanim(arguments, 1),
7550                       null, .35, 'easeIn', function(){
7551                          if(!visible){
7552                              if(visMode == El.DISPLAY){
7553                                  dom.style.display = "none";
7554                              }else{
7555                                  dom.style.visibility = "hidden";
7556                              }
7557                              Roo.get(dom).setOpacity(1);
7558                          }
7559                      });
7560             }
7561             return this;
7562         },
7563
7564         /**
7565          * Returns true if display is not "none"
7566          * @return {Boolean}
7567          */
7568         isDisplayed : function() {
7569             return this.getStyle("display") != "none";
7570         },
7571
7572         /**
7573          * Toggles the element's visibility or display, depending on visibility mode.
7574          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7575          * @return {Roo.Element} this
7576          */
7577         toggle : function(animate){
7578             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7579             return this;
7580         },
7581
7582         /**
7583          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7584          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7585          * @return {Roo.Element} this
7586          */
7587         setDisplayed : function(value) {
7588             if(typeof value == "boolean"){
7589                value = value ? this.originalDisplay : "none";
7590             }
7591             this.setStyle("display", value);
7592             return this;
7593         },
7594
7595         /**
7596          * Tries to focus the element. Any exceptions are caught and ignored.
7597          * @return {Roo.Element} this
7598          */
7599         focus : function() {
7600             try{
7601                 this.dom.focus();
7602             }catch(e){}
7603             return this;
7604         },
7605
7606         /**
7607          * Tries to blur the element. Any exceptions are caught and ignored.
7608          * @return {Roo.Element} this
7609          */
7610         blur : function() {
7611             try{
7612                 this.dom.blur();
7613             }catch(e){}
7614             return this;
7615         },
7616
7617         /**
7618          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7619          * @param {String/Array} className The CSS class to add, or an array of classes
7620          * @return {Roo.Element} this
7621          */
7622         addClass : function(className){
7623             if(className instanceof Array){
7624                 for(var i = 0, len = className.length; i < len; i++) {
7625                     this.addClass(className[i]);
7626                 }
7627             }else{
7628                 if(className && !this.hasClass(className)){
7629                     this.dom.className = this.dom.className + " " + className;
7630                 }
7631             }
7632             return this;
7633         },
7634
7635         /**
7636          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7637          * @param {String/Array} className The CSS class to add, or an array of classes
7638          * @return {Roo.Element} this
7639          */
7640         radioClass : function(className){
7641             var siblings = this.dom.parentNode.childNodes;
7642             for(var i = 0; i < siblings.length; i++) {
7643                 var s = siblings[i];
7644                 if(s.nodeType == 1){
7645                     Roo.get(s).removeClass(className);
7646                 }
7647             }
7648             this.addClass(className);
7649             return this;
7650         },
7651
7652         /**
7653          * Removes one or more CSS classes from the element.
7654          * @param {String/Array} className The CSS class to remove, or an array of classes
7655          * @return {Roo.Element} this
7656          */
7657         removeClass : function(className){
7658             if(!className || !this.dom.className){
7659                 return this;
7660             }
7661             if(className instanceof Array){
7662                 for(var i = 0, len = className.length; i < len; i++) {
7663                     this.removeClass(className[i]);
7664                 }
7665             }else{
7666                 if(this.hasClass(className)){
7667                     var re = this.classReCache[className];
7668                     if (!re) {
7669                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7670                        this.classReCache[className] = re;
7671                     }
7672                     this.dom.className =
7673                         this.dom.className.replace(re, " ");
7674                 }
7675             }
7676             return this;
7677         },
7678
7679         // private
7680         classReCache: {},
7681
7682         /**
7683          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7684          * @param {String} className The CSS class to toggle
7685          * @return {Roo.Element} this
7686          */
7687         toggleClass : function(className){
7688             if(this.hasClass(className)){
7689                 this.removeClass(className);
7690             }else{
7691                 this.addClass(className);
7692             }
7693             return this;
7694         },
7695
7696         /**
7697          * Checks if the specified CSS class exists on this element's DOM node.
7698          * @param {String} className The CSS class to check for
7699          * @return {Boolean} True if the class exists, else false
7700          */
7701         hasClass : function(className){
7702             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7703         },
7704
7705         /**
7706          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7707          * @param {String} oldClassName The CSS class to replace
7708          * @param {String} newClassName The replacement CSS class
7709          * @return {Roo.Element} this
7710          */
7711         replaceClass : function(oldClassName, newClassName){
7712             this.removeClass(oldClassName);
7713             this.addClass(newClassName);
7714             return this;
7715         },
7716
7717         /**
7718          * Returns an object with properties matching the styles requested.
7719          * For example, el.getStyles('color', 'font-size', 'width') might return
7720          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7721          * @param {String} style1 A style name
7722          * @param {String} style2 A style name
7723          * @param {String} etc.
7724          * @return {Object} The style object
7725          */
7726         getStyles : function(){
7727             var a = arguments, len = a.length, r = {};
7728             for(var i = 0; i < len; i++){
7729                 r[a[i]] = this.getStyle(a[i]);
7730             }
7731             return r;
7732         },
7733
7734         /**
7735          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7736          * @param {String} property The style property whose value is returned.
7737          * @return {String} The current value of the style property for this element.
7738          */
7739         getStyle : function(){
7740             return view && view.getComputedStyle ?
7741                 function(prop){
7742                     var el = this.dom, v, cs, camel;
7743                     if(prop == 'float'){
7744                         prop = "cssFloat";
7745                     }
7746                     if(el.style && (v = el.style[prop])){
7747                         return v;
7748                     }
7749                     if(cs = view.getComputedStyle(el, "")){
7750                         if(!(camel = propCache[prop])){
7751                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7752                         }
7753                         return cs[camel];
7754                     }
7755                     return null;
7756                 } :
7757                 function(prop){
7758                     var el = this.dom, v, cs, camel;
7759                     if(prop == 'opacity'){
7760                         if(typeof el.style.filter == 'string'){
7761                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7762                             if(m){
7763                                 var fv = parseFloat(m[1]);
7764                                 if(!isNaN(fv)){
7765                                     return fv ? fv / 100 : 0;
7766                                 }
7767                             }
7768                         }
7769                         return 1;
7770                     }else if(prop == 'float'){
7771                         prop = "styleFloat";
7772                     }
7773                     if(!(camel = propCache[prop])){
7774                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7775                     }
7776                     if(v = el.style[camel]){
7777                         return v;
7778                     }
7779                     if(cs = el.currentStyle){
7780                         return cs[camel];
7781                     }
7782                     return null;
7783                 };
7784         }(),
7785
7786         /**
7787          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7788          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7789          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7790          * @return {Roo.Element} this
7791          */
7792         setStyle : function(prop, value){
7793             if(typeof prop == "string"){
7794                 
7795                 if (prop == 'float') {
7796                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7797                     return this;
7798                 }
7799                 
7800                 var camel;
7801                 if(!(camel = propCache[prop])){
7802                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7803                 }
7804                 
7805                 if(camel == 'opacity') {
7806                     this.setOpacity(value);
7807                 }else{
7808                     this.dom.style[camel] = value;
7809                 }
7810             }else{
7811                 for(var style in prop){
7812                     if(typeof prop[style] != "function"){
7813                        this.setStyle(style, prop[style]);
7814                     }
7815                 }
7816             }
7817             return this;
7818         },
7819
7820         /**
7821          * More flexible version of {@link #setStyle} for setting style properties.
7822          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7823          * a function which returns such a specification.
7824          * @return {Roo.Element} this
7825          */
7826         applyStyles : function(style){
7827             Roo.DomHelper.applyStyles(this.dom, style);
7828             return this;
7829         },
7830
7831         /**
7832           * 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).
7833           * @return {Number} The X position of the element
7834           */
7835         getX : function(){
7836             return D.getX(this.dom);
7837         },
7838
7839         /**
7840           * 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).
7841           * @return {Number} The Y position of the element
7842           */
7843         getY : function(){
7844             return D.getY(this.dom);
7845         },
7846
7847         /**
7848           * 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).
7849           * @return {Array} The XY position of the element
7850           */
7851         getXY : function(){
7852             return D.getXY(this.dom);
7853         },
7854
7855         /**
7856          * 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).
7857          * @param {Number} The X position of the element
7858          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7859          * @return {Roo.Element} this
7860          */
7861         setX : function(x, animate){
7862             if(!animate || !A){
7863                 D.setX(this.dom, x);
7864             }else{
7865                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7866             }
7867             return this;
7868         },
7869
7870         /**
7871          * 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).
7872          * @param {Number} The Y position of the element
7873          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7874          * @return {Roo.Element} this
7875          */
7876         setY : function(y, animate){
7877             if(!animate || !A){
7878                 D.setY(this.dom, y);
7879             }else{
7880                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7881             }
7882             return this;
7883         },
7884
7885         /**
7886          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7887          * @param {String} left The left CSS property value
7888          * @return {Roo.Element} this
7889          */
7890         setLeft : function(left){
7891             this.setStyle("left", this.addUnits(left));
7892             return this;
7893         },
7894
7895         /**
7896          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7897          * @param {String} top The top CSS property value
7898          * @return {Roo.Element} this
7899          */
7900         setTop : function(top){
7901             this.setStyle("top", this.addUnits(top));
7902             return this;
7903         },
7904
7905         /**
7906          * Sets the element's CSS right style.
7907          * @param {String} right The right CSS property value
7908          * @return {Roo.Element} this
7909          */
7910         setRight : function(right){
7911             this.setStyle("right", this.addUnits(right));
7912             return this;
7913         },
7914
7915         /**
7916          * Sets the element's CSS bottom style.
7917          * @param {String} bottom The bottom CSS property value
7918          * @return {Roo.Element} this
7919          */
7920         setBottom : function(bottom){
7921             this.setStyle("bottom", this.addUnits(bottom));
7922             return this;
7923         },
7924
7925         /**
7926          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7927          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7928          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7929          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7930          * @return {Roo.Element} this
7931          */
7932         setXY : function(pos, animate){
7933             if(!animate || !A){
7934                 D.setXY(this.dom, pos);
7935             }else{
7936                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7937             }
7938             return this;
7939         },
7940
7941         /**
7942          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7943          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7944          * @param {Number} x X value for new position (coordinates are page-based)
7945          * @param {Number} y Y value for new position (coordinates are page-based)
7946          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7947          * @return {Roo.Element} this
7948          */
7949         setLocation : function(x, y, animate){
7950             this.setXY([x, y], this.preanim(arguments, 2));
7951             return this;
7952         },
7953
7954         /**
7955          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7956          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7957          * @param {Number} x X value for new position (coordinates are page-based)
7958          * @param {Number} y Y value for new position (coordinates are page-based)
7959          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7960          * @return {Roo.Element} this
7961          */
7962         moveTo : function(x, y, animate){
7963             this.setXY([x, y], this.preanim(arguments, 2));
7964             return this;
7965         },
7966
7967         /**
7968          * Returns the region of the given element.
7969          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7970          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7971          */
7972         getRegion : function(){
7973             return D.getRegion(this.dom);
7974         },
7975
7976         /**
7977          * Returns the offset height of the element
7978          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7979          * @return {Number} The element's height
7980          */
7981         getHeight : function(contentHeight){
7982             var h = this.dom.offsetHeight || 0;
7983             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7984         },
7985
7986         /**
7987          * Returns the offset width of the element
7988          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7989          * @return {Number} The element's width
7990          */
7991         getWidth : function(contentWidth){
7992             var w = this.dom.offsetWidth || 0;
7993             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7994         },
7995
7996         /**
7997          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7998          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7999          * if a height has not been set using CSS.
8000          * @return {Number}
8001          */
8002         getComputedHeight : function(){
8003             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
8004             if(!h){
8005                 h = parseInt(this.getStyle('height'), 10) || 0;
8006                 if(!this.isBorderBox()){
8007                     h += this.getFrameWidth('tb');
8008                 }
8009             }
8010             return h;
8011         },
8012
8013         /**
8014          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
8015          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
8016          * if a width has not been set using CSS.
8017          * @return {Number}
8018          */
8019         getComputedWidth : function(){
8020             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
8021             if(!w){
8022                 w = parseInt(this.getStyle('width'), 10) || 0;
8023                 if(!this.isBorderBox()){
8024                     w += this.getFrameWidth('lr');
8025                 }
8026             }
8027             return w;
8028         },
8029
8030         /**
8031          * Returns the size of the element.
8032          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
8033          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8034          */
8035         getSize : function(contentSize){
8036             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
8037         },
8038
8039         /**
8040          * Returns the width and height of the viewport.
8041          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
8042          */
8043         getViewSize : function(){
8044             var d = this.dom, doc = document, aw = 0, ah = 0;
8045             if(d == doc || d == doc.body){
8046                 return {width : D.getViewWidth(), height: D.getViewHeight()};
8047             }else{
8048                 return {
8049                     width : d.clientWidth,
8050                     height: d.clientHeight
8051                 };
8052             }
8053         },
8054
8055         /**
8056          * Returns the value of the "value" attribute
8057          * @param {Boolean} asNumber true to parse the value as a number
8058          * @return {String/Number}
8059          */
8060         getValue : function(asNumber){
8061             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
8062         },
8063
8064         // private
8065         adjustWidth : function(width){
8066             if(typeof width == "number"){
8067                 if(this.autoBoxAdjust && !this.isBorderBox()){
8068                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8069                 }
8070                 if(width < 0){
8071                     width = 0;
8072                 }
8073             }
8074             return width;
8075         },
8076
8077         // private
8078         adjustHeight : function(height){
8079             if(typeof height == "number"){
8080                if(this.autoBoxAdjust && !this.isBorderBox()){
8081                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8082                }
8083                if(height < 0){
8084                    height = 0;
8085                }
8086             }
8087             return height;
8088         },
8089
8090         /**
8091          * Set the width of the element
8092          * @param {Number} width The new width
8093          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8094          * @return {Roo.Element} this
8095          */
8096         setWidth : function(width, animate){
8097             width = this.adjustWidth(width);
8098             if(!animate || !A){
8099                 this.dom.style.width = this.addUnits(width);
8100             }else{
8101                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
8102             }
8103             return this;
8104         },
8105
8106         /**
8107          * Set the height of the element
8108          * @param {Number} height The new height
8109          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8110          * @return {Roo.Element} this
8111          */
8112          setHeight : function(height, animate){
8113             height = this.adjustHeight(height);
8114             if(!animate || !A){
8115                 this.dom.style.height = this.addUnits(height);
8116             }else{
8117                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
8118             }
8119             return this;
8120         },
8121
8122         /**
8123          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
8124          * @param {Number} width The new width
8125          * @param {Number} height The new height
8126          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8127          * @return {Roo.Element} this
8128          */
8129          setSize : function(width, height, animate){
8130             if(typeof width == "object"){ // in case of object from getSize()
8131                 height = width.height; width = width.width;
8132             }
8133             width = this.adjustWidth(width); height = this.adjustHeight(height);
8134             if(!animate || !A){
8135                 this.dom.style.width = this.addUnits(width);
8136                 this.dom.style.height = this.addUnits(height);
8137             }else{
8138                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8139             }
8140             return this;
8141         },
8142
8143         /**
8144          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8145          * @param {Number} x X value for new position (coordinates are page-based)
8146          * @param {Number} y Y value for new position (coordinates are page-based)
8147          * @param {Number} width The new width
8148          * @param {Number} height The new height
8149          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8150          * @return {Roo.Element} this
8151          */
8152         setBounds : function(x, y, width, height, animate){
8153             if(!animate || !A){
8154                 this.setSize(width, height);
8155                 this.setLocation(x, y);
8156             }else{
8157                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8158                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8159                               this.preanim(arguments, 4), 'motion');
8160             }
8161             return this;
8162         },
8163
8164         /**
8165          * 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.
8166          * @param {Roo.lib.Region} region The region to fill
8167          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8168          * @return {Roo.Element} this
8169          */
8170         setRegion : function(region, animate){
8171             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8172             return this;
8173         },
8174
8175         /**
8176          * Appends an event handler
8177          *
8178          * @param {String}   eventName     The type of event to append
8179          * @param {Function} fn        The method the event invokes
8180          * @param {Object} scope       (optional) The scope (this object) of the fn
8181          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8182          */
8183         addListener : function(eventName, fn, scope, options){
8184             if (this.dom) {
8185                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8186             }
8187         },
8188
8189         /**
8190          * Removes an event handler from this element
8191          * @param {String} eventName the type of event to remove
8192          * @param {Function} fn the method the event invokes
8193          * @return {Roo.Element} this
8194          */
8195         removeListener : function(eventName, fn){
8196             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8197             return this;
8198         },
8199
8200         /**
8201          * Removes all previous added listeners from this element
8202          * @return {Roo.Element} this
8203          */
8204         removeAllListeners : function(){
8205             E.purgeElement(this.dom);
8206             return this;
8207         },
8208
8209         relayEvent : function(eventName, observable){
8210             this.on(eventName, function(e){
8211                 observable.fireEvent(eventName, e);
8212             });
8213         },
8214
8215         /**
8216          * Set the opacity of the element
8217          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8218          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8219          * @return {Roo.Element} this
8220          */
8221          setOpacity : function(opacity, animate){
8222             if(!animate || !A){
8223                 var s = this.dom.style;
8224                 if(Roo.isIE){
8225                     s.zoom = 1;
8226                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8227                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8228                 }else{
8229                     s.opacity = opacity;
8230                 }
8231             }else{
8232                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8233             }
8234             return this;
8235         },
8236
8237         /**
8238          * Gets the left X coordinate
8239          * @param {Boolean} local True to get the local css position instead of page coordinate
8240          * @return {Number}
8241          */
8242         getLeft : function(local){
8243             if(!local){
8244                 return this.getX();
8245             }else{
8246                 return parseInt(this.getStyle("left"), 10) || 0;
8247             }
8248         },
8249
8250         /**
8251          * Gets the right X coordinate of the element (element X position + element width)
8252          * @param {Boolean} local True to get the local css position instead of page coordinate
8253          * @return {Number}
8254          */
8255         getRight : function(local){
8256             if(!local){
8257                 return this.getX() + this.getWidth();
8258             }else{
8259                 return (this.getLeft(true) + this.getWidth()) || 0;
8260             }
8261         },
8262
8263         /**
8264          * Gets the top Y coordinate
8265          * @param {Boolean} local True to get the local css position instead of page coordinate
8266          * @return {Number}
8267          */
8268         getTop : function(local) {
8269             if(!local){
8270                 return this.getY();
8271             }else{
8272                 return parseInt(this.getStyle("top"), 10) || 0;
8273             }
8274         },
8275
8276         /**
8277          * Gets the bottom Y coordinate of the element (element Y position + element height)
8278          * @param {Boolean} local True to get the local css position instead of page coordinate
8279          * @return {Number}
8280          */
8281         getBottom : function(local){
8282             if(!local){
8283                 return this.getY() + this.getHeight();
8284             }else{
8285                 return (this.getTop(true) + this.getHeight()) || 0;
8286             }
8287         },
8288
8289         /**
8290         * Initializes positioning on this element. If a desired position is not passed, it will make the
8291         * the element positioned relative IF it is not already positioned.
8292         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8293         * @param {Number} zIndex (optional) The zIndex to apply
8294         * @param {Number} x (optional) Set the page X position
8295         * @param {Number} y (optional) Set the page Y position
8296         */
8297         position : function(pos, zIndex, x, y){
8298             if(!pos){
8299                if(this.getStyle('position') == 'static'){
8300                    this.setStyle('position', 'relative');
8301                }
8302             }else{
8303                 this.setStyle("position", pos);
8304             }
8305             if(zIndex){
8306                 this.setStyle("z-index", zIndex);
8307             }
8308             if(x !== undefined && y !== undefined){
8309                 this.setXY([x, y]);
8310             }else if(x !== undefined){
8311                 this.setX(x);
8312             }else if(y !== undefined){
8313                 this.setY(y);
8314             }
8315         },
8316
8317         /**
8318         * Clear positioning back to the default when the document was loaded
8319         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8320         * @return {Roo.Element} this
8321          */
8322         clearPositioning : function(value){
8323             value = value ||'';
8324             this.setStyle({
8325                 "left": value,
8326                 "right": value,
8327                 "top": value,
8328                 "bottom": value,
8329                 "z-index": "",
8330                 "position" : "static"
8331             });
8332             return this;
8333         },
8334
8335         /**
8336         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8337         * snapshot before performing an update and then restoring the element.
8338         * @return {Object}
8339         */
8340         getPositioning : function(){
8341             var l = this.getStyle("left");
8342             var t = this.getStyle("top");
8343             return {
8344                 "position" : this.getStyle("position"),
8345                 "left" : l,
8346                 "right" : l ? "" : this.getStyle("right"),
8347                 "top" : t,
8348                 "bottom" : t ? "" : this.getStyle("bottom"),
8349                 "z-index" : this.getStyle("z-index")
8350             };
8351         },
8352
8353         /**
8354          * Gets the width of the border(s) for the specified side(s)
8355          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8356          * passing lr would get the border (l)eft width + the border (r)ight width.
8357          * @return {Number} The width of the sides passed added together
8358          */
8359         getBorderWidth : function(side){
8360             return this.addStyles(side, El.borders);
8361         },
8362
8363         /**
8364          * Gets the width of the padding(s) for the specified side(s)
8365          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8366          * passing lr would get the padding (l)eft + the padding (r)ight.
8367          * @return {Number} The padding of the sides passed added together
8368          */
8369         getPadding : function(side){
8370             return this.addStyles(side, El.paddings);
8371         },
8372
8373         /**
8374         * Set positioning with an object returned by getPositioning().
8375         * @param {Object} posCfg
8376         * @return {Roo.Element} this
8377          */
8378         setPositioning : function(pc){
8379             this.applyStyles(pc);
8380             if(pc.right == "auto"){
8381                 this.dom.style.right = "";
8382             }
8383             if(pc.bottom == "auto"){
8384                 this.dom.style.bottom = "";
8385             }
8386             return this;
8387         },
8388
8389         // private
8390         fixDisplay : function(){
8391             if(this.getStyle("display") == "none"){
8392                 this.setStyle("visibility", "hidden");
8393                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8394                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8395                     this.setStyle("display", "block");
8396                 }
8397             }
8398         },
8399
8400         /**
8401          * Quick set left and top adding default units
8402          * @param {String} left The left CSS property value
8403          * @param {String} top The top CSS property value
8404          * @return {Roo.Element} this
8405          */
8406          setLeftTop : function(left, top){
8407             this.dom.style.left = this.addUnits(left);
8408             this.dom.style.top = this.addUnits(top);
8409             return this;
8410         },
8411
8412         /**
8413          * Move this element relative to its current position.
8414          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8415          * @param {Number} distance How far to move the element in pixels
8416          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8417          * @return {Roo.Element} this
8418          */
8419          move : function(direction, distance, animate){
8420             var xy = this.getXY();
8421             direction = direction.toLowerCase();
8422             switch(direction){
8423                 case "l":
8424                 case "left":
8425                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8426                     break;
8427                case "r":
8428                case "right":
8429                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8430                     break;
8431                case "t":
8432                case "top":
8433                case "up":
8434                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8435                     break;
8436                case "b":
8437                case "bottom":
8438                case "down":
8439                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8440                     break;
8441             }
8442             return this;
8443         },
8444
8445         /**
8446          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8447          * @return {Roo.Element} this
8448          */
8449         clip : function(){
8450             if(!this.isClipped){
8451                this.isClipped = true;
8452                this.originalClip = {
8453                    "o": this.getStyle("overflow"),
8454                    "x": this.getStyle("overflow-x"),
8455                    "y": this.getStyle("overflow-y")
8456                };
8457                this.setStyle("overflow", "hidden");
8458                this.setStyle("overflow-x", "hidden");
8459                this.setStyle("overflow-y", "hidden");
8460             }
8461             return this;
8462         },
8463
8464         /**
8465          *  Return clipping (overflow) to original clipping before clip() was called
8466          * @return {Roo.Element} this
8467          */
8468         unclip : function(){
8469             if(this.isClipped){
8470                 this.isClipped = false;
8471                 var o = this.originalClip;
8472                 if(o.o){this.setStyle("overflow", o.o);}
8473                 if(o.x){this.setStyle("overflow-x", o.x);}
8474                 if(o.y){this.setStyle("overflow-y", o.y);}
8475             }
8476             return this;
8477         },
8478
8479
8480         /**
8481          * Gets the x,y coordinates specified by the anchor position on the element.
8482          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8483          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8484          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8485          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8486          * @return {Array} [x, y] An array containing the element's x and y coordinates
8487          */
8488         getAnchorXY : function(anchor, local, s){
8489             //Passing a different size is useful for pre-calculating anchors,
8490             //especially for anchored animations that change the el size.
8491
8492             var w, h, vp = false;
8493             if(!s){
8494                 var d = this.dom;
8495                 if(d == document.body || d == document){
8496                     vp = true;
8497                     w = D.getViewWidth(); h = D.getViewHeight();
8498                 }else{
8499                     w = this.getWidth(); h = this.getHeight();
8500                 }
8501             }else{
8502                 w = s.width;  h = s.height;
8503             }
8504             var x = 0, y = 0, r = Math.round;
8505             switch((anchor || "tl").toLowerCase()){
8506                 case "c":
8507                     x = r(w*.5);
8508                     y = r(h*.5);
8509                 break;
8510                 case "t":
8511                     x = r(w*.5);
8512                     y = 0;
8513                 break;
8514                 case "l":
8515                     x = 0;
8516                     y = r(h*.5);
8517                 break;
8518                 case "r":
8519                     x = w;
8520                     y = r(h*.5);
8521                 break;
8522                 case "b":
8523                     x = r(w*.5);
8524                     y = h;
8525                 break;
8526                 case "tl":
8527                     x = 0;
8528                     y = 0;
8529                 break;
8530                 case "bl":
8531                     x = 0;
8532                     y = h;
8533                 break;
8534                 case "br":
8535                     x = w;
8536                     y = h;
8537                 break;
8538                 case "tr":
8539                     x = w;
8540                     y = 0;
8541                 break;
8542             }
8543             if(local === true){
8544                 return [x, y];
8545             }
8546             if(vp){
8547                 var sc = this.getScroll();
8548                 return [x + sc.left, y + sc.top];
8549             }
8550             //Add the element's offset xy
8551             var o = this.getXY();
8552             return [x+o[0], y+o[1]];
8553         },
8554
8555         /**
8556          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8557          * supported position values.
8558          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8559          * @param {String} position The position to align to.
8560          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8561          * @return {Array} [x, y]
8562          */
8563         getAlignToXY : function(el, p, o){
8564             el = Roo.get(el);
8565             var d = this.dom;
8566             if(!el.dom){
8567                 throw "Element.alignTo with an element that doesn't exist";
8568             }
8569             var c = false; //constrain to viewport
8570             var p1 = "", p2 = "";
8571             o = o || [0,0];
8572
8573             if(!p){
8574                 p = "tl-bl";
8575             }else if(p == "?"){
8576                 p = "tl-bl?";
8577             }else if(p.indexOf("-") == -1){
8578                 p = "tl-" + p;
8579             }
8580             p = p.toLowerCase();
8581             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8582             if(!m){
8583                throw "Element.alignTo with an invalid alignment " + p;
8584             }
8585             p1 = m[1]; p2 = m[2]; c = !!m[3];
8586
8587             //Subtract the aligned el's internal xy from the target's offset xy
8588             //plus custom offset to get the aligned el's new offset xy
8589             var a1 = this.getAnchorXY(p1, true);
8590             var a2 = el.getAnchorXY(p2, false);
8591             var x = a2[0] - a1[0] + o[0];
8592             var y = a2[1] - a1[1] + o[1];
8593             if(c){
8594                 //constrain the aligned el to viewport if necessary
8595                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8596                 // 5px of margin for ie
8597                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8598
8599                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8600                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8601                 //otherwise swap the aligned el to the opposite border of the target.
8602                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8603                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8604                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8605                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8606
8607                var doc = document;
8608                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8609                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8610
8611                if((x+w) > dw + scrollX){
8612                     x = swapX ? r.left-w : dw+scrollX-w;
8613                 }
8614                if(x < scrollX){
8615                    x = swapX ? r.right : scrollX;
8616                }
8617                if((y+h) > dh + scrollY){
8618                     y = swapY ? r.top-h : dh+scrollY-h;
8619                 }
8620                if (y < scrollY){
8621                    y = swapY ? r.bottom : scrollY;
8622                }
8623             }
8624             return [x,y];
8625         },
8626
8627         // private
8628         getConstrainToXY : function(){
8629             var os = {top:0, left:0, bottom:0, right: 0};
8630
8631             return function(el, local, offsets, proposedXY){
8632                 el = Roo.get(el);
8633                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8634
8635                 var vw, vh, vx = 0, vy = 0;
8636                 if(el.dom == document.body || el.dom == document){
8637                     vw = Roo.lib.Dom.getViewWidth();
8638                     vh = Roo.lib.Dom.getViewHeight();
8639                 }else{
8640                     vw = el.dom.clientWidth;
8641                     vh = el.dom.clientHeight;
8642                     if(!local){
8643                         var vxy = el.getXY();
8644                         vx = vxy[0];
8645                         vy = vxy[1];
8646                     }
8647                 }
8648
8649                 var s = el.getScroll();
8650
8651                 vx += offsets.left + s.left;
8652                 vy += offsets.top + s.top;
8653
8654                 vw -= offsets.right;
8655                 vh -= offsets.bottom;
8656
8657                 var vr = vx+vw;
8658                 var vb = vy+vh;
8659
8660                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8661                 var x = xy[0], y = xy[1];
8662                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8663
8664                 // only move it if it needs it
8665                 var moved = false;
8666
8667                 // first validate right/bottom
8668                 if((x + w) > vr){
8669                     x = vr - w;
8670                     moved = true;
8671                 }
8672                 if((y + h) > vb){
8673                     y = vb - h;
8674                     moved = true;
8675                 }
8676                 // then make sure top/left isn't negative
8677                 if(x < vx){
8678                     x = vx;
8679                     moved = true;
8680                 }
8681                 if(y < vy){
8682                     y = vy;
8683                     moved = true;
8684                 }
8685                 return moved ? [x, y] : false;
8686             };
8687         }(),
8688
8689         // private
8690         adjustForConstraints : function(xy, parent, offsets){
8691             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8692         },
8693
8694         /**
8695          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8696          * document it aligns it to the viewport.
8697          * The position parameter is optional, and can be specified in any one of the following formats:
8698          * <ul>
8699          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8700          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8701          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8702          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8703          *   <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
8704          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8705          * </ul>
8706          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8707          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8708          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8709          * that specified in order to enforce the viewport constraints.
8710          * Following are all of the supported anchor positions:
8711     <pre>
8712     Value  Description
8713     -----  -----------------------------
8714     tl     The top left corner (default)
8715     t      The center of the top edge
8716     tr     The top right corner
8717     l      The center of the left edge
8718     c      In the center of the element
8719     r      The center of the right edge
8720     bl     The bottom left corner
8721     b      The center of the bottom edge
8722     br     The bottom right corner
8723     </pre>
8724     Example Usage:
8725     <pre><code>
8726     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8727     el.alignTo("other-el");
8728
8729     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8730     el.alignTo("other-el", "tr?");
8731
8732     // align the bottom right corner of el with the center left edge of other-el
8733     el.alignTo("other-el", "br-l?");
8734
8735     // align the center of el with the bottom left corner of other-el and
8736     // adjust the x position by -6 pixels (and the y position by 0)
8737     el.alignTo("other-el", "c-bl", [-6, 0]);
8738     </code></pre>
8739          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8740          * @param {String} position The position to align to.
8741          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8742          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8743          * @return {Roo.Element} this
8744          */
8745         alignTo : function(element, position, offsets, animate){
8746             var xy = this.getAlignToXY(element, position, offsets);
8747             this.setXY(xy, this.preanim(arguments, 3));
8748             return this;
8749         },
8750
8751         /**
8752          * Anchors an element to another element and realigns it when the window is resized.
8753          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8754          * @param {String} position The position to align to.
8755          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8756          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8757          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8758          * is a number, it is used as the buffer delay (defaults to 50ms).
8759          * @param {Function} callback The function to call after the animation finishes
8760          * @return {Roo.Element} this
8761          */
8762         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8763             var action = function(){
8764                 this.alignTo(el, alignment, offsets, animate);
8765                 Roo.callback(callback, this);
8766             };
8767             Roo.EventManager.onWindowResize(action, this);
8768             var tm = typeof monitorScroll;
8769             if(tm != 'undefined'){
8770                 Roo.EventManager.on(window, 'scroll', action, this,
8771                     {buffer: tm == 'number' ? monitorScroll : 50});
8772             }
8773             action.call(this); // align immediately
8774             return this;
8775         },
8776         /**
8777          * Clears any opacity settings from this element. Required in some cases for IE.
8778          * @return {Roo.Element} this
8779          */
8780         clearOpacity : function(){
8781             if (window.ActiveXObject) {
8782                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8783                     this.dom.style.filter = "";
8784                 }
8785             } else {
8786                 this.dom.style.opacity = "";
8787                 this.dom.style["-moz-opacity"] = "";
8788                 this.dom.style["-khtml-opacity"] = "";
8789             }
8790             return this;
8791         },
8792
8793         /**
8794          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8795          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8796          * @return {Roo.Element} this
8797          */
8798         hide : function(animate){
8799             this.setVisible(false, this.preanim(arguments, 0));
8800             return this;
8801         },
8802
8803         /**
8804         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8805         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8806          * @return {Roo.Element} this
8807          */
8808         show : function(animate){
8809             this.setVisible(true, this.preanim(arguments, 0));
8810             return this;
8811         },
8812
8813         /**
8814          * @private Test if size has a unit, otherwise appends the default
8815          */
8816         addUnits : function(size){
8817             return Roo.Element.addUnits(size, this.defaultUnit);
8818         },
8819
8820         /**
8821          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8822          * @return {Roo.Element} this
8823          */
8824         beginMeasure : function(){
8825             var el = this.dom;
8826             if(el.offsetWidth || el.offsetHeight){
8827                 return this; // offsets work already
8828             }
8829             var changed = [];
8830             var p = this.dom, b = document.body; // start with this element
8831             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8832                 var pe = Roo.get(p);
8833                 if(pe.getStyle('display') == 'none'){
8834                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8835                     p.style.visibility = "hidden";
8836                     p.style.display = "block";
8837                 }
8838                 p = p.parentNode;
8839             }
8840             this._measureChanged = changed;
8841             return this;
8842
8843         },
8844
8845         /**
8846          * Restores displays to before beginMeasure was called
8847          * @return {Roo.Element} this
8848          */
8849         endMeasure : function(){
8850             var changed = this._measureChanged;
8851             if(changed){
8852                 for(var i = 0, len = changed.length; i < len; i++) {
8853                     var r = changed[i];
8854                     r.el.style.visibility = r.visibility;
8855                     r.el.style.display = "none";
8856                 }
8857                 this._measureChanged = null;
8858             }
8859             return this;
8860         },
8861
8862         /**
8863         * Update the innerHTML of this element, optionally searching for and processing scripts
8864         * @param {String} html The new HTML
8865         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8866         * @param {Function} callback For async script loading you can be noticed when the update completes
8867         * @return {Roo.Element} this
8868          */
8869         update : function(html, loadScripts, callback){
8870             if(typeof html == "undefined"){
8871                 html = "";
8872             }
8873             if(loadScripts !== true){
8874                 this.dom.innerHTML = html;
8875                 if(typeof callback == "function"){
8876                     callback();
8877                 }
8878                 return this;
8879             }
8880             var id = Roo.id();
8881             var dom = this.dom;
8882
8883             html += '<span id="' + id + '"></span>';
8884
8885             E.onAvailable(id, function(){
8886                 var hd = document.getElementsByTagName("head")[0];
8887                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8888                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8889                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8890
8891                 var match;
8892                 while(match = re.exec(html)){
8893                     var attrs = match[1];
8894                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8895                     if(srcMatch && srcMatch[2]){
8896                        var s = document.createElement("script");
8897                        s.src = srcMatch[2];
8898                        var typeMatch = attrs.match(typeRe);
8899                        if(typeMatch && typeMatch[2]){
8900                            s.type = typeMatch[2];
8901                        }
8902                        hd.appendChild(s);
8903                     }else if(match[2] && match[2].length > 0){
8904                         if(window.execScript) {
8905                            window.execScript(match[2]);
8906                         } else {
8907                             /**
8908                              * eval:var:id
8909                              * eval:var:dom
8910                              * eval:var:html
8911                              * 
8912                              */
8913                            window.eval(match[2]);
8914                         }
8915                     }
8916                 }
8917                 var el = document.getElementById(id);
8918                 if(el){el.parentNode.removeChild(el);}
8919                 if(typeof callback == "function"){
8920                     callback();
8921                 }
8922             });
8923             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8924             return this;
8925         },
8926
8927         /**
8928          * Direct access to the UpdateManager update() method (takes the same parameters).
8929          * @param {String/Function} url The url for this request or a function to call to get the url
8930          * @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}
8931          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8932          * @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.
8933          * @return {Roo.Element} this
8934          */
8935         load : function(){
8936             var um = this.getUpdateManager();
8937             um.update.apply(um, arguments);
8938             return this;
8939         },
8940
8941         /**
8942         * Gets this element's UpdateManager
8943         * @return {Roo.UpdateManager} The UpdateManager
8944         */
8945         getUpdateManager : function(){
8946             if(!this.updateManager){
8947                 this.updateManager = new Roo.UpdateManager(this);
8948             }
8949             return this.updateManager;
8950         },
8951
8952         /**
8953          * Disables text selection for this element (normalized across browsers)
8954          * @return {Roo.Element} this
8955          */
8956         unselectable : function(){
8957             this.dom.unselectable = "on";
8958             this.swallowEvent("selectstart", true);
8959             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8960             this.addClass("x-unselectable");
8961             return this;
8962         },
8963
8964         /**
8965         * Calculates the x, y to center this element on the screen
8966         * @return {Array} The x, y values [x, y]
8967         */
8968         getCenterXY : function(){
8969             return this.getAlignToXY(document, 'c-c');
8970         },
8971
8972         /**
8973         * Centers the Element in either the viewport, or another Element.
8974         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8975         */
8976         center : function(centerIn){
8977             this.alignTo(centerIn || document, 'c-c');
8978             return this;
8979         },
8980
8981         /**
8982          * Tests various css rules/browsers to determine if this element uses a border box
8983          * @return {Boolean}
8984          */
8985         isBorderBox : function(){
8986             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8987         },
8988
8989         /**
8990          * Return a box {x, y, width, height} that can be used to set another elements
8991          * size/location to match this element.
8992          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8993          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8994          * @return {Object} box An object in the format {x, y, width, height}
8995          */
8996         getBox : function(contentBox, local){
8997             var xy;
8998             if(!local){
8999                 xy = this.getXY();
9000             }else{
9001                 var left = parseInt(this.getStyle("left"), 10) || 0;
9002                 var top = parseInt(this.getStyle("top"), 10) || 0;
9003                 xy = [left, top];
9004             }
9005             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
9006             if(!contentBox){
9007                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
9008             }else{
9009                 var l = this.getBorderWidth("l")+this.getPadding("l");
9010                 var r = this.getBorderWidth("r")+this.getPadding("r");
9011                 var t = this.getBorderWidth("t")+this.getPadding("t");
9012                 var b = this.getBorderWidth("b")+this.getPadding("b");
9013                 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)};
9014             }
9015             bx.right = bx.x + bx.width;
9016             bx.bottom = bx.y + bx.height;
9017             return bx;
9018         },
9019
9020         /**
9021          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
9022          for more information about the sides.
9023          * @param {String} sides
9024          * @return {Number}
9025          */
9026         getFrameWidth : function(sides, onlyContentBox){
9027             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
9028         },
9029
9030         /**
9031          * 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.
9032          * @param {Object} box The box to fill {x, y, width, height}
9033          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
9034          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9035          * @return {Roo.Element} this
9036          */
9037         setBox : function(box, adjust, animate){
9038             var w = box.width, h = box.height;
9039             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
9040                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9041                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9042             }
9043             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
9044             return this;
9045         },
9046
9047         /**
9048          * Forces the browser to repaint this element
9049          * @return {Roo.Element} this
9050          */
9051          repaint : function(){
9052             var dom = this.dom;
9053             this.addClass("x-repaint");
9054             setTimeout(function(){
9055                 Roo.get(dom).removeClass("x-repaint");
9056             }, 1);
9057             return this;
9058         },
9059
9060         /**
9061          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
9062          * then it returns the calculated width of the sides (see getPadding)
9063          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
9064          * @return {Object/Number}
9065          */
9066         getMargins : function(side){
9067             if(!side){
9068                 return {
9069                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
9070                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
9071                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
9072                     right: parseInt(this.getStyle("margin-right"), 10) || 0
9073                 };
9074             }else{
9075                 return this.addStyles(side, El.margins);
9076              }
9077         },
9078
9079         // private
9080         addStyles : function(sides, styles){
9081             var val = 0, v, w;
9082             for(var i = 0, len = sides.length; i < len; i++){
9083                 v = this.getStyle(styles[sides.charAt(i)]);
9084                 if(v){
9085                      w = parseInt(v, 10);
9086                      if(w){ val += w; }
9087                 }
9088             }
9089             return val;
9090         },
9091
9092         /**
9093          * Creates a proxy element of this element
9094          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
9095          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
9096          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
9097          * @return {Roo.Element} The new proxy element
9098          */
9099         createProxy : function(config, renderTo, matchBox){
9100             if(renderTo){
9101                 renderTo = Roo.getDom(renderTo);
9102             }else{
9103                 renderTo = document.body;
9104             }
9105             config = typeof config == "object" ?
9106                 config : {tag : "div", cls: config};
9107             var proxy = Roo.DomHelper.append(renderTo, config, true);
9108             if(matchBox){
9109                proxy.setBox(this.getBox());
9110             }
9111             return proxy;
9112         },
9113
9114         /**
9115          * Puts a mask over this element to disable user interaction. Requires core.css.
9116          * This method can only be applied to elements which accept child nodes.
9117          * @param {String} msg (optional) A message to display in the mask
9118          * @param {String} msgCls (optional) A css class to apply to the msg element
9119          * @return {Element} The mask  element
9120          */
9121         mask : function(msg, msgCls)
9122         {
9123             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
9124                 this.setStyle("position", "relative");
9125             }
9126             if(!this._mask){
9127                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
9128             }
9129             
9130             this.addClass("x-masked");
9131             this._mask.setDisplayed(true);
9132             
9133             // we wander
9134             var z = 0;
9135             var dom = this.dom;
9136             while (dom && dom.style) {
9137                 if (!isNaN(parseInt(dom.style.zIndex))) {
9138                     z = Math.max(z, parseInt(dom.style.zIndex));
9139                 }
9140                 dom = dom.parentNode;
9141             }
9142             // if we are masking the body - then it hides everything..
9143             if (this.dom == document.body) {
9144                 z = 1000000;
9145                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9146                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9147             }
9148            
9149             if(typeof msg == 'string'){
9150                 if(!this._maskMsg){
9151                     this._maskMsg = Roo.DomHelper.append(this.dom, {
9152                         cls: "roo-el-mask-msg", 
9153                         cn: [
9154                             {
9155                                 tag: 'i',
9156                                 cls: 'fa fa-spinner fa-spin'
9157                             },
9158                             {
9159                                 tag: 'div'
9160                             }   
9161                         ]
9162                     }, true);
9163                 }
9164                 var mm = this._maskMsg;
9165                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9166                 if (mm.dom.lastChild) { // weird IE issue?
9167                     mm.dom.lastChild.innerHTML = msg;
9168                 }
9169                 mm.setDisplayed(true);
9170                 mm.center(this);
9171                 mm.setStyle('z-index', z + 102);
9172             }
9173             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9174                 this._mask.setHeight(this.getHeight());
9175             }
9176             this._mask.setStyle('z-index', z + 100);
9177             
9178             return this._mask;
9179         },
9180
9181         /**
9182          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9183          * it is cached for reuse.
9184          */
9185         unmask : function(removeEl){
9186             if(this._mask){
9187                 if(removeEl === true){
9188                     this._mask.remove();
9189                     delete this._mask;
9190                     if(this._maskMsg){
9191                         this._maskMsg.remove();
9192                         delete this._maskMsg;
9193                     }
9194                 }else{
9195                     this._mask.setDisplayed(false);
9196                     if(this._maskMsg){
9197                         this._maskMsg.setDisplayed(false);
9198                     }
9199                 }
9200             }
9201             this.removeClass("x-masked");
9202         },
9203
9204         /**
9205          * Returns true if this element is masked
9206          * @return {Boolean}
9207          */
9208         isMasked : function(){
9209             return this._mask && this._mask.isVisible();
9210         },
9211
9212         /**
9213          * Creates an iframe shim for this element to keep selects and other windowed objects from
9214          * showing through.
9215          * @return {Roo.Element} The new shim element
9216          */
9217         createShim : function(){
9218             var el = document.createElement('iframe');
9219             el.frameBorder = 'no';
9220             el.className = 'roo-shim';
9221             if(Roo.isIE && Roo.isSecure){
9222                 el.src = Roo.SSL_SECURE_URL;
9223             }
9224             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9225             shim.autoBoxAdjust = false;
9226             return shim;
9227         },
9228
9229         /**
9230          * Removes this element from the DOM and deletes it from the cache
9231          */
9232         remove : function(){
9233             if(this.dom.parentNode){
9234                 this.dom.parentNode.removeChild(this.dom);
9235             }
9236             delete El.cache[this.dom.id];
9237         },
9238
9239         /**
9240          * Sets up event handlers to add and remove a css class when the mouse is over this element
9241          * @param {String} className
9242          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9243          * mouseout events for children elements
9244          * @return {Roo.Element} this
9245          */
9246         addClassOnOver : function(className, preventFlicker){
9247             this.on("mouseover", function(){
9248                 Roo.fly(this, '_internal').addClass(className);
9249             }, this.dom);
9250             var removeFn = function(e){
9251                 if(preventFlicker !== true || !e.within(this, true)){
9252                     Roo.fly(this, '_internal').removeClass(className);
9253                 }
9254             };
9255             this.on("mouseout", removeFn, this.dom);
9256             return this;
9257         },
9258
9259         /**
9260          * Sets up event handlers to add and remove a css class when this element has the focus
9261          * @param {String} className
9262          * @return {Roo.Element} this
9263          */
9264         addClassOnFocus : function(className){
9265             this.on("focus", function(){
9266                 Roo.fly(this, '_internal').addClass(className);
9267             }, this.dom);
9268             this.on("blur", function(){
9269                 Roo.fly(this, '_internal').removeClass(className);
9270             }, this.dom);
9271             return this;
9272         },
9273         /**
9274          * 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)
9275          * @param {String} className
9276          * @return {Roo.Element} this
9277          */
9278         addClassOnClick : function(className){
9279             var dom = this.dom;
9280             this.on("mousedown", function(){
9281                 Roo.fly(dom, '_internal').addClass(className);
9282                 var d = Roo.get(document);
9283                 var fn = function(){
9284                     Roo.fly(dom, '_internal').removeClass(className);
9285                     d.removeListener("mouseup", fn);
9286                 };
9287                 d.on("mouseup", fn);
9288             });
9289             return this;
9290         },
9291
9292         /**
9293          * Stops the specified event from bubbling and optionally prevents the default action
9294          * @param {String} eventName
9295          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9296          * @return {Roo.Element} this
9297          */
9298         swallowEvent : function(eventName, preventDefault){
9299             var fn = function(e){
9300                 e.stopPropagation();
9301                 if(preventDefault){
9302                     e.preventDefault();
9303                 }
9304             };
9305             if(eventName instanceof Array){
9306                 for(var i = 0, len = eventName.length; i < len; i++){
9307                      this.on(eventName[i], fn);
9308                 }
9309                 return this;
9310             }
9311             this.on(eventName, fn);
9312             return this;
9313         },
9314
9315         /**
9316          * @private
9317          */
9318       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9319
9320         /**
9321          * Sizes this element to its parent element's dimensions performing
9322          * neccessary box adjustments.
9323          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9324          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9325          * @return {Roo.Element} this
9326          */
9327         fitToParent : function(monitorResize, targetParent) {
9328           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9329           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9330           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9331             return;
9332           }
9333           var p = Roo.get(targetParent || this.dom.parentNode);
9334           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9335           if (monitorResize === true) {
9336             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9337             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9338           }
9339           return this;
9340         },
9341
9342         /**
9343          * Gets the next sibling, skipping text nodes
9344          * @return {HTMLElement} The next sibling or null
9345          */
9346         getNextSibling : function(){
9347             var n = this.dom.nextSibling;
9348             while(n && n.nodeType != 1){
9349                 n = n.nextSibling;
9350             }
9351             return n;
9352         },
9353
9354         /**
9355          * Gets the previous sibling, skipping text nodes
9356          * @return {HTMLElement} The previous sibling or null
9357          */
9358         getPrevSibling : function(){
9359             var n = this.dom.previousSibling;
9360             while(n && n.nodeType != 1){
9361                 n = n.previousSibling;
9362             }
9363             return n;
9364         },
9365
9366
9367         /**
9368          * Appends the passed element(s) to this element
9369          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9370          * @return {Roo.Element} this
9371          */
9372         appendChild: function(el){
9373             el = Roo.get(el);
9374             el.appendTo(this);
9375             return this;
9376         },
9377
9378         /**
9379          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9380          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9381          * automatically generated with the specified attributes.
9382          * @param {HTMLElement} insertBefore (optional) a child element of this element
9383          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9384          * @return {Roo.Element} The new child element
9385          */
9386         createChild: function(config, insertBefore, returnDom){
9387             config = config || {tag:'div'};
9388             if(insertBefore){
9389                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9390             }
9391             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9392         },
9393
9394         /**
9395          * Appends this element to the passed element
9396          * @param {String/HTMLElement/Element} el The new parent element
9397          * @return {Roo.Element} this
9398          */
9399         appendTo: function(el){
9400             el = Roo.getDom(el);
9401             el.appendChild(this.dom);
9402             return this;
9403         },
9404
9405         /**
9406          * Inserts this element before the passed element in the DOM
9407          * @param {String/HTMLElement/Element} el The element to insert before
9408          * @return {Roo.Element} this
9409          */
9410         insertBefore: function(el){
9411             el = Roo.getDom(el);
9412             el.parentNode.insertBefore(this.dom, el);
9413             return this;
9414         },
9415
9416         /**
9417          * Inserts this element after the passed element in the DOM
9418          * @param {String/HTMLElement/Element} el The element to insert after
9419          * @return {Roo.Element} this
9420          */
9421         insertAfter: function(el){
9422             el = Roo.getDom(el);
9423             el.parentNode.insertBefore(this.dom, el.nextSibling);
9424             return this;
9425         },
9426
9427         /**
9428          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9429          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9430          * @return {Roo.Element} The new child
9431          */
9432         insertFirst: function(el, returnDom){
9433             el = el || {};
9434             if(typeof el == 'object' && !el.nodeType){ // dh config
9435                 return this.createChild(el, this.dom.firstChild, returnDom);
9436             }else{
9437                 el = Roo.getDom(el);
9438                 this.dom.insertBefore(el, this.dom.firstChild);
9439                 return !returnDom ? Roo.get(el) : el;
9440             }
9441         },
9442
9443         /**
9444          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9445          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9446          * @param {String} where (optional) 'before' or 'after' defaults to before
9447          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9448          * @return {Roo.Element} the inserted Element
9449          */
9450         insertSibling: function(el, where, returnDom){
9451             where = where ? where.toLowerCase() : 'before';
9452             el = el || {};
9453             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9454
9455             if(typeof el == 'object' && !el.nodeType){ // dh config
9456                 if(where == 'after' && !this.dom.nextSibling){
9457                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9458                 }else{
9459                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9460                 }
9461
9462             }else{
9463                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9464                             where == 'before' ? this.dom : this.dom.nextSibling);
9465                 if(!returnDom){
9466                     rt = Roo.get(rt);
9467                 }
9468             }
9469             return rt;
9470         },
9471
9472         /**
9473          * Creates and wraps this element with another element
9474          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9475          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9476          * @return {HTMLElement/Element} The newly created wrapper element
9477          */
9478         wrap: function(config, returnDom){
9479             if(!config){
9480                 config = {tag: "div"};
9481             }
9482             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9483             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9484             return newEl;
9485         },
9486
9487         /**
9488          * Replaces the passed element with this element
9489          * @param {String/HTMLElement/Element} el The element to replace
9490          * @return {Roo.Element} this
9491          */
9492         replace: function(el){
9493             el = Roo.get(el);
9494             this.insertBefore(el);
9495             el.remove();
9496             return this;
9497         },
9498
9499         /**
9500          * Inserts an html fragment into this element
9501          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9502          * @param {String} html The HTML fragment
9503          * @param {Boolean} returnEl True to return an Roo.Element
9504          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9505          */
9506         insertHtml : function(where, html, returnEl){
9507             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9508             return returnEl ? Roo.get(el) : el;
9509         },
9510
9511         /**
9512          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9513          * @param {Object} o The object with the attributes
9514          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9515          * @return {Roo.Element} this
9516          */
9517         set : function(o, useSet){
9518             var el = this.dom;
9519             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9520             for(var attr in o){
9521                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
9522                 if(attr=="cls"){
9523                     el.className = o["cls"];
9524                 }else{
9525                     if(useSet) {
9526                         el.setAttribute(attr, o[attr]);
9527                     } else {
9528                         el[attr] = o[attr];
9529                     }
9530                 }
9531             }
9532             if(o.style){
9533                 Roo.DomHelper.applyStyles(el, o.style);
9534             }
9535             return this;
9536         },
9537
9538         /**
9539          * Convenience method for constructing a KeyMap
9540          * @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:
9541          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9542          * @param {Function} fn The function to call
9543          * @param {Object} scope (optional) The scope of the function
9544          * @return {Roo.KeyMap} The KeyMap created
9545          */
9546         addKeyListener : function(key, fn, scope){
9547             var config;
9548             if(typeof key != "object" || key instanceof Array){
9549                 config = {
9550                     key: key,
9551                     fn: fn,
9552                     scope: scope
9553                 };
9554             }else{
9555                 config = {
9556                     key : key.key,
9557                     shift : key.shift,
9558                     ctrl : key.ctrl,
9559                     alt : key.alt,
9560                     fn: fn,
9561                     scope: scope
9562                 };
9563             }
9564             return new Roo.KeyMap(this, config);
9565         },
9566
9567         /**
9568          * Creates a KeyMap for this element
9569          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9570          * @return {Roo.KeyMap} The KeyMap created
9571          */
9572         addKeyMap : function(config){
9573             return new Roo.KeyMap(this, config);
9574         },
9575
9576         /**
9577          * Returns true if this element is scrollable.
9578          * @return {Boolean}
9579          */
9580          isScrollable : function(){
9581             var dom = this.dom;
9582             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9583         },
9584
9585         /**
9586          * 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().
9587          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9588          * @param {Number} value The new scroll value
9589          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9590          * @return {Element} this
9591          */
9592
9593         scrollTo : function(side, value, animate){
9594             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9595             if(!animate || !A){
9596                 this.dom[prop] = value;
9597             }else{
9598                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9599                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9600             }
9601             return this;
9602         },
9603
9604         /**
9605          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9606          * within this element's scrollable range.
9607          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9608          * @param {Number} distance How far to scroll the element in pixels
9609          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9610          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9611          * was scrolled as far as it could go.
9612          */
9613          scroll : function(direction, distance, animate){
9614              if(!this.isScrollable()){
9615                  return;
9616              }
9617              var el = this.dom;
9618              var l = el.scrollLeft, t = el.scrollTop;
9619              var w = el.scrollWidth, h = el.scrollHeight;
9620              var cw = el.clientWidth, ch = el.clientHeight;
9621              direction = direction.toLowerCase();
9622              var scrolled = false;
9623              var a = this.preanim(arguments, 2);
9624              switch(direction){
9625                  case "l":
9626                  case "left":
9627                      if(w - l > cw){
9628                          var v = Math.min(l + distance, w-cw);
9629                          this.scrollTo("left", v, a);
9630                          scrolled = true;
9631                      }
9632                      break;
9633                 case "r":
9634                 case "right":
9635                      if(l > 0){
9636                          var v = Math.max(l - distance, 0);
9637                          this.scrollTo("left", v, a);
9638                          scrolled = true;
9639                      }
9640                      break;
9641                 case "t":
9642                 case "top":
9643                 case "up":
9644                      if(t > 0){
9645                          var v = Math.max(t - distance, 0);
9646                          this.scrollTo("top", v, a);
9647                          scrolled = true;
9648                      }
9649                      break;
9650                 case "b":
9651                 case "bottom":
9652                 case "down":
9653                      if(h - t > ch){
9654                          var v = Math.min(t + distance, h-ch);
9655                          this.scrollTo("top", v, a);
9656                          scrolled = true;
9657                      }
9658                      break;
9659              }
9660              return scrolled;
9661         },
9662
9663         /**
9664          * Translates the passed page coordinates into left/top css values for this element
9665          * @param {Number/Array} x The page x or an array containing [x, y]
9666          * @param {Number} y The page y
9667          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9668          */
9669         translatePoints : function(x, y){
9670             if(typeof x == 'object' || x instanceof Array){
9671                 y = x[1]; x = x[0];
9672             }
9673             var p = this.getStyle('position');
9674             var o = this.getXY();
9675
9676             var l = parseInt(this.getStyle('left'), 10);
9677             var t = parseInt(this.getStyle('top'), 10);
9678
9679             if(isNaN(l)){
9680                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9681             }
9682             if(isNaN(t)){
9683                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9684             }
9685
9686             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9687         },
9688
9689         /**
9690          * Returns the current scroll position of the element.
9691          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9692          */
9693         getScroll : function(){
9694             var d = this.dom, doc = document;
9695             if(d == doc || d == doc.body){
9696                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9697                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9698                 return {left: l, top: t};
9699             }else{
9700                 return {left: d.scrollLeft, top: d.scrollTop};
9701             }
9702         },
9703
9704         /**
9705          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9706          * are convert to standard 6 digit hex color.
9707          * @param {String} attr The css attribute
9708          * @param {String} defaultValue The default value to use when a valid color isn't found
9709          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9710          * YUI color anims.
9711          */
9712         getColor : function(attr, defaultValue, prefix){
9713             var v = this.getStyle(attr);
9714             if(!v || v == "transparent" || v == "inherit") {
9715                 return defaultValue;
9716             }
9717             var color = typeof prefix == "undefined" ? "#" : prefix;
9718             if(v.substr(0, 4) == "rgb("){
9719                 var rvs = v.slice(4, v.length -1).split(",");
9720                 for(var i = 0; i < 3; i++){
9721                     var h = parseInt(rvs[i]).toString(16);
9722                     if(h < 16){
9723                         h = "0" + h;
9724                     }
9725                     color += h;
9726                 }
9727             } else {
9728                 if(v.substr(0, 1) == "#"){
9729                     if(v.length == 4) {
9730                         for(var i = 1; i < 4; i++){
9731                             var c = v.charAt(i);
9732                             color +=  c + c;
9733                         }
9734                     }else if(v.length == 7){
9735                         color += v.substr(1);
9736                     }
9737                 }
9738             }
9739             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9740         },
9741
9742         /**
9743          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9744          * gradient background, rounded corners and a 4-way shadow.
9745          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9746          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9747          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9748          * @return {Roo.Element} this
9749          */
9750         boxWrap : function(cls){
9751             cls = cls || 'x-box';
9752             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9753             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9754             return el;
9755         },
9756
9757         /**
9758          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9759          * @param {String} namespace The namespace in which to look for the attribute
9760          * @param {String} name The attribute name
9761          * @return {String} The attribute value
9762          */
9763         getAttributeNS : Roo.isIE ? function(ns, name){
9764             var d = this.dom;
9765             var type = typeof d[ns+":"+name];
9766             if(type != 'undefined' && type != 'unknown'){
9767                 return d[ns+":"+name];
9768             }
9769             return d[name];
9770         } : function(ns, name){
9771             var d = this.dom;
9772             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9773         },
9774         
9775         
9776         /**
9777          * Sets or Returns the value the dom attribute value
9778          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9779          * @param {String} value (optional) The value to set the attribute to
9780          * @return {String} The attribute value
9781          */
9782         attr : function(name){
9783             if (arguments.length > 1) {
9784                 this.dom.setAttribute(name, arguments[1]);
9785                 return arguments[1];
9786             }
9787             if (typeof(name) == 'object') {
9788                 for(var i in name) {
9789                     this.attr(i, name[i]);
9790                 }
9791                 return name;
9792             }
9793             
9794             
9795             if (!this.dom.hasAttribute(name)) {
9796                 return undefined;
9797             }
9798             return this.dom.getAttribute(name);
9799         }
9800         
9801         
9802         
9803     };
9804
9805     var ep = El.prototype;
9806
9807     /**
9808      * Appends an event handler (Shorthand for addListener)
9809      * @param {String}   eventName     The type of event to append
9810      * @param {Function} fn        The method the event invokes
9811      * @param {Object} scope       (optional) The scope (this object) of the fn
9812      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9813      * @method
9814      */
9815     ep.on = ep.addListener;
9816         // backwards compat
9817     ep.mon = ep.addListener;
9818
9819     /**
9820      * Removes an event handler from this element (shorthand for removeListener)
9821      * @param {String} eventName the type of event to remove
9822      * @param {Function} fn the method the event invokes
9823      * @return {Roo.Element} this
9824      * @method
9825      */
9826     ep.un = ep.removeListener;
9827
9828     /**
9829      * true to automatically adjust width and height settings for box-model issues (default to true)
9830      */
9831     ep.autoBoxAdjust = true;
9832
9833     // private
9834     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9835
9836     // private
9837     El.addUnits = function(v, defaultUnit){
9838         if(v === "" || v == "auto"){
9839             return v;
9840         }
9841         if(v === undefined){
9842             return '';
9843         }
9844         if(typeof v == "number" || !El.unitPattern.test(v)){
9845             return v + (defaultUnit || 'px');
9846         }
9847         return v;
9848     };
9849
9850     // special markup used throughout Roo when box wrapping elements
9851     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>';
9852     /**
9853      * Visibility mode constant - Use visibility to hide element
9854      * @static
9855      * @type Number
9856      */
9857     El.VISIBILITY = 1;
9858     /**
9859      * Visibility mode constant - Use display to hide element
9860      * @static
9861      * @type Number
9862      */
9863     El.DISPLAY = 2;
9864
9865     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9866     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9867     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9868
9869
9870
9871     /**
9872      * @private
9873      */
9874     El.cache = {};
9875
9876     var docEl;
9877
9878     /**
9879      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9880      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9881      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9882      * @return {Element} The Element object
9883      * @static
9884      */
9885     El.get = function(el){
9886         var ex, elm, id;
9887         if(!el){ return null; }
9888         if(typeof el == "string"){ // element id
9889             if(!(elm = document.getElementById(el))){
9890                 return null;
9891             }
9892             if(ex = El.cache[el]){
9893                 ex.dom = elm;
9894             }else{
9895                 ex = El.cache[el] = new El(elm);
9896             }
9897             return ex;
9898         }else if(el.tagName){ // dom element
9899             if(!(id = el.id)){
9900                 id = Roo.id(el);
9901             }
9902             if(ex = El.cache[id]){
9903                 ex.dom = el;
9904             }else{
9905                 ex = El.cache[id] = new El(el);
9906             }
9907             return ex;
9908         }else if(el instanceof El){
9909             if(el != docEl){
9910                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9911                                                               // catch case where it hasn't been appended
9912                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9913             }
9914             return el;
9915         }else if(el.isComposite){
9916             return el;
9917         }else if(el instanceof Array){
9918             return El.select(el);
9919         }else if(el == document){
9920             // create a bogus element object representing the document object
9921             if(!docEl){
9922                 var f = function(){};
9923                 f.prototype = El.prototype;
9924                 docEl = new f();
9925                 docEl.dom = document;
9926             }
9927             return docEl;
9928         }
9929         return null;
9930     };
9931
9932     // private
9933     El.uncache = function(el){
9934         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9935             if(a[i]){
9936                 delete El.cache[a[i].id || a[i]];
9937             }
9938         }
9939     };
9940
9941     // private
9942     // Garbage collection - uncache elements/purge listeners on orphaned elements
9943     // so we don't hold a reference and cause the browser to retain them
9944     El.garbageCollect = function(){
9945         if(!Roo.enableGarbageCollector){
9946             clearInterval(El.collectorThread);
9947             return;
9948         }
9949         for(var eid in El.cache){
9950             var el = El.cache[eid], d = el.dom;
9951             // -------------------------------------------------------
9952             // Determining what is garbage:
9953             // -------------------------------------------------------
9954             // !d
9955             // dom node is null, definitely garbage
9956             // -------------------------------------------------------
9957             // !d.parentNode
9958             // no parentNode == direct orphan, definitely garbage
9959             // -------------------------------------------------------
9960             // !d.offsetParent && !document.getElementById(eid)
9961             // display none elements have no offsetParent so we will
9962             // also try to look it up by it's id. However, check
9963             // offsetParent first so we don't do unneeded lookups.
9964             // This enables collection of elements that are not orphans
9965             // directly, but somewhere up the line they have an orphan
9966             // parent.
9967             // -------------------------------------------------------
9968             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9969                 delete El.cache[eid];
9970                 if(d && Roo.enableListenerCollection){
9971                     E.purgeElement(d);
9972                 }
9973             }
9974         }
9975     }
9976     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9977
9978
9979     // dom is optional
9980     El.Flyweight = function(dom){
9981         this.dom = dom;
9982     };
9983     El.Flyweight.prototype = El.prototype;
9984
9985     El._flyweights = {};
9986     /**
9987      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9988      * the dom node can be overwritten by other code.
9989      * @param {String/HTMLElement} el The dom node or id
9990      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9991      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9992      * @static
9993      * @return {Element} The shared Element object
9994      */
9995     El.fly = function(el, named){
9996         named = named || '_global';
9997         el = Roo.getDom(el);
9998         if(!el){
9999             return null;
10000         }
10001         if(!El._flyweights[named]){
10002             El._flyweights[named] = new El.Flyweight();
10003         }
10004         El._flyweights[named].dom = el;
10005         return El._flyweights[named];
10006     };
10007
10008     /**
10009      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10010      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10011      * Shorthand of {@link Roo.Element#get}
10012      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10013      * @return {Element} The Element object
10014      * @member Roo
10015      * @method get
10016      */
10017     Roo.get = El.get;
10018     /**
10019      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10020      * the dom node can be overwritten by other code.
10021      * Shorthand of {@link Roo.Element#fly}
10022      * @param {String/HTMLElement} el The dom node or id
10023      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10024      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10025      * @static
10026      * @return {Element} The shared Element object
10027      * @member Roo
10028      * @method fly
10029      */
10030     Roo.fly = El.fly;
10031
10032     // speedy lookup for elements never to box adjust
10033     var noBoxAdjust = Roo.isStrict ? {
10034         select:1
10035     } : {
10036         input:1, select:1, textarea:1
10037     };
10038     if(Roo.isIE || Roo.isGecko){
10039         noBoxAdjust['button'] = 1;
10040     }
10041
10042
10043     Roo.EventManager.on(window, 'unload', function(){
10044         delete El.cache;
10045         delete El._flyweights;
10046     });
10047 })();
10048
10049
10050
10051
10052 if(Roo.DomQuery){
10053     Roo.Element.selectorFunction = Roo.DomQuery.select;
10054 }
10055
10056 Roo.Element.select = function(selector, unique, root){
10057     var els;
10058     if(typeof selector == "string"){
10059         els = Roo.Element.selectorFunction(selector, root);
10060     }else if(selector.length !== undefined){
10061         els = selector;
10062     }else{
10063         throw "Invalid selector";
10064     }
10065     if(unique === true){
10066         return new Roo.CompositeElement(els);
10067     }else{
10068         return new Roo.CompositeElementLite(els);
10069     }
10070 };
10071 /**
10072  * Selects elements based on the passed CSS selector to enable working on them as 1.
10073  * @param {String/Array} selector The CSS selector or an array of elements
10074  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
10075  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
10076  * @return {CompositeElementLite/CompositeElement}
10077  * @member Roo
10078  * @method select
10079  */
10080 Roo.select = Roo.Element.select;
10081
10082
10083
10084
10085
10086
10087
10088
10089
10090
10091
10092
10093
10094
10095 /*
10096  * Based on:
10097  * Ext JS Library 1.1.1
10098  * Copyright(c) 2006-2007, Ext JS, LLC.
10099  *
10100  * Originally Released Under LGPL - original licence link has changed is not relivant.
10101  *
10102  * Fork - LGPL
10103  * <script type="text/javascript">
10104  */
10105
10106
10107
10108 //Notifies Element that fx methods are available
10109 Roo.enableFx = true;
10110
10111 /**
10112  * @class Roo.Fx
10113  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
10114  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
10115  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
10116  * Element effects to work.</p><br/>
10117  *
10118  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
10119  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
10120  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
10121  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
10122  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
10123  * expected results and should be done with care.</p><br/>
10124  *
10125  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
10126  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
10127 <pre>
10128 Value  Description
10129 -----  -----------------------------
10130 tl     The top left corner
10131 t      The center of the top edge
10132 tr     The top right corner
10133 l      The center of the left edge
10134 r      The center of the right edge
10135 bl     The bottom left corner
10136 b      The center of the bottom edge
10137 br     The bottom right corner
10138 </pre>
10139  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
10140  * below are common options that can be passed to any Fx method.</b>
10141  * @cfg {Function} callback A function called when the effect is finished
10142  * @cfg {Object} scope The scope of the effect function
10143  * @cfg {String} easing A valid Easing value for the effect
10144  * @cfg {String} afterCls A css class to apply after the effect
10145  * @cfg {Number} duration The length of time (in seconds) that the effect should last
10146  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
10147  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
10148  * effects that end with the element being visually hidden, ignored otherwise)
10149  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
10150  * a function which returns such a specification that will be applied to the Element after the effect finishes
10151  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
10152  * @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
10153  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10154  */
10155 Roo.Fx = {
10156         /**
10157          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10158          * origin for the slide effect.  This function automatically handles wrapping the element with
10159          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10160          * Usage:
10161          *<pre><code>
10162 // default: slide the element in from the top
10163 el.slideIn();
10164
10165 // custom: slide the element in from the right with a 2-second duration
10166 el.slideIn('r', { duration: 2 });
10167
10168 // common config options shown with default values
10169 el.slideIn('t', {
10170     easing: 'easeOut',
10171     duration: .5
10172 });
10173 </code></pre>
10174          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10175          * @param {Object} options (optional) Object literal with any of the Fx config options
10176          * @return {Roo.Element} The Element
10177          */
10178     slideIn : function(anchor, o){
10179         var el = this.getFxEl();
10180         o = o || {};
10181
10182         el.queueFx(o, function(){
10183
10184             anchor = anchor || "t";
10185
10186             // fix display to visibility
10187             this.fixDisplay();
10188
10189             // restore values after effect
10190             var r = this.getFxRestore();
10191             var b = this.getBox();
10192             // fixed size for slide
10193             this.setSize(b);
10194
10195             // wrap if needed
10196             var wrap = this.fxWrap(r.pos, o, "hidden");
10197
10198             var st = this.dom.style;
10199             st.visibility = "visible";
10200             st.position = "absolute";
10201
10202             // clear out temp styles after slide and unwrap
10203             var after = function(){
10204                 el.fxUnwrap(wrap, r.pos, o);
10205                 st.width = r.width;
10206                 st.height = r.height;
10207                 el.afterFx(o);
10208             };
10209             // time to calc the positions
10210             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10211
10212             switch(anchor.toLowerCase()){
10213                 case "t":
10214                     wrap.setSize(b.width, 0);
10215                     st.left = st.bottom = "0";
10216                     a = {height: bh};
10217                 break;
10218                 case "l":
10219                     wrap.setSize(0, b.height);
10220                     st.right = st.top = "0";
10221                     a = {width: bw};
10222                 break;
10223                 case "r":
10224                     wrap.setSize(0, b.height);
10225                     wrap.setX(b.right);
10226                     st.left = st.top = "0";
10227                     a = {width: bw, points: pt};
10228                 break;
10229                 case "b":
10230                     wrap.setSize(b.width, 0);
10231                     wrap.setY(b.bottom);
10232                     st.left = st.top = "0";
10233                     a = {height: bh, points: pt};
10234                 break;
10235                 case "tl":
10236                     wrap.setSize(0, 0);
10237                     st.right = st.bottom = "0";
10238                     a = {width: bw, height: bh};
10239                 break;
10240                 case "bl":
10241                     wrap.setSize(0, 0);
10242                     wrap.setY(b.y+b.height);
10243                     st.right = st.top = "0";
10244                     a = {width: bw, height: bh, points: pt};
10245                 break;
10246                 case "br":
10247                     wrap.setSize(0, 0);
10248                     wrap.setXY([b.right, b.bottom]);
10249                     st.left = st.top = "0";
10250                     a = {width: bw, height: bh, points: pt};
10251                 break;
10252                 case "tr":
10253                     wrap.setSize(0, 0);
10254                     wrap.setX(b.x+b.width);
10255                     st.left = st.bottom = "0";
10256                     a = {width: bw, height: bh, points: pt};
10257                 break;
10258             }
10259             this.dom.style.visibility = "visible";
10260             wrap.show();
10261
10262             arguments.callee.anim = wrap.fxanim(a,
10263                 o,
10264                 'motion',
10265                 .5,
10266                 'easeOut', after);
10267         });
10268         return this;
10269     },
10270     
10271         /**
10272          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10273          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10274          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10275          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10276          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10277          * Usage:
10278          *<pre><code>
10279 // default: slide the element out to the top
10280 el.slideOut();
10281
10282 // custom: slide the element out to the right with a 2-second duration
10283 el.slideOut('r', { duration: 2 });
10284
10285 // common config options shown with default values
10286 el.slideOut('t', {
10287     easing: 'easeOut',
10288     duration: .5,
10289     remove: false,
10290     useDisplay: false
10291 });
10292 </code></pre>
10293          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10294          * @param {Object} options (optional) Object literal with any of the Fx config options
10295          * @return {Roo.Element} The Element
10296          */
10297     slideOut : function(anchor, o){
10298         var el = this.getFxEl();
10299         o = o || {};
10300
10301         el.queueFx(o, function(){
10302
10303             anchor = anchor || "t";
10304
10305             // restore values after effect
10306             var r = this.getFxRestore();
10307             
10308             var b = this.getBox();
10309             // fixed size for slide
10310             this.setSize(b);
10311
10312             // wrap if needed
10313             var wrap = this.fxWrap(r.pos, o, "visible");
10314
10315             var st = this.dom.style;
10316             st.visibility = "visible";
10317             st.position = "absolute";
10318
10319             wrap.setSize(b);
10320
10321             var after = function(){
10322                 if(o.useDisplay){
10323                     el.setDisplayed(false);
10324                 }else{
10325                     el.hide();
10326                 }
10327
10328                 el.fxUnwrap(wrap, r.pos, o);
10329
10330                 st.width = r.width;
10331                 st.height = r.height;
10332
10333                 el.afterFx(o);
10334             };
10335
10336             var a, zero = {to: 0};
10337             switch(anchor.toLowerCase()){
10338                 case "t":
10339                     st.left = st.bottom = "0";
10340                     a = {height: zero};
10341                 break;
10342                 case "l":
10343                     st.right = st.top = "0";
10344                     a = {width: zero};
10345                 break;
10346                 case "r":
10347                     st.left = st.top = "0";
10348                     a = {width: zero, points: {to:[b.right, b.y]}};
10349                 break;
10350                 case "b":
10351                     st.left = st.top = "0";
10352                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10353                 break;
10354                 case "tl":
10355                     st.right = st.bottom = "0";
10356                     a = {width: zero, height: zero};
10357                 break;
10358                 case "bl":
10359                     st.right = st.top = "0";
10360                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10361                 break;
10362                 case "br":
10363                     st.left = st.top = "0";
10364                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10365                 break;
10366                 case "tr":
10367                     st.left = st.bottom = "0";
10368                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10369                 break;
10370             }
10371
10372             arguments.callee.anim = wrap.fxanim(a,
10373                 o,
10374                 'motion',
10375                 .5,
10376                 "easeOut", after);
10377         });
10378         return this;
10379     },
10380
10381         /**
10382          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10383          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10384          * The element must be removed from the DOM using the 'remove' config option if desired.
10385          * Usage:
10386          *<pre><code>
10387 // default
10388 el.puff();
10389
10390 // common config options shown with default values
10391 el.puff({
10392     easing: 'easeOut',
10393     duration: .5,
10394     remove: false,
10395     useDisplay: false
10396 });
10397 </code></pre>
10398          * @param {Object} options (optional) Object literal with any of the Fx config options
10399          * @return {Roo.Element} The Element
10400          */
10401     puff : function(o){
10402         var el = this.getFxEl();
10403         o = o || {};
10404
10405         el.queueFx(o, function(){
10406             this.clearOpacity();
10407             this.show();
10408
10409             // restore values after effect
10410             var r = this.getFxRestore();
10411             var st = this.dom.style;
10412
10413             var after = function(){
10414                 if(o.useDisplay){
10415                     el.setDisplayed(false);
10416                 }else{
10417                     el.hide();
10418                 }
10419
10420                 el.clearOpacity();
10421
10422                 el.setPositioning(r.pos);
10423                 st.width = r.width;
10424                 st.height = r.height;
10425                 st.fontSize = '';
10426                 el.afterFx(o);
10427             };
10428
10429             var width = this.getWidth();
10430             var height = this.getHeight();
10431
10432             arguments.callee.anim = this.fxanim({
10433                     width : {to: this.adjustWidth(width * 2)},
10434                     height : {to: this.adjustHeight(height * 2)},
10435                     points : {by: [-(width * .5), -(height * .5)]},
10436                     opacity : {to: 0},
10437                     fontSize: {to:200, unit: "%"}
10438                 },
10439                 o,
10440                 'motion',
10441                 .5,
10442                 "easeOut", after);
10443         });
10444         return this;
10445     },
10446
10447         /**
10448          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10449          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10450          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10451          * Usage:
10452          *<pre><code>
10453 // default
10454 el.switchOff();
10455
10456 // all config options shown with default values
10457 el.switchOff({
10458     easing: 'easeIn',
10459     duration: .3,
10460     remove: false,
10461     useDisplay: false
10462 });
10463 </code></pre>
10464          * @param {Object} options (optional) Object literal with any of the Fx config options
10465          * @return {Roo.Element} The Element
10466          */
10467     switchOff : function(o){
10468         var el = this.getFxEl();
10469         o = o || {};
10470
10471         el.queueFx(o, function(){
10472             this.clearOpacity();
10473             this.clip();
10474
10475             // restore values after effect
10476             var r = this.getFxRestore();
10477             var st = this.dom.style;
10478
10479             var after = function(){
10480                 if(o.useDisplay){
10481                     el.setDisplayed(false);
10482                 }else{
10483                     el.hide();
10484                 }
10485
10486                 el.clearOpacity();
10487                 el.setPositioning(r.pos);
10488                 st.width = r.width;
10489                 st.height = r.height;
10490
10491                 el.afterFx(o);
10492             };
10493
10494             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10495                 this.clearOpacity();
10496                 (function(){
10497                     this.fxanim({
10498                         height:{to:1},
10499                         points:{by:[0, this.getHeight() * .5]}
10500                     }, o, 'motion', 0.3, 'easeIn', after);
10501                 }).defer(100, this);
10502             });
10503         });
10504         return this;
10505     },
10506
10507     /**
10508      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10509      * changed using the "attr" config option) and then fading back to the original color. If no original
10510      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10511      * Usage:
10512 <pre><code>
10513 // default: highlight background to yellow
10514 el.highlight();
10515
10516 // custom: highlight foreground text to blue for 2 seconds
10517 el.highlight("0000ff", { attr: 'color', duration: 2 });
10518
10519 // common config options shown with default values
10520 el.highlight("ffff9c", {
10521     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10522     endColor: (current color) or "ffffff",
10523     easing: 'easeIn',
10524     duration: 1
10525 });
10526 </code></pre>
10527      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10528      * @param {Object} options (optional) Object literal with any of the Fx config options
10529      * @return {Roo.Element} The Element
10530      */ 
10531     highlight : function(color, o){
10532         var el = this.getFxEl();
10533         o = o || {};
10534
10535         el.queueFx(o, function(){
10536             color = color || "ffff9c";
10537             attr = o.attr || "backgroundColor";
10538
10539             this.clearOpacity();
10540             this.show();
10541
10542             var origColor = this.getColor(attr);
10543             var restoreColor = this.dom.style[attr];
10544             endColor = (o.endColor || origColor) || "ffffff";
10545
10546             var after = function(){
10547                 el.dom.style[attr] = restoreColor;
10548                 el.afterFx(o);
10549             };
10550
10551             var a = {};
10552             a[attr] = {from: color, to: endColor};
10553             arguments.callee.anim = this.fxanim(a,
10554                 o,
10555                 'color',
10556                 1,
10557                 'easeIn', after);
10558         });
10559         return this;
10560     },
10561
10562    /**
10563     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10564     * Usage:
10565 <pre><code>
10566 // default: a single light blue ripple
10567 el.frame();
10568
10569 // custom: 3 red ripples lasting 3 seconds total
10570 el.frame("ff0000", 3, { duration: 3 });
10571
10572 // common config options shown with default values
10573 el.frame("C3DAF9", 1, {
10574     duration: 1 //duration of entire animation (not each individual ripple)
10575     // Note: Easing is not configurable and will be ignored if included
10576 });
10577 </code></pre>
10578     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10579     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10580     * @param {Object} options (optional) Object literal with any of the Fx config options
10581     * @return {Roo.Element} The Element
10582     */
10583     frame : function(color, count, o){
10584         var el = this.getFxEl();
10585         o = o || {};
10586
10587         el.queueFx(o, function(){
10588             color = color || "#C3DAF9";
10589             if(color.length == 6){
10590                 color = "#" + color;
10591             }
10592             count = count || 1;
10593             duration = o.duration || 1;
10594             this.show();
10595
10596             var b = this.getBox();
10597             var animFn = function(){
10598                 var proxy = this.createProxy({
10599
10600                      style:{
10601                         visbility:"hidden",
10602                         position:"absolute",
10603                         "z-index":"35000", // yee haw
10604                         border:"0px solid " + color
10605                      }
10606                   });
10607                 var scale = Roo.isBorderBox ? 2 : 1;
10608                 proxy.animate({
10609                     top:{from:b.y, to:b.y - 20},
10610                     left:{from:b.x, to:b.x - 20},
10611                     borderWidth:{from:0, to:10},
10612                     opacity:{from:1, to:0},
10613                     height:{from:b.height, to:(b.height + (20*scale))},
10614                     width:{from:b.width, to:(b.width + (20*scale))}
10615                 }, duration, function(){
10616                     proxy.remove();
10617                 });
10618                 if(--count > 0){
10619                      animFn.defer((duration/2)*1000, this);
10620                 }else{
10621                     el.afterFx(o);
10622                 }
10623             };
10624             animFn.call(this);
10625         });
10626         return this;
10627     },
10628
10629    /**
10630     * Creates a pause before any subsequent queued effects begin.  If there are
10631     * no effects queued after the pause it will have no effect.
10632     * Usage:
10633 <pre><code>
10634 el.pause(1);
10635 </code></pre>
10636     * @param {Number} seconds The length of time to pause (in seconds)
10637     * @return {Roo.Element} The Element
10638     */
10639     pause : function(seconds){
10640         var el = this.getFxEl();
10641         var o = {};
10642
10643         el.queueFx(o, function(){
10644             setTimeout(function(){
10645                 el.afterFx(o);
10646             }, seconds * 1000);
10647         });
10648         return this;
10649     },
10650
10651    /**
10652     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10653     * using the "endOpacity" config option.
10654     * Usage:
10655 <pre><code>
10656 // default: fade in from opacity 0 to 100%
10657 el.fadeIn();
10658
10659 // custom: fade in from opacity 0 to 75% over 2 seconds
10660 el.fadeIn({ endOpacity: .75, duration: 2});
10661
10662 // common config options shown with default values
10663 el.fadeIn({
10664     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10665     easing: 'easeOut',
10666     duration: .5
10667 });
10668 </code></pre>
10669     * @param {Object} options (optional) Object literal with any of the Fx config options
10670     * @return {Roo.Element} The Element
10671     */
10672     fadeIn : function(o){
10673         var el = this.getFxEl();
10674         o = o || {};
10675         el.queueFx(o, function(){
10676             this.setOpacity(0);
10677             this.fixDisplay();
10678             this.dom.style.visibility = 'visible';
10679             var to = o.endOpacity || 1;
10680             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10681                 o, null, .5, "easeOut", function(){
10682                 if(to == 1){
10683                     this.clearOpacity();
10684                 }
10685                 el.afterFx(o);
10686             });
10687         });
10688         return this;
10689     },
10690
10691    /**
10692     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10693     * using the "endOpacity" config option.
10694     * Usage:
10695 <pre><code>
10696 // default: fade out from the element's current opacity to 0
10697 el.fadeOut();
10698
10699 // custom: fade out from the element's current opacity to 25% over 2 seconds
10700 el.fadeOut({ endOpacity: .25, duration: 2});
10701
10702 // common config options shown with default values
10703 el.fadeOut({
10704     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10705     easing: 'easeOut',
10706     duration: .5
10707     remove: false,
10708     useDisplay: false
10709 });
10710 </code></pre>
10711     * @param {Object} options (optional) Object literal with any of the Fx config options
10712     * @return {Roo.Element} The Element
10713     */
10714     fadeOut : function(o){
10715         var el = this.getFxEl();
10716         o = o || {};
10717         el.queueFx(o, function(){
10718             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10719                 o, null, .5, "easeOut", function(){
10720                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10721                      this.dom.style.display = "none";
10722                 }else{
10723                      this.dom.style.visibility = "hidden";
10724                 }
10725                 this.clearOpacity();
10726                 el.afterFx(o);
10727             });
10728         });
10729         return this;
10730     },
10731
10732    /**
10733     * Animates the transition of an element's dimensions from a starting height/width
10734     * to an ending height/width.
10735     * Usage:
10736 <pre><code>
10737 // change height and width to 100x100 pixels
10738 el.scale(100, 100);
10739
10740 // common config options shown with default values.  The height and width will default to
10741 // the element's existing values if passed as null.
10742 el.scale(
10743     [element's width],
10744     [element's height], {
10745     easing: 'easeOut',
10746     duration: .35
10747 });
10748 </code></pre>
10749     * @param {Number} width  The new width (pass undefined to keep the original width)
10750     * @param {Number} height  The new height (pass undefined to keep the original height)
10751     * @param {Object} options (optional) Object literal with any of the Fx config options
10752     * @return {Roo.Element} The Element
10753     */
10754     scale : function(w, h, o){
10755         this.shift(Roo.apply({}, o, {
10756             width: w,
10757             height: h
10758         }));
10759         return this;
10760     },
10761
10762    /**
10763     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10764     * Any of these properties not specified in the config object will not be changed.  This effect 
10765     * requires that at least one new dimension, position or opacity setting must be passed in on
10766     * the config object in order for the function to have any effect.
10767     * Usage:
10768 <pre><code>
10769 // slide the element horizontally to x position 200 while changing the height and opacity
10770 el.shift({ x: 200, height: 50, opacity: .8 });
10771
10772 // common config options shown with default values.
10773 el.shift({
10774     width: [element's width],
10775     height: [element's height],
10776     x: [element's x position],
10777     y: [element's y position],
10778     opacity: [element's opacity],
10779     easing: 'easeOut',
10780     duration: .35
10781 });
10782 </code></pre>
10783     * @param {Object} options  Object literal with any of the Fx config options
10784     * @return {Roo.Element} The Element
10785     */
10786     shift : function(o){
10787         var el = this.getFxEl();
10788         o = o || {};
10789         el.queueFx(o, function(){
10790             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10791             if(w !== undefined){
10792                 a.width = {to: this.adjustWidth(w)};
10793             }
10794             if(h !== undefined){
10795                 a.height = {to: this.adjustHeight(h)};
10796             }
10797             if(x !== undefined || y !== undefined){
10798                 a.points = {to: [
10799                     x !== undefined ? x : this.getX(),
10800                     y !== undefined ? y : this.getY()
10801                 ]};
10802             }
10803             if(op !== undefined){
10804                 a.opacity = {to: op};
10805             }
10806             if(o.xy !== undefined){
10807                 a.points = {to: o.xy};
10808             }
10809             arguments.callee.anim = this.fxanim(a,
10810                 o, 'motion', .35, "easeOut", function(){
10811                 el.afterFx(o);
10812             });
10813         });
10814         return this;
10815     },
10816
10817         /**
10818          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10819          * ending point of the effect.
10820          * Usage:
10821          *<pre><code>
10822 // default: slide the element downward while fading out
10823 el.ghost();
10824
10825 // custom: slide the element out to the right with a 2-second duration
10826 el.ghost('r', { duration: 2 });
10827
10828 // common config options shown with default values
10829 el.ghost('b', {
10830     easing: 'easeOut',
10831     duration: .5
10832     remove: false,
10833     useDisplay: false
10834 });
10835 </code></pre>
10836          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10837          * @param {Object} options (optional) Object literal with any of the Fx config options
10838          * @return {Roo.Element} The Element
10839          */
10840     ghost : function(anchor, o){
10841         var el = this.getFxEl();
10842         o = o || {};
10843
10844         el.queueFx(o, function(){
10845             anchor = anchor || "b";
10846
10847             // restore values after effect
10848             var r = this.getFxRestore();
10849             var w = this.getWidth(),
10850                 h = this.getHeight();
10851
10852             var st = this.dom.style;
10853
10854             var after = function(){
10855                 if(o.useDisplay){
10856                     el.setDisplayed(false);
10857                 }else{
10858                     el.hide();
10859                 }
10860
10861                 el.clearOpacity();
10862                 el.setPositioning(r.pos);
10863                 st.width = r.width;
10864                 st.height = r.height;
10865
10866                 el.afterFx(o);
10867             };
10868
10869             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10870             switch(anchor.toLowerCase()){
10871                 case "t":
10872                     pt.by = [0, -h];
10873                 break;
10874                 case "l":
10875                     pt.by = [-w, 0];
10876                 break;
10877                 case "r":
10878                     pt.by = [w, 0];
10879                 break;
10880                 case "b":
10881                     pt.by = [0, h];
10882                 break;
10883                 case "tl":
10884                     pt.by = [-w, -h];
10885                 break;
10886                 case "bl":
10887                     pt.by = [-w, h];
10888                 break;
10889                 case "br":
10890                     pt.by = [w, h];
10891                 break;
10892                 case "tr":
10893                     pt.by = [w, -h];
10894                 break;
10895             }
10896
10897             arguments.callee.anim = this.fxanim(a,
10898                 o,
10899                 'motion',
10900                 .5,
10901                 "easeOut", after);
10902         });
10903         return this;
10904     },
10905
10906         /**
10907          * Ensures that all effects queued after syncFx is called on the element are
10908          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10909          * @return {Roo.Element} The Element
10910          */
10911     syncFx : function(){
10912         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10913             block : false,
10914             concurrent : true,
10915             stopFx : false
10916         });
10917         return this;
10918     },
10919
10920         /**
10921          * Ensures that all effects queued after sequenceFx is called on the element are
10922          * run in sequence.  This is the opposite of {@link #syncFx}.
10923          * @return {Roo.Element} The Element
10924          */
10925     sequenceFx : function(){
10926         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10927             block : false,
10928             concurrent : false,
10929             stopFx : false
10930         });
10931         return this;
10932     },
10933
10934         /* @private */
10935     nextFx : function(){
10936         var ef = this.fxQueue[0];
10937         if(ef){
10938             ef.call(this);
10939         }
10940     },
10941
10942         /**
10943          * Returns true if the element has any effects actively running or queued, else returns false.
10944          * @return {Boolean} True if element has active effects, else false
10945          */
10946     hasActiveFx : function(){
10947         return this.fxQueue && this.fxQueue[0];
10948     },
10949
10950         /**
10951          * Stops any running effects and clears the element's internal effects queue if it contains
10952          * any additional effects that haven't started yet.
10953          * @return {Roo.Element} The Element
10954          */
10955     stopFx : function(){
10956         if(this.hasActiveFx()){
10957             var cur = this.fxQueue[0];
10958             if(cur && cur.anim && cur.anim.isAnimated()){
10959                 this.fxQueue = [cur]; // clear out others
10960                 cur.anim.stop(true);
10961             }
10962         }
10963         return this;
10964     },
10965
10966         /* @private */
10967     beforeFx : function(o){
10968         if(this.hasActiveFx() && !o.concurrent){
10969            if(o.stopFx){
10970                this.stopFx();
10971                return true;
10972            }
10973            return false;
10974         }
10975         return true;
10976     },
10977
10978         /**
10979          * Returns true if the element is currently blocking so that no other effect can be queued
10980          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10981          * used to ensure that an effect initiated by a user action runs to completion prior to the
10982          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10983          * @return {Boolean} True if blocking, else false
10984          */
10985     hasFxBlock : function(){
10986         var q = this.fxQueue;
10987         return q && q[0] && q[0].block;
10988     },
10989
10990         /* @private */
10991     queueFx : function(o, fn){
10992         if(!this.fxQueue){
10993             this.fxQueue = [];
10994         }
10995         if(!this.hasFxBlock()){
10996             Roo.applyIf(o, this.fxDefaults);
10997             if(!o.concurrent){
10998                 var run = this.beforeFx(o);
10999                 fn.block = o.block;
11000                 this.fxQueue.push(fn);
11001                 if(run){
11002                     this.nextFx();
11003                 }
11004             }else{
11005                 fn.call(this);
11006             }
11007         }
11008         return this;
11009     },
11010
11011         /* @private */
11012     fxWrap : function(pos, o, vis){
11013         var wrap;
11014         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
11015             var wrapXY;
11016             if(o.fixPosition){
11017                 wrapXY = this.getXY();
11018             }
11019             var div = document.createElement("div");
11020             div.style.visibility = vis;
11021             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
11022             wrap.setPositioning(pos);
11023             if(wrap.getStyle("position") == "static"){
11024                 wrap.position("relative");
11025             }
11026             this.clearPositioning('auto');
11027             wrap.clip();
11028             wrap.dom.appendChild(this.dom);
11029             if(wrapXY){
11030                 wrap.setXY(wrapXY);
11031             }
11032         }
11033         return wrap;
11034     },
11035
11036         /* @private */
11037     fxUnwrap : function(wrap, pos, o){
11038         this.clearPositioning();
11039         this.setPositioning(pos);
11040         if(!o.wrap){
11041             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
11042             wrap.remove();
11043         }
11044     },
11045
11046         /* @private */
11047     getFxRestore : function(){
11048         var st = this.dom.style;
11049         return {pos: this.getPositioning(), width: st.width, height : st.height};
11050     },
11051
11052         /* @private */
11053     afterFx : function(o){
11054         if(o.afterStyle){
11055             this.applyStyles(o.afterStyle);
11056         }
11057         if(o.afterCls){
11058             this.addClass(o.afterCls);
11059         }
11060         if(o.remove === true){
11061             this.remove();
11062         }
11063         Roo.callback(o.callback, o.scope, [this]);
11064         if(!o.concurrent){
11065             this.fxQueue.shift();
11066             this.nextFx();
11067         }
11068     },
11069
11070         /* @private */
11071     getFxEl : function(){ // support for composite element fx
11072         return Roo.get(this.dom);
11073     },
11074
11075         /* @private */
11076     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
11077         animType = animType || 'run';
11078         opt = opt || {};
11079         var anim = Roo.lib.Anim[animType](
11080             this.dom, args,
11081             (opt.duration || defaultDur) || .35,
11082             (opt.easing || defaultEase) || 'easeOut',
11083             function(){
11084                 Roo.callback(cb, this);
11085             },
11086             this
11087         );
11088         opt.anim = anim;
11089         return anim;
11090     }
11091 };
11092
11093 // backwords compat
11094 Roo.Fx.resize = Roo.Fx.scale;
11095
11096 //When included, Roo.Fx is automatically applied to Element so that all basic
11097 //effects are available directly via the Element API
11098 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
11099  * Based on:
11100  * Ext JS Library 1.1.1
11101  * Copyright(c) 2006-2007, Ext JS, LLC.
11102  *
11103  * Originally Released Under LGPL - original licence link has changed is not relivant.
11104  *
11105  * Fork - LGPL
11106  * <script type="text/javascript">
11107  */
11108
11109
11110 /**
11111  * @class Roo.CompositeElement
11112  * Standard composite class. Creates a Roo.Element for every element in the collection.
11113  * <br><br>
11114  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11115  * actions will be performed on all the elements in this collection.</b>
11116  * <br><br>
11117  * All methods return <i>this</i> and can be chained.
11118  <pre><code>
11119  var els = Roo.select("#some-el div.some-class", true);
11120  // or select directly from an existing element
11121  var el = Roo.get('some-el');
11122  el.select('div.some-class', true);
11123
11124  els.setWidth(100); // all elements become 100 width
11125  els.hide(true); // all elements fade out and hide
11126  // or
11127  els.setWidth(100).hide(true);
11128  </code></pre>
11129  */
11130 Roo.CompositeElement = function(els){
11131     this.elements = [];
11132     this.addElements(els);
11133 };
11134 Roo.CompositeElement.prototype = {
11135     isComposite: true,
11136     addElements : function(els){
11137         if(!els) {
11138             return this;
11139         }
11140         if(typeof els == "string"){
11141             els = Roo.Element.selectorFunction(els);
11142         }
11143         var yels = this.elements;
11144         var index = yels.length-1;
11145         for(var i = 0, len = els.length; i < len; i++) {
11146                 yels[++index] = Roo.get(els[i]);
11147         }
11148         return this;
11149     },
11150
11151     /**
11152     * Clears this composite and adds the elements returned by the passed selector.
11153     * @param {String/Array} els A string CSS selector, an array of elements or an element
11154     * @return {CompositeElement} this
11155     */
11156     fill : function(els){
11157         this.elements = [];
11158         this.add(els);
11159         return this;
11160     },
11161
11162     /**
11163     * Filters this composite to only elements that match the passed selector.
11164     * @param {String} selector A string CSS selector
11165     * @param {Boolean} inverse return inverse filter (not matches)
11166     * @return {CompositeElement} this
11167     */
11168     filter : function(selector, inverse){
11169         var els = [];
11170         inverse = inverse || false;
11171         this.each(function(el){
11172             var match = inverse ? !el.is(selector) : el.is(selector);
11173             if(match){
11174                 els[els.length] = el.dom;
11175             }
11176         });
11177         this.fill(els);
11178         return this;
11179     },
11180
11181     invoke : function(fn, args){
11182         var els = this.elements;
11183         for(var i = 0, len = els.length; i < len; i++) {
11184                 Roo.Element.prototype[fn].apply(els[i], args);
11185         }
11186         return this;
11187     },
11188     /**
11189     * Adds elements to this composite.
11190     * @param {String/Array} els A string CSS selector, an array of elements or an element
11191     * @return {CompositeElement} this
11192     */
11193     add : function(els){
11194         if(typeof els == "string"){
11195             this.addElements(Roo.Element.selectorFunction(els));
11196         }else if(els.length !== undefined){
11197             this.addElements(els);
11198         }else{
11199             this.addElements([els]);
11200         }
11201         return this;
11202     },
11203     /**
11204     * Calls the passed function passing (el, this, index) for each element in this composite.
11205     * @param {Function} fn The function to call
11206     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11207     * @return {CompositeElement} this
11208     */
11209     each : function(fn, scope){
11210         var els = this.elements;
11211         for(var i = 0, len = els.length; i < len; i++){
11212             if(fn.call(scope || els[i], els[i], this, i) === false) {
11213                 break;
11214             }
11215         }
11216         return this;
11217     },
11218
11219     /**
11220      * Returns the Element object at the specified index
11221      * @param {Number} index
11222      * @return {Roo.Element}
11223      */
11224     item : function(index){
11225         return this.elements[index] || null;
11226     },
11227
11228     /**
11229      * Returns the first Element
11230      * @return {Roo.Element}
11231      */
11232     first : function(){
11233         return this.item(0);
11234     },
11235
11236     /**
11237      * Returns the last Element
11238      * @return {Roo.Element}
11239      */
11240     last : function(){
11241         return this.item(this.elements.length-1);
11242     },
11243
11244     /**
11245      * Returns the number of elements in this composite
11246      * @return Number
11247      */
11248     getCount : function(){
11249         return this.elements.length;
11250     },
11251
11252     /**
11253      * Returns true if this composite contains the passed element
11254      * @return Boolean
11255      */
11256     contains : function(el){
11257         return this.indexOf(el) !== -1;
11258     },
11259
11260     /**
11261      * Returns true if this composite contains the passed element
11262      * @return Boolean
11263      */
11264     indexOf : function(el){
11265         return this.elements.indexOf(Roo.get(el));
11266     },
11267
11268
11269     /**
11270     * Removes the specified element(s).
11271     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11272     * or an array of any of those.
11273     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11274     * @return {CompositeElement} this
11275     */
11276     removeElement : function(el, removeDom){
11277         if(el instanceof Array){
11278             for(var i = 0, len = el.length; i < len; i++){
11279                 this.removeElement(el[i]);
11280             }
11281             return this;
11282         }
11283         var index = typeof el == 'number' ? el : this.indexOf(el);
11284         if(index !== -1){
11285             if(removeDom){
11286                 var d = this.elements[index];
11287                 if(d.dom){
11288                     d.remove();
11289                 }else{
11290                     d.parentNode.removeChild(d);
11291                 }
11292             }
11293             this.elements.splice(index, 1);
11294         }
11295         return this;
11296     },
11297
11298     /**
11299     * Replaces the specified element with the passed element.
11300     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11301     * to replace.
11302     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11303     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11304     * @return {CompositeElement} this
11305     */
11306     replaceElement : function(el, replacement, domReplace){
11307         var index = typeof el == 'number' ? el : this.indexOf(el);
11308         if(index !== -1){
11309             if(domReplace){
11310                 this.elements[index].replaceWith(replacement);
11311             }else{
11312                 this.elements.splice(index, 1, Roo.get(replacement))
11313             }
11314         }
11315         return this;
11316     },
11317
11318     /**
11319      * Removes all elements.
11320      */
11321     clear : function(){
11322         this.elements = [];
11323     }
11324 };
11325 (function(){
11326     Roo.CompositeElement.createCall = function(proto, fnName){
11327         if(!proto[fnName]){
11328             proto[fnName] = function(){
11329                 return this.invoke(fnName, arguments);
11330             };
11331         }
11332     };
11333     for(var fnName in Roo.Element.prototype){
11334         if(typeof Roo.Element.prototype[fnName] == "function"){
11335             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11336         }
11337     };
11338 })();
11339 /*
11340  * Based on:
11341  * Ext JS Library 1.1.1
11342  * Copyright(c) 2006-2007, Ext JS, LLC.
11343  *
11344  * Originally Released Under LGPL - original licence link has changed is not relivant.
11345  *
11346  * Fork - LGPL
11347  * <script type="text/javascript">
11348  */
11349
11350 /**
11351  * @class Roo.CompositeElementLite
11352  * @extends Roo.CompositeElement
11353  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11354  <pre><code>
11355  var els = Roo.select("#some-el div.some-class");
11356  // or select directly from an existing element
11357  var el = Roo.get('some-el');
11358  el.select('div.some-class');
11359
11360  els.setWidth(100); // all elements become 100 width
11361  els.hide(true); // all elements fade out and hide
11362  // or
11363  els.setWidth(100).hide(true);
11364  </code></pre><br><br>
11365  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11366  * actions will be performed on all the elements in this collection.</b>
11367  */
11368 Roo.CompositeElementLite = function(els){
11369     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11370     this.el = new Roo.Element.Flyweight();
11371 };
11372 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11373     addElements : function(els){
11374         if(els){
11375             if(els instanceof Array){
11376                 this.elements = this.elements.concat(els);
11377             }else{
11378                 var yels = this.elements;
11379                 var index = yels.length-1;
11380                 for(var i = 0, len = els.length; i < len; i++) {
11381                     yels[++index] = els[i];
11382                 }
11383             }
11384         }
11385         return this;
11386     },
11387     invoke : function(fn, args){
11388         var els = this.elements;
11389         var el = this.el;
11390         for(var i = 0, len = els.length; i < len; i++) {
11391             el.dom = els[i];
11392                 Roo.Element.prototype[fn].apply(el, args);
11393         }
11394         return this;
11395     },
11396     /**
11397      * Returns a flyweight Element of the dom element object at the specified index
11398      * @param {Number} index
11399      * @return {Roo.Element}
11400      */
11401     item : function(index){
11402         if(!this.elements[index]){
11403             return null;
11404         }
11405         this.el.dom = this.elements[index];
11406         return this.el;
11407     },
11408
11409     // fixes scope with flyweight
11410     addListener : function(eventName, handler, scope, opt){
11411         var els = this.elements;
11412         for(var i = 0, len = els.length; i < len; i++) {
11413             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11414         }
11415         return this;
11416     },
11417
11418     /**
11419     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11420     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11421     * a reference to the dom node, use el.dom.</b>
11422     * @param {Function} fn The function to call
11423     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11424     * @return {CompositeElement} this
11425     */
11426     each : function(fn, scope){
11427         var els = this.elements;
11428         var el = this.el;
11429         for(var i = 0, len = els.length; i < len; i++){
11430             el.dom = els[i];
11431                 if(fn.call(scope || el, el, this, i) === false){
11432                 break;
11433             }
11434         }
11435         return this;
11436     },
11437
11438     indexOf : function(el){
11439         return this.elements.indexOf(Roo.getDom(el));
11440     },
11441
11442     replaceElement : function(el, replacement, domReplace){
11443         var index = typeof el == 'number' ? el : this.indexOf(el);
11444         if(index !== -1){
11445             replacement = Roo.getDom(replacement);
11446             if(domReplace){
11447                 var d = this.elements[index];
11448                 d.parentNode.insertBefore(replacement, d);
11449                 d.parentNode.removeChild(d);
11450             }
11451             this.elements.splice(index, 1, replacement);
11452         }
11453         return this;
11454     }
11455 });
11456 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11457
11458 /*
11459  * Based on:
11460  * Ext JS Library 1.1.1
11461  * Copyright(c) 2006-2007, Ext JS, LLC.
11462  *
11463  * Originally Released Under LGPL - original licence link has changed is not relivant.
11464  *
11465  * Fork - LGPL
11466  * <script type="text/javascript">
11467  */
11468
11469  
11470
11471 /**
11472  * @class Roo.data.Connection
11473  * @extends Roo.util.Observable
11474  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11475  * either to a configured URL, or to a URL specified at request time. 
11476  * 
11477  * Requests made by this class are asynchronous, and will return immediately. No data from
11478  * the server will be available to the statement immediately following the {@link #request} call.
11479  * To process returned data, use a callback in the request options object, or an event listener.
11480  * 
11481  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11482  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11483  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11484  * property and, if present, the IFRAME's XML document as the responseXML property.
11485  * 
11486  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11487  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11488  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11489  * standard DOM methods.
11490  * @constructor
11491  * @param {Object} config a configuration object.
11492  */
11493 Roo.data.Connection = function(config){
11494     Roo.apply(this, config);
11495     this.addEvents({
11496         /**
11497          * @event beforerequest
11498          * Fires before a network request is made to retrieve a data object.
11499          * @param {Connection} conn This Connection object.
11500          * @param {Object} options The options config object passed to the {@link #request} method.
11501          */
11502         "beforerequest" : true,
11503         /**
11504          * @event requestcomplete
11505          * Fires if the request was successfully completed.
11506          * @param {Connection} conn This Connection object.
11507          * @param {Object} response The XHR object containing the response data.
11508          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11509          * @param {Object} options The options config object passed to the {@link #request} method.
11510          */
11511         "requestcomplete" : true,
11512         /**
11513          * @event requestexception
11514          * Fires if an error HTTP status was returned from the server.
11515          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11516          * @param {Connection} conn This Connection object.
11517          * @param {Object} response The XHR object containing the response data.
11518          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11519          * @param {Object} options The options config object passed to the {@link #request} method.
11520          */
11521         "requestexception" : true
11522     });
11523     Roo.data.Connection.superclass.constructor.call(this);
11524 };
11525
11526 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11527     /**
11528      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11529      */
11530     /**
11531      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11532      * extra parameters to each request made by this object. (defaults to undefined)
11533      */
11534     /**
11535      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11536      *  to each request made by this object. (defaults to undefined)
11537      */
11538     /**
11539      * @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)
11540      */
11541     /**
11542      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11543      */
11544     timeout : 30000,
11545     /**
11546      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11547      * @type Boolean
11548      */
11549     autoAbort:false,
11550
11551     /**
11552      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11553      * @type Boolean
11554      */
11555     disableCaching: true,
11556
11557     /**
11558      * Sends an HTTP request to a remote server.
11559      * @param {Object} options An object which may contain the following properties:<ul>
11560      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11561      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11562      * request, a url encoded string or a function to call to get either.</li>
11563      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11564      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11565      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11566      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11567      * <li>options {Object} The parameter to the request call.</li>
11568      * <li>success {Boolean} True if the request succeeded.</li>
11569      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11570      * </ul></li>
11571      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11572      * The callback is passed the following parameters:<ul>
11573      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11574      * <li>options {Object} The parameter to the request call.</li>
11575      * </ul></li>
11576      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11577      * The callback is passed the following parameters:<ul>
11578      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11579      * <li>options {Object} The parameter to the request call.</li>
11580      * </ul></li>
11581      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11582      * for the callback function. Defaults to the browser window.</li>
11583      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11584      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11585      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11586      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11587      * params for the post data. Any params will be appended to the URL.</li>
11588      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11589      * </ul>
11590      * @return {Number} transactionId
11591      */
11592     request : function(o){
11593         if(this.fireEvent("beforerequest", this, o) !== false){
11594             var p = o.params;
11595
11596             if(typeof p == "function"){
11597                 p = p.call(o.scope||window, o);
11598             }
11599             if(typeof p == "object"){
11600                 p = Roo.urlEncode(o.params);
11601             }
11602             if(this.extraParams){
11603                 var extras = Roo.urlEncode(this.extraParams);
11604                 p = p ? (p + '&' + extras) : extras;
11605             }
11606
11607             var url = o.url || this.url;
11608             if(typeof url == 'function'){
11609                 url = url.call(o.scope||window, o);
11610             }
11611
11612             if(o.form){
11613                 var form = Roo.getDom(o.form);
11614                 url = url || form.action;
11615
11616                 var enctype = form.getAttribute("enctype");
11617                 
11618                 if (o.formData) {
11619                     return this.doFormDataUpload(o,p,url);
11620                 }
11621                 
11622                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11623                     return this.doFormUpload(o, p, url);
11624                 }
11625                 var f = Roo.lib.Ajax.serializeForm(form);
11626                 p = p ? (p + '&' + f) : f;
11627             }
11628
11629             var hs = o.headers;
11630             if(this.defaultHeaders){
11631                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11632                 if(!o.headers){
11633                     o.headers = hs;
11634                 }
11635             }
11636
11637             var cb = {
11638                 success: this.handleResponse,
11639                 failure: this.handleFailure,
11640                 scope: this,
11641                 argument: {options: o},
11642                 timeout : o.timeout || this.timeout
11643             };
11644
11645             var method = o.method||this.method||(p ? "POST" : "GET");
11646
11647             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11648                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11649             }
11650
11651             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11652                 if(o.autoAbort){
11653                     this.abort();
11654                 }
11655             }else if(this.autoAbort !== false){
11656                 this.abort();
11657             }
11658
11659             if((method == 'GET' && p) || o.xmlData){
11660                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11661                 p = '';
11662             }
11663             Roo.lib.Ajax.useDefaultHeader = typeof(o.headers) == 'undefined' || typeof(o.headers['Content-Type']) == 'undefined';
11664             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11665             Roo.lib.Ajax.useDefaultHeader == true;
11666             return this.transId;
11667         }else{
11668             Roo.callback(o.callback, o.scope, [o, null, null]);
11669             return null;
11670         }
11671     },
11672
11673     /**
11674      * Determine whether this object has a request outstanding.
11675      * @param {Number} transactionId (Optional) defaults to the last transaction
11676      * @return {Boolean} True if there is an outstanding request.
11677      */
11678     isLoading : function(transId){
11679         if(transId){
11680             return Roo.lib.Ajax.isCallInProgress(transId);
11681         }else{
11682             return this.transId ? true : false;
11683         }
11684     },
11685
11686     /**
11687      * Aborts any outstanding request.
11688      * @param {Number} transactionId (Optional) defaults to the last transaction
11689      */
11690     abort : function(transId){
11691         if(transId || this.isLoading()){
11692             Roo.lib.Ajax.abort(transId || this.transId);
11693         }
11694     },
11695
11696     // private
11697     handleResponse : function(response){
11698         this.transId = false;
11699         var options = response.argument.options;
11700         response.argument = options ? options.argument : null;
11701         this.fireEvent("requestcomplete", this, response, options);
11702         Roo.callback(options.success, options.scope, [response, options]);
11703         Roo.callback(options.callback, options.scope, [options, true, response]);
11704     },
11705
11706     // private
11707     handleFailure : function(response, e){
11708         this.transId = false;
11709         var options = response.argument.options;
11710         response.argument = options ? options.argument : null;
11711         this.fireEvent("requestexception", this, response, options, e);
11712         Roo.callback(options.failure, options.scope, [response, options]);
11713         Roo.callback(options.callback, options.scope, [options, false, response]);
11714     },
11715
11716     // private
11717     doFormUpload : function(o, ps, url){
11718         var id = Roo.id();
11719         var frame = document.createElement('iframe');
11720         frame.id = id;
11721         frame.name = id;
11722         frame.className = 'x-hidden';
11723         if(Roo.isIE){
11724             frame.src = Roo.SSL_SECURE_URL;
11725         }
11726         document.body.appendChild(frame);
11727
11728         if(Roo.isIE){
11729            document.frames[id].name = id;
11730         }
11731
11732         var form = Roo.getDom(o.form);
11733         form.target = id;
11734         form.method = 'POST';
11735         form.enctype = form.encoding = 'multipart/form-data';
11736         if(url){
11737             form.action = url;
11738         }
11739
11740         var hiddens, hd;
11741         if(ps){ // add dynamic params
11742             hiddens = [];
11743             ps = Roo.urlDecode(ps, false);
11744             for(var k in ps){
11745                 if(ps.hasOwnProperty(k)){
11746                     hd = document.createElement('input');
11747                     hd.type = 'hidden';
11748                     hd.name = k;
11749                     hd.value = ps[k];
11750                     form.appendChild(hd);
11751                     hiddens.push(hd);
11752                 }
11753             }
11754         }
11755
11756         function cb(){
11757             var r = {  // bogus response object
11758                 responseText : '',
11759                 responseXML : null
11760             };
11761
11762             r.argument = o ? o.argument : null;
11763
11764             try { //
11765                 var doc;
11766                 if(Roo.isIE){
11767                     doc = frame.contentWindow.document;
11768                 }else {
11769                     doc = (frame.contentDocument || window.frames[id].document);
11770                 }
11771                 if(doc && doc.body){
11772                     r.responseText = doc.body.innerHTML;
11773                 }
11774                 if(doc && doc.XMLDocument){
11775                     r.responseXML = doc.XMLDocument;
11776                 }else {
11777                     r.responseXML = doc;
11778                 }
11779             }
11780             catch(e) {
11781                 // ignore
11782             }
11783
11784             Roo.EventManager.removeListener(frame, 'load', cb, this);
11785
11786             this.fireEvent("requestcomplete", this, r, o);
11787             Roo.callback(o.success, o.scope, [r, o]);
11788             Roo.callback(o.callback, o.scope, [o, true, r]);
11789
11790             setTimeout(function(){document.body.removeChild(frame);}, 100);
11791         }
11792
11793         Roo.EventManager.on(frame, 'load', cb, this);
11794         form.submit();
11795
11796         if(hiddens){ // remove dynamic params
11797             for(var i = 0, len = hiddens.length; i < len; i++){
11798                 form.removeChild(hiddens[i]);
11799             }
11800         }
11801     },
11802     // this is a 'formdata version???'
11803     
11804     
11805     doFormDataUpload : function(o, ps, url)
11806     {
11807         var form = Roo.getDom(o.form);
11808         form.enctype = form.encoding = 'multipart/form-data';
11809         var formData = o.formData === true ? new FormData(form) : o.formData;
11810       
11811         var cb = {
11812             success: this.handleResponse,
11813             failure: this.handleFailure,
11814             scope: this,
11815             argument: {options: o},
11816             timeout : o.timeout || this.timeout
11817         };
11818  
11819         if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11820             if(o.autoAbort){
11821                 this.abort();
11822             }
11823         }else if(this.autoAbort !== false){
11824             this.abort();
11825         }
11826
11827         //Roo.lib.Ajax.defaultPostHeader = null;
11828         Roo.lib.Ajax.useDefaultHeader = false;
11829         this.transId = Roo.lib.Ajax.request( "POST", url, cb, o.formData, o);
11830         Roo.lib.Ajax.useDefaultHeader = true;
11831  
11832          
11833     }
11834     
11835 });
11836 /*
11837  * Based on:
11838  * Ext JS Library 1.1.1
11839  * Copyright(c) 2006-2007, Ext JS, LLC.
11840  *
11841  * Originally Released Under LGPL - original licence link has changed is not relivant.
11842  *
11843  * Fork - LGPL
11844  * <script type="text/javascript">
11845  */
11846  
11847 /**
11848  * Global Ajax request class.
11849  * 
11850  * @class Roo.Ajax
11851  * @extends Roo.data.Connection
11852  * @static
11853  * 
11854  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11855  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11856  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11857  * @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)
11858  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11859  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11860  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11861  */
11862 Roo.Ajax = new Roo.data.Connection({
11863     // fix up the docs
11864     /**
11865      * @scope Roo.Ajax
11866      * @type {Boolear} 
11867      */
11868     autoAbort : false,
11869
11870     /**
11871      * Serialize the passed form into a url encoded string
11872      * @scope Roo.Ajax
11873      * @param {String/HTMLElement} form
11874      * @return {String}
11875      */
11876     serializeForm : function(form){
11877         return Roo.lib.Ajax.serializeForm(form);
11878     }
11879 });/*
11880  * Based on:
11881  * Ext JS Library 1.1.1
11882  * Copyright(c) 2006-2007, Ext JS, LLC.
11883  *
11884  * Originally Released Under LGPL - original licence link has changed is not relivant.
11885  *
11886  * Fork - LGPL
11887  * <script type="text/javascript">
11888  */
11889
11890  
11891 /**
11892  * @class Roo.UpdateManager
11893  * @extends Roo.util.Observable
11894  * Provides AJAX-style update for Element object.<br><br>
11895  * Usage:<br>
11896  * <pre><code>
11897  * // Get it from a Roo.Element object
11898  * var el = Roo.get("foo");
11899  * var mgr = el.getUpdateManager();
11900  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11901  * ...
11902  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11903  * <br>
11904  * // or directly (returns the same UpdateManager instance)
11905  * var mgr = new Roo.UpdateManager("myElementId");
11906  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11907  * mgr.on("update", myFcnNeedsToKnow);
11908  * <br>
11909    // short handed call directly from the element object
11910    Roo.get("foo").load({
11911         url: "bar.php",
11912         scripts:true,
11913         params: "for=bar",
11914         text: "Loading Foo..."
11915    });
11916  * </code></pre>
11917  * @constructor
11918  * Create new UpdateManager directly.
11919  * @param {String/HTMLElement/Roo.Element} el The element to update
11920  * @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).
11921  */
11922 Roo.UpdateManager = function(el, forceNew){
11923     el = Roo.get(el);
11924     if(!forceNew && el.updateManager){
11925         return el.updateManager;
11926     }
11927     /**
11928      * The Element object
11929      * @type Roo.Element
11930      */
11931     this.el = el;
11932     /**
11933      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11934      * @type String
11935      */
11936     this.defaultUrl = null;
11937
11938     this.addEvents({
11939         /**
11940          * @event beforeupdate
11941          * Fired before an update is made, return false from your handler and the update is cancelled.
11942          * @param {Roo.Element} el
11943          * @param {String/Object/Function} url
11944          * @param {String/Object} params
11945          */
11946         "beforeupdate": true,
11947         /**
11948          * @event update
11949          * Fired after successful update is made.
11950          * @param {Roo.Element} el
11951          * @param {Object} oResponseObject The response Object
11952          */
11953         "update": true,
11954         /**
11955          * @event failure
11956          * Fired on update failure.
11957          * @param {Roo.Element} el
11958          * @param {Object} oResponseObject The response Object
11959          */
11960         "failure": true
11961     });
11962     var d = Roo.UpdateManager.defaults;
11963     /**
11964      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11965      * @type String
11966      */
11967     this.sslBlankUrl = d.sslBlankUrl;
11968     /**
11969      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11970      * @type Boolean
11971      */
11972     this.disableCaching = d.disableCaching;
11973     /**
11974      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11975      * @type String
11976      */
11977     this.indicatorText = d.indicatorText;
11978     /**
11979      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11980      * @type String
11981      */
11982     this.showLoadIndicator = d.showLoadIndicator;
11983     /**
11984      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11985      * @type Number
11986      */
11987     this.timeout = d.timeout;
11988
11989     /**
11990      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11991      * @type Boolean
11992      */
11993     this.loadScripts = d.loadScripts;
11994
11995     /**
11996      * Transaction object of current executing transaction
11997      */
11998     this.transaction = null;
11999
12000     /**
12001      * @private
12002      */
12003     this.autoRefreshProcId = null;
12004     /**
12005      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
12006      * @type Function
12007      */
12008     this.refreshDelegate = this.refresh.createDelegate(this);
12009     /**
12010      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
12011      * @type Function
12012      */
12013     this.updateDelegate = this.update.createDelegate(this);
12014     /**
12015      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
12016      * @type Function
12017      */
12018     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
12019     /**
12020      * @private
12021      */
12022     this.successDelegate = this.processSuccess.createDelegate(this);
12023     /**
12024      * @private
12025      */
12026     this.failureDelegate = this.processFailure.createDelegate(this);
12027
12028     if(!this.renderer){
12029      /**
12030       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
12031       */
12032     this.renderer = new Roo.UpdateManager.BasicRenderer();
12033     }
12034     
12035     Roo.UpdateManager.superclass.constructor.call(this);
12036 };
12037
12038 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
12039     /**
12040      * Get the Element this UpdateManager is bound to
12041      * @return {Roo.Element} The element
12042      */
12043     getEl : function(){
12044         return this.el;
12045     },
12046     /**
12047      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
12048      * @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:
12049 <pre><code>
12050 um.update({<br/>
12051     url: "your-url.php",<br/>
12052     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
12053     callback: yourFunction,<br/>
12054     scope: yourObject, //(optional scope)  <br/>
12055     discardUrl: false, <br/>
12056     nocache: false,<br/>
12057     text: "Loading...",<br/>
12058     timeout: 30,<br/>
12059     scripts: false<br/>
12060 });
12061 </code></pre>
12062      * The only required property is url. The optional properties nocache, text and scripts
12063      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
12064      * @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}
12065      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12066      * @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.
12067      */
12068     update : function(url, params, callback, discardUrl){
12069         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
12070             var method = this.method,
12071                 cfg;
12072             if(typeof url == "object"){ // must be config object
12073                 cfg = url;
12074                 url = cfg.url;
12075                 params = params || cfg.params;
12076                 callback = callback || cfg.callback;
12077                 discardUrl = discardUrl || cfg.discardUrl;
12078                 if(callback && cfg.scope){
12079                     callback = callback.createDelegate(cfg.scope);
12080                 }
12081                 if(typeof cfg.method != "undefined"){method = cfg.method;};
12082                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
12083                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
12084                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
12085                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
12086             }
12087             this.showLoading();
12088             if(!discardUrl){
12089                 this.defaultUrl = url;
12090             }
12091             if(typeof url == "function"){
12092                 url = url.call(this);
12093             }
12094
12095             method = method || (params ? "POST" : "GET");
12096             if(method == "GET"){
12097                 url = this.prepareUrl(url);
12098             }
12099
12100             var o = Roo.apply(cfg ||{}, {
12101                 url : url,
12102                 params: params,
12103                 success: this.successDelegate,
12104                 failure: this.failureDelegate,
12105                 callback: undefined,
12106                 timeout: (this.timeout*1000),
12107                 argument: {"url": url, "form": null, "callback": callback, "params": params}
12108             });
12109             Roo.log("updated manager called with timeout of " + o.timeout);
12110             this.transaction = Roo.Ajax.request(o);
12111         }
12112     },
12113
12114     /**
12115      * 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.
12116      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
12117      * @param {String/HTMLElement} form The form Id or form element
12118      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
12119      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
12120      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12121      */
12122     formUpdate : function(form, url, reset, callback){
12123         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
12124             if(typeof url == "function"){
12125                 url = url.call(this);
12126             }
12127             form = Roo.getDom(form);
12128             this.transaction = Roo.Ajax.request({
12129                 form: form,
12130                 url:url,
12131                 success: this.successDelegate,
12132                 failure: this.failureDelegate,
12133                 timeout: (this.timeout*1000),
12134                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
12135             });
12136             this.showLoading.defer(1, this);
12137         }
12138     },
12139
12140     /**
12141      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
12142      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12143      */
12144     refresh : function(callback){
12145         if(this.defaultUrl == null){
12146             return;
12147         }
12148         this.update(this.defaultUrl, null, callback, true);
12149     },
12150
12151     /**
12152      * Set this element to auto refresh.
12153      * @param {Number} interval How often to update (in seconds).
12154      * @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)
12155      * @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}
12156      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12157      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
12158      */
12159     startAutoRefresh : function(interval, url, params, callback, refreshNow){
12160         if(refreshNow){
12161             this.update(url || this.defaultUrl, params, callback, true);
12162         }
12163         if(this.autoRefreshProcId){
12164             clearInterval(this.autoRefreshProcId);
12165         }
12166         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
12167     },
12168
12169     /**
12170      * Stop auto refresh on this element.
12171      */
12172      stopAutoRefresh : function(){
12173         if(this.autoRefreshProcId){
12174             clearInterval(this.autoRefreshProcId);
12175             delete this.autoRefreshProcId;
12176         }
12177     },
12178
12179     isAutoRefreshing : function(){
12180        return this.autoRefreshProcId ? true : false;
12181     },
12182     /**
12183      * Called to update the element to "Loading" state. Override to perform custom action.
12184      */
12185     showLoading : function(){
12186         if(this.showLoadIndicator){
12187             this.el.update(this.indicatorText);
12188         }
12189     },
12190
12191     /**
12192      * Adds unique parameter to query string if disableCaching = true
12193      * @private
12194      */
12195     prepareUrl : function(url){
12196         if(this.disableCaching){
12197             var append = "_dc=" + (new Date().getTime());
12198             if(url.indexOf("?") !== -1){
12199                 url += "&" + append;
12200             }else{
12201                 url += "?" + append;
12202             }
12203         }
12204         return url;
12205     },
12206
12207     /**
12208      * @private
12209      */
12210     processSuccess : function(response){
12211         this.transaction = null;
12212         if(response.argument.form && response.argument.reset){
12213             try{ // put in try/catch since some older FF releases had problems with this
12214                 response.argument.form.reset();
12215             }catch(e){}
12216         }
12217         if(this.loadScripts){
12218             this.renderer.render(this.el, response, this,
12219                 this.updateComplete.createDelegate(this, [response]));
12220         }else{
12221             this.renderer.render(this.el, response, this);
12222             this.updateComplete(response);
12223         }
12224     },
12225
12226     updateComplete : function(response){
12227         this.fireEvent("update", this.el, response);
12228         if(typeof response.argument.callback == "function"){
12229             response.argument.callback(this.el, true, response);
12230         }
12231     },
12232
12233     /**
12234      * @private
12235      */
12236     processFailure : function(response){
12237         this.transaction = null;
12238         this.fireEvent("failure", this.el, response);
12239         if(typeof response.argument.callback == "function"){
12240             response.argument.callback(this.el, false, response);
12241         }
12242     },
12243
12244     /**
12245      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12246      * @param {Object} renderer The object implementing the render() method
12247      */
12248     setRenderer : function(renderer){
12249         this.renderer = renderer;
12250     },
12251
12252     getRenderer : function(){
12253        return this.renderer;
12254     },
12255
12256     /**
12257      * Set the defaultUrl used for updates
12258      * @param {String/Function} defaultUrl The url or a function to call to get the url
12259      */
12260     setDefaultUrl : function(defaultUrl){
12261         this.defaultUrl = defaultUrl;
12262     },
12263
12264     /**
12265      * Aborts the executing transaction
12266      */
12267     abort : function(){
12268         if(this.transaction){
12269             Roo.Ajax.abort(this.transaction);
12270         }
12271     },
12272
12273     /**
12274      * Returns true if an update is in progress
12275      * @return {Boolean}
12276      */
12277     isUpdating : function(){
12278         if(this.transaction){
12279             return Roo.Ajax.isLoading(this.transaction);
12280         }
12281         return false;
12282     }
12283 });
12284
12285 /**
12286  * @class Roo.UpdateManager.defaults
12287  * @static (not really - but it helps the doc tool)
12288  * The defaults collection enables customizing the default properties of UpdateManager
12289  */
12290    Roo.UpdateManager.defaults = {
12291        /**
12292          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12293          * @type Number
12294          */
12295          timeout : 30,
12296
12297          /**
12298          * True to process scripts by default (Defaults to false).
12299          * @type Boolean
12300          */
12301         loadScripts : false,
12302
12303         /**
12304         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12305         * @type String
12306         */
12307         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12308         /**
12309          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12310          * @type Boolean
12311          */
12312         disableCaching : false,
12313         /**
12314          * Whether to show indicatorText when loading (Defaults to true).
12315          * @type Boolean
12316          */
12317         showLoadIndicator : true,
12318         /**
12319          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12320          * @type String
12321          */
12322         indicatorText : '<div class="loading-indicator">Loading...</div>'
12323    };
12324
12325 /**
12326  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12327  *Usage:
12328  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12329  * @param {String/HTMLElement/Roo.Element} el The element to update
12330  * @param {String} url The url
12331  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12332  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12333  * @static
12334  * @deprecated
12335  * @member Roo.UpdateManager
12336  */
12337 Roo.UpdateManager.updateElement = function(el, url, params, options){
12338     var um = Roo.get(el, true).getUpdateManager();
12339     Roo.apply(um, options);
12340     um.update(url, params, options ? options.callback : null);
12341 };
12342 // alias for backwards compat
12343 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12344 /**
12345  * @class Roo.UpdateManager.BasicRenderer
12346  * Default Content renderer. Updates the elements innerHTML with the responseText.
12347  */
12348 Roo.UpdateManager.BasicRenderer = function(){};
12349
12350 Roo.UpdateManager.BasicRenderer.prototype = {
12351     /**
12352      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12353      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12354      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12355      * @param {Roo.Element} el The element being rendered
12356      * @param {Object} response The YUI Connect response object
12357      * @param {UpdateManager} updateManager The calling update manager
12358      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12359      */
12360      render : function(el, response, updateManager, callback){
12361         el.update(response.responseText, updateManager.loadScripts, callback);
12362     }
12363 };
12364 /*
12365  * Based on:
12366  * Roo JS
12367  * (c)) Alan Knowles
12368  * Licence : LGPL
12369  */
12370
12371
12372 /**
12373  * @class Roo.DomTemplate
12374  * @extends Roo.Template
12375  * An effort at a dom based template engine..
12376  *
12377  * Similar to XTemplate, except it uses dom parsing to create the template..
12378  *
12379  * Supported features:
12380  *
12381  *  Tags:
12382
12383 <pre><code>
12384       {a_variable} - output encoded.
12385       {a_variable.format:("Y-m-d")} - call a method on the variable
12386       {a_variable:raw} - unencoded output
12387       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12388       {a_variable:this.method_on_template(...)} - call a method on the template object.
12389  
12390 </code></pre>
12391  *  The tpl tag:
12392 <pre><code>
12393         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12394         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12395         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12396         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12397   
12398 </code></pre>
12399  *      
12400  */
12401 Roo.DomTemplate = function()
12402 {
12403      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12404      if (this.html) {
12405         this.compile();
12406      }
12407 };
12408
12409
12410 Roo.extend(Roo.DomTemplate, Roo.Template, {
12411     /**
12412      * id counter for sub templates.
12413      */
12414     id : 0,
12415     /**
12416      * flag to indicate if dom parser is inside a pre,
12417      * it will strip whitespace if not.
12418      */
12419     inPre : false,
12420     
12421     /**
12422      * The various sub templates
12423      */
12424     tpls : false,
12425     
12426     
12427     
12428     /**
12429      *
12430      * basic tag replacing syntax
12431      * WORD:WORD()
12432      *
12433      * // you can fake an object call by doing this
12434      *  x.t:(test,tesT) 
12435      * 
12436      */
12437     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12438     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12439     
12440     iterChild : function (node, method) {
12441         
12442         var oldPre = this.inPre;
12443         if (node.tagName == 'PRE') {
12444             this.inPre = true;
12445         }
12446         for( var i = 0; i < node.childNodes.length; i++) {
12447             method.call(this, node.childNodes[i]);
12448         }
12449         this.inPre = oldPre;
12450     },
12451     
12452     
12453     
12454     /**
12455      * compile the template
12456      *
12457      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12458      *
12459      */
12460     compile: function()
12461     {
12462         var s = this.html;
12463         
12464         // covert the html into DOM...
12465         var doc = false;
12466         var div =false;
12467         try {
12468             doc = document.implementation.createHTMLDocument("");
12469             doc.documentElement.innerHTML =   this.html  ;
12470             div = doc.documentElement;
12471         } catch (e) {
12472             // old IE... - nasty -- it causes all sorts of issues.. with
12473             // images getting pulled from server..
12474             div = document.createElement('div');
12475             div.innerHTML = this.html;
12476         }
12477         //doc.documentElement.innerHTML = htmlBody
12478          
12479         
12480         
12481         this.tpls = [];
12482         var _t = this;
12483         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12484         
12485         var tpls = this.tpls;
12486         
12487         // create a top level template from the snippet..
12488         
12489         //Roo.log(div.innerHTML);
12490         
12491         var tpl = {
12492             uid : 'master',
12493             id : this.id++,
12494             attr : false,
12495             value : false,
12496             body : div.innerHTML,
12497             
12498             forCall : false,
12499             execCall : false,
12500             dom : div,
12501             isTop : true
12502             
12503         };
12504         tpls.unshift(tpl);
12505         
12506         
12507         // compile them...
12508         this.tpls = [];
12509         Roo.each(tpls, function(tp){
12510             this.compileTpl(tp);
12511             this.tpls[tp.id] = tp;
12512         }, this);
12513         
12514         this.master = tpls[0];
12515         return this;
12516         
12517         
12518     },
12519     
12520     compileNode : function(node, istop) {
12521         // test for
12522         //Roo.log(node);
12523         
12524         
12525         // skip anything not a tag..
12526         if (node.nodeType != 1) {
12527             if (node.nodeType == 3 && !this.inPre) {
12528                 // reduce white space..
12529                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12530                 
12531             }
12532             return;
12533         }
12534         
12535         var tpl = {
12536             uid : false,
12537             id : false,
12538             attr : false,
12539             value : false,
12540             body : '',
12541             
12542             forCall : false,
12543             execCall : false,
12544             dom : false,
12545             isTop : istop
12546             
12547             
12548         };
12549         
12550         
12551         switch(true) {
12552             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12553             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12554             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12555             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12556             // no default..
12557         }
12558         
12559         
12560         if (!tpl.attr) {
12561             // just itterate children..
12562             this.iterChild(node,this.compileNode);
12563             return;
12564         }
12565         tpl.uid = this.id++;
12566         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12567         node.removeAttribute('roo-'+ tpl.attr);
12568         if (tpl.attr != 'name') {
12569             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12570             node.parentNode.replaceChild(placeholder,  node);
12571         } else {
12572             
12573             var placeholder =  document.createElement('span');
12574             placeholder.className = 'roo-tpl-' + tpl.value;
12575             node.parentNode.replaceChild(placeholder,  node);
12576         }
12577         
12578         // parent now sees '{domtplXXXX}
12579         this.iterChild(node,this.compileNode);
12580         
12581         // we should now have node body...
12582         var div = document.createElement('div');
12583         div.appendChild(node);
12584         tpl.dom = node;
12585         // this has the unfortunate side effect of converting tagged attributes
12586         // eg. href="{...}" into %7C...%7D
12587         // this has been fixed by searching for those combo's although it's a bit hacky..
12588         
12589         
12590         tpl.body = div.innerHTML;
12591         
12592         
12593          
12594         tpl.id = tpl.uid;
12595         switch(tpl.attr) {
12596             case 'for' :
12597                 switch (tpl.value) {
12598                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12599                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12600                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12601                 }
12602                 break;
12603             
12604             case 'exec':
12605                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12606                 break;
12607             
12608             case 'if':     
12609                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12610                 break;
12611             
12612             case 'name':
12613                 tpl.id  = tpl.value; // replace non characters???
12614                 break;
12615             
12616         }
12617         
12618         
12619         this.tpls.push(tpl);
12620         
12621         
12622         
12623     },
12624     
12625     
12626     
12627     
12628     /**
12629      * Compile a segment of the template into a 'sub-template'
12630      *
12631      * 
12632      * 
12633      *
12634      */
12635     compileTpl : function(tpl)
12636     {
12637         var fm = Roo.util.Format;
12638         var useF = this.disableFormats !== true;
12639         
12640         var sep = Roo.isGecko ? "+\n" : ",\n";
12641         
12642         var undef = function(str) {
12643             Roo.debug && Roo.log("Property not found :"  + str);
12644             return '';
12645         };
12646           
12647         //Roo.log(tpl.body);
12648         
12649         
12650         
12651         var fn = function(m, lbrace, name, format, args)
12652         {
12653             //Roo.log("ARGS");
12654             //Roo.log(arguments);
12655             args = args ? args.replace(/\\'/g,"'") : args;
12656             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12657             if (typeof(format) == 'undefined') {
12658                 format =  'htmlEncode'; 
12659             }
12660             if (format == 'raw' ) {
12661                 format = false;
12662             }
12663             
12664             if(name.substr(0, 6) == 'domtpl'){
12665                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12666             }
12667             
12668             // build an array of options to determine if value is undefined..
12669             
12670             // basically get 'xxxx.yyyy' then do
12671             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12672             //    (function () { Roo.log("Property not found"); return ''; })() :
12673             //    ......
12674             
12675             var udef_ar = [];
12676             var lookfor = '';
12677             Roo.each(name.split('.'), function(st) {
12678                 lookfor += (lookfor.length ? '.': '') + st;
12679                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12680             });
12681             
12682             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12683             
12684             
12685             if(format && useF){
12686                 
12687                 args = args ? ',' + args : "";
12688                  
12689                 if(format.substr(0, 5) != "this."){
12690                     format = "fm." + format + '(';
12691                 }else{
12692                     format = 'this.call("'+ format.substr(5) + '", ';
12693                     args = ", values";
12694                 }
12695                 
12696                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12697             }
12698              
12699             if (args && args.length) {
12700                 // called with xxyx.yuu:(test,test)
12701                 // change to ()
12702                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12703             }
12704             // raw.. - :raw modifier..
12705             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12706             
12707         };
12708         var body;
12709         // branched to use + in gecko and [].join() in others
12710         if(Roo.isGecko){
12711             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12712                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12713                     "';};};";
12714         }else{
12715             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12716             body.push(tpl.body.replace(/(\r\n|\n)/g,
12717                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12718             body.push("'].join('');};};");
12719             body = body.join('');
12720         }
12721         
12722         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12723        
12724         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12725         eval(body);
12726         
12727         return this;
12728     },
12729      
12730     /**
12731      * same as applyTemplate, except it's done to one of the subTemplates
12732      * when using named templates, you can do:
12733      *
12734      * var str = pl.applySubTemplate('your-name', values);
12735      *
12736      * 
12737      * @param {Number} id of the template
12738      * @param {Object} values to apply to template
12739      * @param {Object} parent (normaly the instance of this object)
12740      */
12741     applySubTemplate : function(id, values, parent)
12742     {
12743         
12744         
12745         var t = this.tpls[id];
12746         
12747         
12748         try { 
12749             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12750                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12751                 return '';
12752             }
12753         } catch(e) {
12754             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12755             Roo.log(values);
12756           
12757             return '';
12758         }
12759         try { 
12760             
12761             if(t.execCall && t.execCall.call(this, values, parent)){
12762                 return '';
12763             }
12764         } catch(e) {
12765             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12766             Roo.log(values);
12767             return '';
12768         }
12769         
12770         try {
12771             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12772             parent = t.target ? values : parent;
12773             if(t.forCall && vs instanceof Array){
12774                 var buf = [];
12775                 for(var i = 0, len = vs.length; i < len; i++){
12776                     try {
12777                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12778                     } catch (e) {
12779                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12780                         Roo.log(e.body);
12781                         //Roo.log(t.compiled);
12782                         Roo.log(vs[i]);
12783                     }   
12784                 }
12785                 return buf.join('');
12786             }
12787         } catch (e) {
12788             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12789             Roo.log(values);
12790             return '';
12791         }
12792         try {
12793             return t.compiled.call(this, vs, parent);
12794         } catch (e) {
12795             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12796             Roo.log(e.body);
12797             //Roo.log(t.compiled);
12798             Roo.log(values);
12799             return '';
12800         }
12801     },
12802
12803    
12804
12805     applyTemplate : function(values){
12806         return this.master.compiled.call(this, values, {});
12807         //var s = this.subs;
12808     },
12809
12810     apply : function(){
12811         return this.applyTemplate.apply(this, arguments);
12812     }
12813
12814  });
12815
12816 Roo.DomTemplate.from = function(el){
12817     el = Roo.getDom(el);
12818     return new Roo.Domtemplate(el.value || el.innerHTML);
12819 };/*
12820  * Based on:
12821  * Ext JS Library 1.1.1
12822  * Copyright(c) 2006-2007, Ext JS, LLC.
12823  *
12824  * Originally Released Under LGPL - original licence link has changed is not relivant.
12825  *
12826  * Fork - LGPL
12827  * <script type="text/javascript">
12828  */
12829
12830 /**
12831  * @class Roo.util.DelayedTask
12832  * Provides a convenient method of performing setTimeout where a new
12833  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12834  * You can use this class to buffer
12835  * the keypress events for a certain number of milliseconds, and perform only if they stop
12836  * for that amount of time.
12837  * @constructor The parameters to this constructor serve as defaults and are not required.
12838  * @param {Function} fn (optional) The default function to timeout
12839  * @param {Object} scope (optional) The default scope of that timeout
12840  * @param {Array} args (optional) The default Array of arguments
12841  */
12842 Roo.util.DelayedTask = function(fn, scope, args){
12843     var id = null, d, t;
12844
12845     var call = function(){
12846         var now = new Date().getTime();
12847         if(now - t >= d){
12848             clearInterval(id);
12849             id = null;
12850             fn.apply(scope, args || []);
12851         }
12852     };
12853     /**
12854      * Cancels any pending timeout and queues a new one
12855      * @param {Number} delay The milliseconds to delay
12856      * @param {Function} newFn (optional) Overrides function passed to constructor
12857      * @param {Object} newScope (optional) Overrides scope passed to constructor
12858      * @param {Array} newArgs (optional) Overrides args passed to constructor
12859      */
12860     this.delay = function(delay, newFn, newScope, newArgs){
12861         if(id && delay != d){
12862             this.cancel();
12863         }
12864         d = delay;
12865         t = new Date().getTime();
12866         fn = newFn || fn;
12867         scope = newScope || scope;
12868         args = newArgs || args;
12869         if(!id){
12870             id = setInterval(call, d);
12871         }
12872     };
12873
12874     /**
12875      * Cancel the last queued timeout
12876      */
12877     this.cancel = function(){
12878         if(id){
12879             clearInterval(id);
12880             id = null;
12881         }
12882     };
12883 };/*
12884  * Based on:
12885  * Ext JS Library 1.1.1
12886  * Copyright(c) 2006-2007, Ext JS, LLC.
12887  *
12888  * Originally Released Under LGPL - original licence link has changed is not relivant.
12889  *
12890  * Fork - LGPL
12891  * <script type="text/javascript">
12892  */
12893  
12894  
12895 Roo.util.TaskRunner = function(interval){
12896     interval = interval || 10;
12897     var tasks = [], removeQueue = [];
12898     var id = 0;
12899     var running = false;
12900
12901     var stopThread = function(){
12902         running = false;
12903         clearInterval(id);
12904         id = 0;
12905     };
12906
12907     var startThread = function(){
12908         if(!running){
12909             running = true;
12910             id = setInterval(runTasks, interval);
12911         }
12912     };
12913
12914     var removeTask = function(task){
12915         removeQueue.push(task);
12916         if(task.onStop){
12917             task.onStop();
12918         }
12919     };
12920
12921     var runTasks = function(){
12922         if(removeQueue.length > 0){
12923             for(var i = 0, len = removeQueue.length; i < len; i++){
12924                 tasks.remove(removeQueue[i]);
12925             }
12926             removeQueue = [];
12927             if(tasks.length < 1){
12928                 stopThread();
12929                 return;
12930             }
12931         }
12932         var now = new Date().getTime();
12933         for(var i = 0, len = tasks.length; i < len; ++i){
12934             var t = tasks[i];
12935             var itime = now - t.taskRunTime;
12936             if(t.interval <= itime){
12937                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12938                 t.taskRunTime = now;
12939                 if(rt === false || t.taskRunCount === t.repeat){
12940                     removeTask(t);
12941                     return;
12942                 }
12943             }
12944             if(t.duration && t.duration <= (now - t.taskStartTime)){
12945                 removeTask(t);
12946             }
12947         }
12948     };
12949
12950     /**
12951      * Queues a new task.
12952      * @param {Object} task
12953      */
12954     this.start = function(task){
12955         tasks.push(task);
12956         task.taskStartTime = new Date().getTime();
12957         task.taskRunTime = 0;
12958         task.taskRunCount = 0;
12959         startThread();
12960         return task;
12961     };
12962
12963     this.stop = function(task){
12964         removeTask(task);
12965         return task;
12966     };
12967
12968     this.stopAll = function(){
12969         stopThread();
12970         for(var i = 0, len = tasks.length; i < len; i++){
12971             if(tasks[i].onStop){
12972                 tasks[i].onStop();
12973             }
12974         }
12975         tasks = [];
12976         removeQueue = [];
12977     };
12978 };
12979
12980 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12981  * Based on:
12982  * Ext JS Library 1.1.1
12983  * Copyright(c) 2006-2007, Ext JS, LLC.
12984  *
12985  * Originally Released Under LGPL - original licence link has changed is not relivant.
12986  *
12987  * Fork - LGPL
12988  * <script type="text/javascript">
12989  */
12990
12991  
12992 /**
12993  * @class Roo.util.MixedCollection
12994  * @extends Roo.util.Observable
12995  * A Collection class that maintains both numeric indexes and keys and exposes events.
12996  * @constructor
12997  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12998  * collection (defaults to false)
12999  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
13000  * and return the key value for that item.  This is used when available to look up the key on items that
13001  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
13002  * equivalent to providing an implementation for the {@link #getKey} method.
13003  */
13004 Roo.util.MixedCollection = function(allowFunctions, keyFn){
13005     this.items = [];
13006     this.map = {};
13007     this.keys = [];
13008     this.length = 0;
13009     this.addEvents({
13010         /**
13011          * @event clear
13012          * Fires when the collection is cleared.
13013          */
13014         "clear" : true,
13015         /**
13016          * @event add
13017          * Fires when an item is added to the collection.
13018          * @param {Number} index The index at which the item was added.
13019          * @param {Object} o The item added.
13020          * @param {String} key The key associated with the added item.
13021          */
13022         "add" : true,
13023         /**
13024          * @event replace
13025          * Fires when an item is replaced in the collection.
13026          * @param {String} key he key associated with the new added.
13027          * @param {Object} old The item being replaced.
13028          * @param {Object} new The new item.
13029          */
13030         "replace" : true,
13031         /**
13032          * @event remove
13033          * Fires when an item is removed from the collection.
13034          * @param {Object} o The item being removed.
13035          * @param {String} key (optional) The key associated with the removed item.
13036          */
13037         "remove" : true,
13038         "sort" : true
13039     });
13040     this.allowFunctions = allowFunctions === true;
13041     if(keyFn){
13042         this.getKey = keyFn;
13043     }
13044     Roo.util.MixedCollection.superclass.constructor.call(this);
13045 };
13046
13047 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
13048     allowFunctions : false,
13049     
13050 /**
13051  * Adds an item to the collection.
13052  * @param {String} key The key to associate with the item
13053  * @param {Object} o The item to add.
13054  * @return {Object} The item added.
13055  */
13056     add : function(key, o){
13057         if(arguments.length == 1){
13058             o = arguments[0];
13059             key = this.getKey(o);
13060         }
13061         if(typeof key == "undefined" || key === null){
13062             this.length++;
13063             this.items.push(o);
13064             this.keys.push(null);
13065         }else{
13066             var old = this.map[key];
13067             if(old){
13068                 return this.replace(key, o);
13069             }
13070             this.length++;
13071             this.items.push(o);
13072             this.map[key] = o;
13073             this.keys.push(key);
13074         }
13075         this.fireEvent("add", this.length-1, o, key);
13076         return o;
13077     },
13078        
13079 /**
13080   * MixedCollection has a generic way to fetch keys if you implement getKey.
13081 <pre><code>
13082 // normal way
13083 var mc = new Roo.util.MixedCollection();
13084 mc.add(someEl.dom.id, someEl);
13085 mc.add(otherEl.dom.id, otherEl);
13086 //and so on
13087
13088 // using getKey
13089 var mc = new Roo.util.MixedCollection();
13090 mc.getKey = function(el){
13091    return el.dom.id;
13092 };
13093 mc.add(someEl);
13094 mc.add(otherEl);
13095
13096 // or via the constructor
13097 var mc = new Roo.util.MixedCollection(false, function(el){
13098    return el.dom.id;
13099 });
13100 mc.add(someEl);
13101 mc.add(otherEl);
13102 </code></pre>
13103  * @param o {Object} The item for which to find the key.
13104  * @return {Object} The key for the passed item.
13105  */
13106     getKey : function(o){
13107          return o.id; 
13108     },
13109    
13110 /**
13111  * Replaces an item in the collection.
13112  * @param {String} key The key associated with the item to replace, or the item to replace.
13113  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
13114  * @return {Object}  The new item.
13115  */
13116     replace : function(key, o){
13117         if(arguments.length == 1){
13118             o = arguments[0];
13119             key = this.getKey(o);
13120         }
13121         var old = this.item(key);
13122         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
13123              return this.add(key, o);
13124         }
13125         var index = this.indexOfKey(key);
13126         this.items[index] = o;
13127         this.map[key] = o;
13128         this.fireEvent("replace", key, old, o);
13129         return o;
13130     },
13131    
13132 /**
13133  * Adds all elements of an Array or an Object to the collection.
13134  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
13135  * an Array of values, each of which are added to the collection.
13136  */
13137     addAll : function(objs){
13138         if(arguments.length > 1 || objs instanceof Array){
13139             var args = arguments.length > 1 ? arguments : objs;
13140             for(var i = 0, len = args.length; i < len; i++){
13141                 this.add(args[i]);
13142             }
13143         }else{
13144             for(var key in objs){
13145                 if(this.allowFunctions || typeof objs[key] != "function"){
13146                     this.add(key, objs[key]);
13147                 }
13148             }
13149         }
13150     },
13151    
13152 /**
13153  * Executes the specified function once for every item in the collection, passing each
13154  * item as the first and only parameter. returning false from the function will stop the iteration.
13155  * @param {Function} fn The function to execute for each item.
13156  * @param {Object} scope (optional) The scope in which to execute the function.
13157  */
13158     each : function(fn, scope){
13159         var items = [].concat(this.items); // each safe for removal
13160         for(var i = 0, len = items.length; i < len; i++){
13161             if(fn.call(scope || items[i], items[i], i, len) === false){
13162                 break;
13163             }
13164         }
13165     },
13166    
13167 /**
13168  * Executes the specified function once for every key in the collection, passing each
13169  * key, and its associated item as the first two parameters.
13170  * @param {Function} fn The function to execute for each item.
13171  * @param {Object} scope (optional) The scope in which to execute the function.
13172  */
13173     eachKey : function(fn, scope){
13174         for(var i = 0, len = this.keys.length; i < len; i++){
13175             fn.call(scope || window, this.keys[i], this.items[i], i, len);
13176         }
13177     },
13178    
13179 /**
13180  * Returns the first item in the collection which elicits a true return value from the
13181  * passed selection function.
13182  * @param {Function} fn The selection function to execute for each item.
13183  * @param {Object} scope (optional) The scope in which to execute the function.
13184  * @return {Object} The first item in the collection which returned true from the selection function.
13185  */
13186     find : function(fn, scope){
13187         for(var i = 0, len = this.items.length; i < len; i++){
13188             if(fn.call(scope || window, this.items[i], this.keys[i])){
13189                 return this.items[i];
13190             }
13191         }
13192         return null;
13193     },
13194    
13195 /**
13196  * Inserts an item at the specified index in the collection.
13197  * @param {Number} index The index to insert the item at.
13198  * @param {String} key The key to associate with the new item, or the item itself.
13199  * @param {Object} o  (optional) If the second parameter was a key, the new item.
13200  * @return {Object} The item inserted.
13201  */
13202     insert : function(index, key, o){
13203         if(arguments.length == 2){
13204             o = arguments[1];
13205             key = this.getKey(o);
13206         }
13207         if(index >= this.length){
13208             return this.add(key, o);
13209         }
13210         this.length++;
13211         this.items.splice(index, 0, o);
13212         if(typeof key != "undefined" && key != null){
13213             this.map[key] = o;
13214         }
13215         this.keys.splice(index, 0, key);
13216         this.fireEvent("add", index, o, key);
13217         return o;
13218     },
13219    
13220 /**
13221  * Removed an item from the collection.
13222  * @param {Object} o The item to remove.
13223  * @return {Object} The item removed.
13224  */
13225     remove : function(o){
13226         return this.removeAt(this.indexOf(o));
13227     },
13228    
13229 /**
13230  * Remove an item from a specified index in the collection.
13231  * @param {Number} index The index within the collection of the item to remove.
13232  */
13233     removeAt : function(index){
13234         if(index < this.length && index >= 0){
13235             this.length--;
13236             var o = this.items[index];
13237             this.items.splice(index, 1);
13238             var key = this.keys[index];
13239             if(typeof key != "undefined"){
13240                 delete this.map[key];
13241             }
13242             this.keys.splice(index, 1);
13243             this.fireEvent("remove", o, key);
13244         }
13245     },
13246    
13247 /**
13248  * Removed an item associated with the passed key fom the collection.
13249  * @param {String} key The key of the item to remove.
13250  */
13251     removeKey : function(key){
13252         return this.removeAt(this.indexOfKey(key));
13253     },
13254    
13255 /**
13256  * Returns the number of items in the collection.
13257  * @return {Number} the number of items in the collection.
13258  */
13259     getCount : function(){
13260         return this.length; 
13261     },
13262    
13263 /**
13264  * Returns index within the collection of the passed Object.
13265  * @param {Object} o The item to find the index of.
13266  * @return {Number} index of the item.
13267  */
13268     indexOf : function(o){
13269         if(!this.items.indexOf){
13270             for(var i = 0, len = this.items.length; i < len; i++){
13271                 if(this.items[i] == o) {
13272                     return i;
13273                 }
13274             }
13275             return -1;
13276         }else{
13277             return this.items.indexOf(o);
13278         }
13279     },
13280    
13281 /**
13282  * Returns index within the collection of the passed key.
13283  * @param {String} key The key to find the index of.
13284  * @return {Number} index of the key.
13285  */
13286     indexOfKey : function(key){
13287         if(!this.keys.indexOf){
13288             for(var i = 0, len = this.keys.length; i < len; i++){
13289                 if(this.keys[i] == key) {
13290                     return i;
13291                 }
13292             }
13293             return -1;
13294         }else{
13295             return this.keys.indexOf(key);
13296         }
13297     },
13298    
13299 /**
13300  * Returns the item associated with the passed key OR index. Key has priority over index.
13301  * @param {String/Number} key The key or index of the item.
13302  * @return {Object} The item associated with the passed key.
13303  */
13304     item : function(key){
13305         if (key === 'length') {
13306             return null;
13307         }
13308         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13309         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13310     },
13311     
13312 /**
13313  * Returns the item at the specified index.
13314  * @param {Number} index The index of the item.
13315  * @return {Object}
13316  */
13317     itemAt : function(index){
13318         return this.items[index];
13319     },
13320     
13321 /**
13322  * Returns the item associated with the passed key.
13323  * @param {String/Number} key The key of the item.
13324  * @return {Object} The item associated with the passed key.
13325  */
13326     key : function(key){
13327         return this.map[key];
13328     },
13329    
13330 /**
13331  * Returns true if the collection contains the passed Object as an item.
13332  * @param {Object} o  The Object to look for in the collection.
13333  * @return {Boolean} True if the collection contains the Object as an item.
13334  */
13335     contains : function(o){
13336         return this.indexOf(o) != -1;
13337     },
13338    
13339 /**
13340  * Returns true if the collection contains the passed Object as a key.
13341  * @param {String} key The key to look for in the collection.
13342  * @return {Boolean} True if the collection contains the Object as a key.
13343  */
13344     containsKey : function(key){
13345         return typeof this.map[key] != "undefined";
13346     },
13347    
13348 /**
13349  * Removes all items from the collection.
13350  */
13351     clear : function(){
13352         this.length = 0;
13353         this.items = [];
13354         this.keys = [];
13355         this.map = {};
13356         this.fireEvent("clear");
13357     },
13358    
13359 /**
13360  * Returns the first item in the collection.
13361  * @return {Object} the first item in the collection..
13362  */
13363     first : function(){
13364         return this.items[0]; 
13365     },
13366    
13367 /**
13368  * Returns the last item in the collection.
13369  * @return {Object} the last item in the collection..
13370  */
13371     last : function(){
13372         return this.items[this.length-1];   
13373     },
13374     
13375     _sort : function(property, dir, fn){
13376         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13377         fn = fn || function(a, b){
13378             return a-b;
13379         };
13380         var c = [], k = this.keys, items = this.items;
13381         for(var i = 0, len = items.length; i < len; i++){
13382             c[c.length] = {key: k[i], value: items[i], index: i};
13383         }
13384         c.sort(function(a, b){
13385             var v = fn(a[property], b[property]) * dsc;
13386             if(v == 0){
13387                 v = (a.index < b.index ? -1 : 1);
13388             }
13389             return v;
13390         });
13391         for(var i = 0, len = c.length; i < len; i++){
13392             items[i] = c[i].value;
13393             k[i] = c[i].key;
13394         }
13395         this.fireEvent("sort", this);
13396     },
13397     
13398     /**
13399      * Sorts this collection with the passed comparison function
13400      * @param {String} direction (optional) "ASC" or "DESC"
13401      * @param {Function} fn (optional) comparison function
13402      */
13403     sort : function(dir, fn){
13404         this._sort("value", dir, fn);
13405     },
13406     
13407     /**
13408      * Sorts this collection by keys
13409      * @param {String} direction (optional) "ASC" or "DESC"
13410      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13411      */
13412     keySort : function(dir, fn){
13413         this._sort("key", dir, fn || function(a, b){
13414             return String(a).toUpperCase()-String(b).toUpperCase();
13415         });
13416     },
13417     
13418     /**
13419      * Returns a range of items in this collection
13420      * @param {Number} startIndex (optional) defaults to 0
13421      * @param {Number} endIndex (optional) default to the last item
13422      * @return {Array} An array of items
13423      */
13424     getRange : function(start, end){
13425         var items = this.items;
13426         if(items.length < 1){
13427             return [];
13428         }
13429         start = start || 0;
13430         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13431         var r = [];
13432         if(start <= end){
13433             for(var i = start; i <= end; i++) {
13434                     r[r.length] = items[i];
13435             }
13436         }else{
13437             for(var i = start; i >= end; i--) {
13438                     r[r.length] = items[i];
13439             }
13440         }
13441         return r;
13442     },
13443         
13444     /**
13445      * Filter the <i>objects</i> in this collection by a specific property. 
13446      * Returns a new collection that has been filtered.
13447      * @param {String} property A property on your objects
13448      * @param {String/RegExp} value Either string that the property values 
13449      * should start with or a RegExp to test against the property
13450      * @return {MixedCollection} The new filtered collection
13451      */
13452     filter : function(property, value){
13453         if(!value.exec){ // not a regex
13454             value = String(value);
13455             if(value.length == 0){
13456                 return this.clone();
13457             }
13458             value = new RegExp("^" + Roo.escapeRe(value), "i");
13459         }
13460         return this.filterBy(function(o){
13461             return o && value.test(o[property]);
13462         });
13463         },
13464     
13465     /**
13466      * Filter by a function. * Returns a new collection that has been filtered.
13467      * The passed function will be called with each 
13468      * object in the collection. If the function returns true, the value is included 
13469      * otherwise it is filtered.
13470      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13471      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13472      * @return {MixedCollection} The new filtered collection
13473      */
13474     filterBy : function(fn, scope){
13475         var r = new Roo.util.MixedCollection();
13476         r.getKey = this.getKey;
13477         var k = this.keys, it = this.items;
13478         for(var i = 0, len = it.length; i < len; i++){
13479             if(fn.call(scope||this, it[i], k[i])){
13480                                 r.add(k[i], it[i]);
13481                         }
13482         }
13483         return r;
13484     },
13485     
13486     /**
13487      * Creates a duplicate of this collection
13488      * @return {MixedCollection}
13489      */
13490     clone : function(){
13491         var r = new Roo.util.MixedCollection();
13492         var k = this.keys, it = this.items;
13493         for(var i = 0, len = it.length; i < len; i++){
13494             r.add(k[i], it[i]);
13495         }
13496         r.getKey = this.getKey;
13497         return r;
13498     }
13499 });
13500 /**
13501  * Returns the item associated with the passed key or index.
13502  * @method
13503  * @param {String/Number} key The key or index of the item.
13504  * @return {Object} The item associated with the passed key.
13505  */
13506 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13507  * Based on:
13508  * Ext JS Library 1.1.1
13509  * Copyright(c) 2006-2007, Ext JS, LLC.
13510  *
13511  * Originally Released Under LGPL - original licence link has changed is not relivant.
13512  *
13513  * Fork - LGPL
13514  * <script type="text/javascript">
13515  */
13516 /**
13517  * @class Roo.util.JSON
13518  * Modified version of Douglas Crockford"s json.js that doesn"t
13519  * mess with the Object prototype 
13520  * http://www.json.org/js.html
13521  * @singleton
13522  */
13523 Roo.util.JSON = new (function(){
13524     var useHasOwn = {}.hasOwnProperty ? true : false;
13525     
13526     // crashes Safari in some instances
13527     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13528     
13529     var pad = function(n) {
13530         return n < 10 ? "0" + n : n;
13531     };
13532     
13533     var m = {
13534         "\b": '\\b',
13535         "\t": '\\t',
13536         "\n": '\\n',
13537         "\f": '\\f',
13538         "\r": '\\r',
13539         '"' : '\\"',
13540         "\\": '\\\\'
13541     };
13542
13543     var encodeString = function(s){
13544         if (/["\\\x00-\x1f]/.test(s)) {
13545             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13546                 var c = m[b];
13547                 if(c){
13548                     return c;
13549                 }
13550                 c = b.charCodeAt();
13551                 return "\\u00" +
13552                     Math.floor(c / 16).toString(16) +
13553                     (c % 16).toString(16);
13554             }) + '"';
13555         }
13556         return '"' + s + '"';
13557     };
13558     
13559     var encodeArray = function(o){
13560         var a = ["["], b, i, l = o.length, v;
13561             for (i = 0; i < l; i += 1) {
13562                 v = o[i];
13563                 switch (typeof v) {
13564                     case "undefined":
13565                     case "function":
13566                     case "unknown":
13567                         break;
13568                     default:
13569                         if (b) {
13570                             a.push(',');
13571                         }
13572                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13573                         b = true;
13574                 }
13575             }
13576             a.push("]");
13577             return a.join("");
13578     };
13579     
13580     var encodeDate = function(o){
13581         return '"' + o.getFullYear() + "-" +
13582                 pad(o.getMonth() + 1) + "-" +
13583                 pad(o.getDate()) + "T" +
13584                 pad(o.getHours()) + ":" +
13585                 pad(o.getMinutes()) + ":" +
13586                 pad(o.getSeconds()) + '"';
13587     };
13588     
13589     /**
13590      * Encodes an Object, Array or other value
13591      * @param {Mixed} o The variable to encode
13592      * @return {String} The JSON string
13593      */
13594     this.encode = function(o)
13595     {
13596         // should this be extended to fully wrap stringify..
13597         
13598         if(typeof o == "undefined" || o === null){
13599             return "null";
13600         }else if(o instanceof Array){
13601             return encodeArray(o);
13602         }else if(o instanceof Date){
13603             return encodeDate(o);
13604         }else if(typeof o == "string"){
13605             return encodeString(o);
13606         }else if(typeof o == "number"){
13607             return isFinite(o) ? String(o) : "null";
13608         }else if(typeof o == "boolean"){
13609             return String(o);
13610         }else {
13611             var a = ["{"], b, i, v;
13612             for (i in o) {
13613                 if(!useHasOwn || o.hasOwnProperty(i)) {
13614                     v = o[i];
13615                     switch (typeof v) {
13616                     case "undefined":
13617                     case "function":
13618                     case "unknown":
13619                         break;
13620                     default:
13621                         if(b){
13622                             a.push(',');
13623                         }
13624                         a.push(this.encode(i), ":",
13625                                 v === null ? "null" : this.encode(v));
13626                         b = true;
13627                     }
13628                 }
13629             }
13630             a.push("}");
13631             return a.join("");
13632         }
13633     };
13634     
13635     /**
13636      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13637      * @param {String} json The JSON string
13638      * @return {Object} The resulting object
13639      */
13640     this.decode = function(json){
13641         
13642         return  /** eval:var:json */ eval("(" + json + ')');
13643     };
13644 })();
13645 /** 
13646  * Shorthand for {@link Roo.util.JSON#encode}
13647  * @member Roo encode 
13648  * @method */
13649 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13650 /** 
13651  * Shorthand for {@link Roo.util.JSON#decode}
13652  * @member Roo decode 
13653  * @method */
13654 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13655 /*
13656  * Based on:
13657  * Ext JS Library 1.1.1
13658  * Copyright(c) 2006-2007, Ext JS, LLC.
13659  *
13660  * Originally Released Under LGPL - original licence link has changed is not relivant.
13661  *
13662  * Fork - LGPL
13663  * <script type="text/javascript">
13664  */
13665  
13666 /**
13667  * @class Roo.util.Format
13668  * Reusable data formatting functions
13669  * @singleton
13670  */
13671 Roo.util.Format = function(){
13672     var trimRe = /^\s+|\s+$/g;
13673     return {
13674         /**
13675          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13676          * @param {String} value The string to truncate
13677          * @param {Number} length The maximum length to allow before truncating
13678          * @return {String} The converted text
13679          */
13680         ellipsis : function(value, len){
13681             if(value && value.length > len){
13682                 return value.substr(0, len-3)+"...";
13683             }
13684             return value;
13685         },
13686
13687         /**
13688          * Checks a reference and converts it to empty string if it is undefined
13689          * @param {Mixed} value Reference to check
13690          * @return {Mixed} Empty string if converted, otherwise the original value
13691          */
13692         undef : function(value){
13693             return typeof value != "undefined" ? value : "";
13694         },
13695
13696         /**
13697          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13698          * @param {String} value The string to encode
13699          * @return {String} The encoded text
13700          */
13701         htmlEncode : function(value){
13702             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13703         },
13704
13705         /**
13706          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13707          * @param {String} value The string to decode
13708          * @return {String} The decoded text
13709          */
13710         htmlDecode : function(value){
13711             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13712         },
13713
13714         /**
13715          * Trims any whitespace from either side of a string
13716          * @param {String} value The text to trim
13717          * @return {String} The trimmed text
13718          */
13719         trim : function(value){
13720             return String(value).replace(trimRe, "");
13721         },
13722
13723         /**
13724          * Returns a substring from within an original string
13725          * @param {String} value The original text
13726          * @param {Number} start The start index of the substring
13727          * @param {Number} length The length of the substring
13728          * @return {String} The substring
13729          */
13730         substr : function(value, start, length){
13731             return String(value).substr(start, length);
13732         },
13733
13734         /**
13735          * Converts a string to all lower case letters
13736          * @param {String} value The text to convert
13737          * @return {String} The converted text
13738          */
13739         lowercase : function(value){
13740             return String(value).toLowerCase();
13741         },
13742
13743         /**
13744          * Converts a string to all upper case letters
13745          * @param {String} value The text to convert
13746          * @return {String} The converted text
13747          */
13748         uppercase : function(value){
13749             return String(value).toUpperCase();
13750         },
13751
13752         /**
13753          * Converts the first character only of a string to upper case
13754          * @param {String} value The text to convert
13755          * @return {String} The converted text
13756          */
13757         capitalize : function(value){
13758             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13759         },
13760
13761         // private
13762         call : function(value, fn){
13763             if(arguments.length > 2){
13764                 var args = Array.prototype.slice.call(arguments, 2);
13765                 args.unshift(value);
13766                  
13767                 return /** eval:var:value */  eval(fn).apply(window, args);
13768             }else{
13769                 /** eval:var:value */
13770                 return /** eval:var:value */ eval(fn).call(window, value);
13771             }
13772         },
13773
13774        
13775         /**
13776          * safer version of Math.toFixed..??/
13777          * @param {Number/String} value The numeric value to format
13778          * @param {Number/String} value Decimal places 
13779          * @return {String} The formatted currency string
13780          */
13781         toFixed : function(v, n)
13782         {
13783             // why not use to fixed - precision is buggered???
13784             if (!n) {
13785                 return Math.round(v-0);
13786             }
13787             var fact = Math.pow(10,n+1);
13788             v = (Math.round((v-0)*fact))/fact;
13789             var z = (''+fact).substring(2);
13790             if (v == Math.floor(v)) {
13791                 return Math.floor(v) + '.' + z;
13792             }
13793             
13794             // now just padd decimals..
13795             var ps = String(v).split('.');
13796             var fd = (ps[1] + z);
13797             var r = fd.substring(0,n); 
13798             var rm = fd.substring(n); 
13799             if (rm < 5) {
13800                 return ps[0] + '.' + r;
13801             }
13802             r*=1; // turn it into a number;
13803             r++;
13804             if (String(r).length != n) {
13805                 ps[0]*=1;
13806                 ps[0]++;
13807                 r = String(r).substring(1); // chop the end off.
13808             }
13809             
13810             return ps[0] + '.' + r;
13811              
13812         },
13813         
13814         /**
13815          * Format a number as US currency
13816          * @param {Number/String} value The numeric value to format
13817          * @return {String} The formatted currency string
13818          */
13819         usMoney : function(v){
13820             return '$' + Roo.util.Format.number(v);
13821         },
13822         
13823         /**
13824          * Format a number
13825          * eventually this should probably emulate php's number_format
13826          * @param {Number/String} value The numeric value to format
13827          * @param {Number} decimals number of decimal places
13828          * @param {String} delimiter for thousands (default comma)
13829          * @return {String} The formatted currency string
13830          */
13831         number : function(v, decimals, thousandsDelimiter)
13832         {
13833             // multiply and round.
13834             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13835             thousandsDelimiter = typeof(thousandsDelimiter) == 'undefined' ? ',' : thousandsDelimiter;
13836             
13837             var mul = Math.pow(10, decimals);
13838             var zero = String(mul).substring(1);
13839             v = (Math.round((v-0)*mul))/mul;
13840             
13841             // if it's '0' number.. then
13842             
13843             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13844             v = String(v);
13845             var ps = v.split('.');
13846             var whole = ps[0];
13847             
13848             var r = /(\d+)(\d{3})/;
13849             // add comma's
13850             
13851             if(thousandsDelimiter.length != 0) {
13852                 whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsDelimiter );
13853             } 
13854             
13855             var sub = ps[1] ?
13856                     // has decimals..
13857                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13858                     // does not have decimals
13859                     (decimals ? ('.' + zero) : '');
13860             
13861             
13862             return whole + sub ;
13863         },
13864         
13865         /**
13866          * Parse a value into a formatted date using the specified format pattern.
13867          * @param {Mixed} value The value to format
13868          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13869          * @return {String} The formatted date string
13870          */
13871         date : function(v, format){
13872             if(!v){
13873                 return "";
13874             }
13875             if(!(v instanceof Date)){
13876                 v = new Date(Date.parse(v));
13877             }
13878             return v.dateFormat(format || Roo.util.Format.defaults.date);
13879         },
13880
13881         /**
13882          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13883          * @param {String} format Any valid date format string
13884          * @return {Function} The date formatting function
13885          */
13886         dateRenderer : function(format){
13887             return function(v){
13888                 return Roo.util.Format.date(v, format);  
13889             };
13890         },
13891
13892         // private
13893         stripTagsRE : /<\/?[^>]+>/gi,
13894         
13895         /**
13896          * Strips all HTML tags
13897          * @param {Mixed} value The text from which to strip tags
13898          * @return {String} The stripped text
13899          */
13900         stripTags : function(v){
13901             return !v ? v : String(v).replace(this.stripTagsRE, "");
13902         }
13903     };
13904 }();
13905 Roo.util.Format.defaults = {
13906     date : 'd/M/Y'
13907 };/*
13908  * Based on:
13909  * Ext JS Library 1.1.1
13910  * Copyright(c) 2006-2007, Ext JS, LLC.
13911  *
13912  * Originally Released Under LGPL - original licence link has changed is not relivant.
13913  *
13914  * Fork - LGPL
13915  * <script type="text/javascript">
13916  */
13917
13918
13919  
13920
13921 /**
13922  * @class Roo.MasterTemplate
13923  * @extends Roo.Template
13924  * Provides a template that can have child templates. The syntax is:
13925 <pre><code>
13926 var t = new Roo.MasterTemplate(
13927         '&lt;select name="{name}"&gt;',
13928                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13929         '&lt;/select&gt;'
13930 );
13931 t.add('options', {value: 'foo', text: 'bar'});
13932 // or you can add multiple child elements in one shot
13933 t.addAll('options', [
13934     {value: 'foo', text: 'bar'},
13935     {value: 'foo2', text: 'bar2'},
13936     {value: 'foo3', text: 'bar3'}
13937 ]);
13938 // then append, applying the master template values
13939 t.append('my-form', {name: 'my-select'});
13940 </code></pre>
13941 * A name attribute for the child template is not required if you have only one child
13942 * template or you want to refer to them by index.
13943  */
13944 Roo.MasterTemplate = function(){
13945     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13946     this.originalHtml = this.html;
13947     var st = {};
13948     var m, re = this.subTemplateRe;
13949     re.lastIndex = 0;
13950     var subIndex = 0;
13951     while(m = re.exec(this.html)){
13952         var name = m[1], content = m[2];
13953         st[subIndex] = {
13954             name: name,
13955             index: subIndex,
13956             buffer: [],
13957             tpl : new Roo.Template(content)
13958         };
13959         if(name){
13960             st[name] = st[subIndex];
13961         }
13962         st[subIndex].tpl.compile();
13963         st[subIndex].tpl.call = this.call.createDelegate(this);
13964         subIndex++;
13965     }
13966     this.subCount = subIndex;
13967     this.subs = st;
13968 };
13969 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13970     /**
13971     * The regular expression used to match sub templates
13972     * @type RegExp
13973     * @property
13974     */
13975     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13976
13977     /**
13978      * Applies the passed values to a child template.
13979      * @param {String/Number} name (optional) The name or index of the child template
13980      * @param {Array/Object} values The values to be applied to the template
13981      * @return {MasterTemplate} this
13982      */
13983      add : function(name, values){
13984         if(arguments.length == 1){
13985             values = arguments[0];
13986             name = 0;
13987         }
13988         var s = this.subs[name];
13989         s.buffer[s.buffer.length] = s.tpl.apply(values);
13990         return this;
13991     },
13992
13993     /**
13994      * Applies all the passed values to a child template.
13995      * @param {String/Number} name (optional) The name or index of the child template
13996      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13997      * @param {Boolean} reset (optional) True to reset the template first
13998      * @return {MasterTemplate} this
13999      */
14000     fill : function(name, values, reset){
14001         var a = arguments;
14002         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
14003             values = a[0];
14004             name = 0;
14005             reset = a[1];
14006         }
14007         if(reset){
14008             this.reset();
14009         }
14010         for(var i = 0, len = values.length; i < len; i++){
14011             this.add(name, values[i]);
14012         }
14013         return this;
14014     },
14015
14016     /**
14017      * Resets the template for reuse
14018      * @return {MasterTemplate} this
14019      */
14020      reset : function(){
14021         var s = this.subs;
14022         for(var i = 0; i < this.subCount; i++){
14023             s[i].buffer = [];
14024         }
14025         return this;
14026     },
14027
14028     applyTemplate : function(values){
14029         var s = this.subs;
14030         var replaceIndex = -1;
14031         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
14032             return s[++replaceIndex].buffer.join("");
14033         });
14034         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
14035     },
14036
14037     apply : function(){
14038         return this.applyTemplate.apply(this, arguments);
14039     },
14040
14041     compile : function(){return this;}
14042 });
14043
14044 /**
14045  * Alias for fill().
14046  * @method
14047  */
14048 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
14049  /**
14050  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
14051  * var tpl = Roo.MasterTemplate.from('element-id');
14052  * @param {String/HTMLElement} el
14053  * @param {Object} config
14054  * @static
14055  */
14056 Roo.MasterTemplate.from = function(el, config){
14057     el = Roo.getDom(el);
14058     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
14059 };/*
14060  * Based on:
14061  * Ext JS Library 1.1.1
14062  * Copyright(c) 2006-2007, Ext JS, LLC.
14063  *
14064  * Originally Released Under LGPL - original licence link has changed is not relivant.
14065  *
14066  * Fork - LGPL
14067  * <script type="text/javascript">
14068  */
14069
14070  
14071 /**
14072  * @class Roo.util.CSS
14073  * Utility class for manipulating CSS rules
14074  * @singleton
14075  */
14076 Roo.util.CSS = function(){
14077         var rules = null;
14078         var doc = document;
14079
14080     var camelRe = /(-[a-z])/gi;
14081     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
14082
14083    return {
14084    /**
14085     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
14086     * tag and appended to the HEAD of the document.
14087     * @param {String|Object} cssText The text containing the css rules
14088     * @param {String} id An id to add to the stylesheet for later removal
14089     * @return {StyleSheet}
14090     */
14091     createStyleSheet : function(cssText, id){
14092         var ss;
14093         var head = doc.getElementsByTagName("head")[0];
14094         var nrules = doc.createElement("style");
14095         nrules.setAttribute("type", "text/css");
14096         if(id){
14097             nrules.setAttribute("id", id);
14098         }
14099         if (typeof(cssText) != 'string') {
14100             // support object maps..
14101             // not sure if this a good idea.. 
14102             // perhaps it should be merged with the general css handling
14103             // and handle js style props.
14104             var cssTextNew = [];
14105             for(var n in cssText) {
14106                 var citems = [];
14107                 for(var k in cssText[n]) {
14108                     citems.push( k + ' : ' +cssText[n][k] + ';' );
14109                 }
14110                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
14111                 
14112             }
14113             cssText = cssTextNew.join("\n");
14114             
14115         }
14116        
14117        
14118        if(Roo.isIE){
14119            head.appendChild(nrules);
14120            ss = nrules.styleSheet;
14121            ss.cssText = cssText;
14122        }else{
14123            try{
14124                 nrules.appendChild(doc.createTextNode(cssText));
14125            }catch(e){
14126                nrules.cssText = cssText; 
14127            }
14128            head.appendChild(nrules);
14129            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
14130        }
14131        this.cacheStyleSheet(ss);
14132        return ss;
14133    },
14134
14135    /**
14136     * Removes a style or link tag by id
14137     * @param {String} id The id of the tag
14138     */
14139    removeStyleSheet : function(id){
14140        var existing = doc.getElementById(id);
14141        if(existing){
14142            existing.parentNode.removeChild(existing);
14143        }
14144    },
14145
14146    /**
14147     * Dynamically swaps an existing stylesheet reference for a new one
14148     * @param {String} id The id of an existing link tag to remove
14149     * @param {String} url The href of the new stylesheet to include
14150     */
14151    swapStyleSheet : function(id, url){
14152        this.removeStyleSheet(id);
14153        var ss = doc.createElement("link");
14154        ss.setAttribute("rel", "stylesheet");
14155        ss.setAttribute("type", "text/css");
14156        ss.setAttribute("id", id);
14157        ss.setAttribute("href", url);
14158        doc.getElementsByTagName("head")[0].appendChild(ss);
14159    },
14160    
14161    /**
14162     * Refresh the rule cache if you have dynamically added stylesheets
14163     * @return {Object} An object (hash) of rules indexed by selector
14164     */
14165    refreshCache : function(){
14166        return this.getRules(true);
14167    },
14168
14169    // private
14170    cacheStyleSheet : function(stylesheet){
14171        if(!rules){
14172            rules = {};
14173        }
14174        try{// try catch for cross domain access issue
14175            var ssRules = stylesheet.cssRules || stylesheet.rules;
14176            for(var j = ssRules.length-1; j >= 0; --j){
14177                rules[ssRules[j].selectorText] = ssRules[j];
14178            }
14179        }catch(e){}
14180    },
14181    
14182    /**
14183     * Gets all css rules for the document
14184     * @param {Boolean} refreshCache true to refresh the internal cache
14185     * @return {Object} An object (hash) of rules indexed by selector
14186     */
14187    getRules : function(refreshCache){
14188                 if(rules == null || refreshCache){
14189                         rules = {};
14190                         var ds = doc.styleSheets;
14191                         for(var i =0, len = ds.length; i < len; i++){
14192                             try{
14193                         this.cacheStyleSheet(ds[i]);
14194                     }catch(e){} 
14195                 }
14196                 }
14197                 return rules;
14198         },
14199         
14200         /**
14201     * Gets an an individual CSS rule by selector(s)
14202     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
14203     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
14204     * @return {CSSRule} The CSS rule or null if one is not found
14205     */
14206    getRule : function(selector, refreshCache){
14207                 var rs = this.getRules(refreshCache);
14208                 if(!(selector instanceof Array)){
14209                     return rs[selector];
14210                 }
14211                 for(var i = 0; i < selector.length; i++){
14212                         if(rs[selector[i]]){
14213                                 return rs[selector[i]];
14214                         }
14215                 }
14216                 return null;
14217         },
14218         
14219         
14220         /**
14221     * Updates a rule property
14222     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14223     * @param {String} property The css property
14224     * @param {String} value The new value for the property
14225     * @return {Boolean} true If a rule was found and updated
14226     */
14227    updateRule : function(selector, property, value){
14228                 if(!(selector instanceof Array)){
14229                         var rule = this.getRule(selector);
14230                         if(rule){
14231                                 rule.style[property.replace(camelRe, camelFn)] = value;
14232                                 return true;
14233                         }
14234                 }else{
14235                         for(var i = 0; i < selector.length; i++){
14236                                 if(this.updateRule(selector[i], property, value)){
14237                                         return true;
14238                                 }
14239                         }
14240                 }
14241                 return false;
14242         }
14243    };   
14244 }();/*
14245  * Based on:
14246  * Ext JS Library 1.1.1
14247  * Copyright(c) 2006-2007, Ext JS, LLC.
14248  *
14249  * Originally Released Under LGPL - original licence link has changed is not relivant.
14250  *
14251  * Fork - LGPL
14252  * <script type="text/javascript">
14253  */
14254
14255  
14256
14257 /**
14258  * @class Roo.util.ClickRepeater
14259  * @extends Roo.util.Observable
14260  * 
14261  * A wrapper class which can be applied to any element. Fires a "click" event while the
14262  * mouse is pressed. The interval between firings may be specified in the config but
14263  * defaults to 10 milliseconds.
14264  * 
14265  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14266  * 
14267  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14268  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14269  * Similar to an autorepeat key delay.
14270  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14271  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14272  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14273  *           "interval" and "delay" are ignored. "immediate" is honored.
14274  * @cfg {Boolean} preventDefault True to prevent the default click event
14275  * @cfg {Boolean} stopDefault True to stop the default click event
14276  * 
14277  * @history
14278  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14279  *     2007-02-02 jvs Renamed to ClickRepeater
14280  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14281  *
14282  *  @constructor
14283  * @param {String/HTMLElement/Element} el The element to listen on
14284  * @param {Object} config
14285  **/
14286 Roo.util.ClickRepeater = function(el, config)
14287 {
14288     this.el = Roo.get(el);
14289     this.el.unselectable();
14290
14291     Roo.apply(this, config);
14292
14293     this.addEvents({
14294     /**
14295      * @event mousedown
14296      * Fires when the mouse button is depressed.
14297      * @param {Roo.util.ClickRepeater} this
14298      */
14299         "mousedown" : true,
14300     /**
14301      * @event click
14302      * Fires on a specified interval during the time the element is pressed.
14303      * @param {Roo.util.ClickRepeater} this
14304      */
14305         "click" : true,
14306     /**
14307      * @event mouseup
14308      * Fires when the mouse key is released.
14309      * @param {Roo.util.ClickRepeater} this
14310      */
14311         "mouseup" : true
14312     });
14313
14314     this.el.on("mousedown", this.handleMouseDown, this);
14315     if(this.preventDefault || this.stopDefault){
14316         this.el.on("click", function(e){
14317             if(this.preventDefault){
14318                 e.preventDefault();
14319             }
14320             if(this.stopDefault){
14321                 e.stopEvent();
14322             }
14323         }, this);
14324     }
14325
14326     // allow inline handler
14327     if(this.handler){
14328         this.on("click", this.handler,  this.scope || this);
14329     }
14330
14331     Roo.util.ClickRepeater.superclass.constructor.call(this);
14332 };
14333
14334 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14335     interval : 20,
14336     delay: 250,
14337     preventDefault : true,
14338     stopDefault : false,
14339     timer : 0,
14340
14341     // private
14342     handleMouseDown : function(){
14343         clearTimeout(this.timer);
14344         this.el.blur();
14345         if(this.pressClass){
14346             this.el.addClass(this.pressClass);
14347         }
14348         this.mousedownTime = new Date();
14349
14350         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14351         this.el.on("mouseout", this.handleMouseOut, this);
14352
14353         this.fireEvent("mousedown", this);
14354         this.fireEvent("click", this);
14355         
14356         this.timer = this.click.defer(this.delay || this.interval, this);
14357     },
14358
14359     // private
14360     click : function(){
14361         this.fireEvent("click", this);
14362         this.timer = this.click.defer(this.getInterval(), this);
14363     },
14364
14365     // private
14366     getInterval: function(){
14367         if(!this.accelerate){
14368             return this.interval;
14369         }
14370         var pressTime = this.mousedownTime.getElapsed();
14371         if(pressTime < 500){
14372             return 400;
14373         }else if(pressTime < 1700){
14374             return 320;
14375         }else if(pressTime < 2600){
14376             return 250;
14377         }else if(pressTime < 3500){
14378             return 180;
14379         }else if(pressTime < 4400){
14380             return 140;
14381         }else if(pressTime < 5300){
14382             return 80;
14383         }else if(pressTime < 6200){
14384             return 50;
14385         }else{
14386             return 10;
14387         }
14388     },
14389
14390     // private
14391     handleMouseOut : function(){
14392         clearTimeout(this.timer);
14393         if(this.pressClass){
14394             this.el.removeClass(this.pressClass);
14395         }
14396         this.el.on("mouseover", this.handleMouseReturn, this);
14397     },
14398
14399     // private
14400     handleMouseReturn : function(){
14401         this.el.un("mouseover", this.handleMouseReturn);
14402         if(this.pressClass){
14403             this.el.addClass(this.pressClass);
14404         }
14405         this.click();
14406     },
14407
14408     // private
14409     handleMouseUp : function(){
14410         clearTimeout(this.timer);
14411         this.el.un("mouseover", this.handleMouseReturn);
14412         this.el.un("mouseout", this.handleMouseOut);
14413         Roo.get(document).un("mouseup", this.handleMouseUp);
14414         this.el.removeClass(this.pressClass);
14415         this.fireEvent("mouseup", this);
14416     }
14417 });/*
14418  * Based on:
14419  * Ext JS Library 1.1.1
14420  * Copyright(c) 2006-2007, Ext JS, LLC.
14421  *
14422  * Originally Released Under LGPL - original licence link has changed is not relivant.
14423  *
14424  * Fork - LGPL
14425  * <script type="text/javascript">
14426  */
14427
14428  
14429 /**
14430  * @class Roo.KeyNav
14431  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14432  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14433  * way to implement custom navigation schemes for any UI component.</p>
14434  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14435  * pageUp, pageDown, del, home, end.  Usage:</p>
14436  <pre><code>
14437 var nav = new Roo.KeyNav("my-element", {
14438     "left" : function(e){
14439         this.moveLeft(e.ctrlKey);
14440     },
14441     "right" : function(e){
14442         this.moveRight(e.ctrlKey);
14443     },
14444     "enter" : function(e){
14445         this.save();
14446     },
14447     scope : this
14448 });
14449 </code></pre>
14450  * @constructor
14451  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14452  * @param {Object} config The config
14453  */
14454 Roo.KeyNav = function(el, config){
14455     this.el = Roo.get(el);
14456     Roo.apply(this, config);
14457     if(!this.disabled){
14458         this.disabled = true;
14459         this.enable();
14460     }
14461 };
14462
14463 Roo.KeyNav.prototype = {
14464     /**
14465      * @cfg {Boolean} disabled
14466      * True to disable this KeyNav instance (defaults to false)
14467      */
14468     disabled : false,
14469     /**
14470      * @cfg {String} defaultEventAction
14471      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14472      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14473      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14474      */
14475     defaultEventAction: "stopEvent",
14476     /**
14477      * @cfg {Boolean} forceKeyDown
14478      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14479      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14480      * handle keydown instead of keypress.
14481      */
14482     forceKeyDown : false,
14483
14484     // private
14485     prepareEvent : function(e){
14486         var k = e.getKey();
14487         var h = this.keyToHandler[k];
14488         //if(h && this[h]){
14489         //    e.stopPropagation();
14490         //}
14491         if(Roo.isSafari && h && k >= 37 && k <= 40){
14492             e.stopEvent();
14493         }
14494     },
14495
14496     // private
14497     relay : function(e){
14498         var k = e.getKey();
14499         var h = this.keyToHandler[k];
14500         if(h && this[h]){
14501             if(this.doRelay(e, this[h], h) !== true){
14502                 e[this.defaultEventAction]();
14503             }
14504         }
14505     },
14506
14507     // private
14508     doRelay : function(e, h, hname){
14509         return h.call(this.scope || this, e);
14510     },
14511
14512     // possible handlers
14513     enter : false,
14514     left : false,
14515     right : false,
14516     up : false,
14517     down : false,
14518     tab : false,
14519     esc : false,
14520     pageUp : false,
14521     pageDown : false,
14522     del : false,
14523     home : false,
14524     end : false,
14525
14526     // quick lookup hash
14527     keyToHandler : {
14528         37 : "left",
14529         39 : "right",
14530         38 : "up",
14531         40 : "down",
14532         33 : "pageUp",
14533         34 : "pageDown",
14534         46 : "del",
14535         36 : "home",
14536         35 : "end",
14537         13 : "enter",
14538         27 : "esc",
14539         9  : "tab"
14540     },
14541
14542         /**
14543          * Enable this KeyNav
14544          */
14545         enable: function(){
14546                 if(this.disabled){
14547             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14548             // the EventObject will normalize Safari automatically
14549             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14550                 this.el.on("keydown", this.relay,  this);
14551             }else{
14552                 this.el.on("keydown", this.prepareEvent,  this);
14553                 this.el.on("keypress", this.relay,  this);
14554             }
14555                     this.disabled = false;
14556                 }
14557         },
14558
14559         /**
14560          * Disable this KeyNav
14561          */
14562         disable: function(){
14563                 if(!this.disabled){
14564                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14565                 this.el.un("keydown", this.relay);
14566             }else{
14567                 this.el.un("keydown", this.prepareEvent);
14568                 this.el.un("keypress", this.relay);
14569             }
14570                     this.disabled = true;
14571                 }
14572         }
14573 };/*
14574  * Based on:
14575  * Ext JS Library 1.1.1
14576  * Copyright(c) 2006-2007, Ext JS, LLC.
14577  *
14578  * Originally Released Under LGPL - original licence link has changed is not relivant.
14579  *
14580  * Fork - LGPL
14581  * <script type="text/javascript">
14582  */
14583
14584  
14585 /**
14586  * @class Roo.KeyMap
14587  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14588  * The constructor accepts the same config object as defined by {@link #addBinding}.
14589  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14590  * combination it will call the function with this signature (if the match is a multi-key
14591  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14592  * A KeyMap can also handle a string representation of keys.<br />
14593  * Usage:
14594  <pre><code>
14595 // map one key by key code
14596 var map = new Roo.KeyMap("my-element", {
14597     key: 13, // or Roo.EventObject.ENTER
14598     fn: myHandler,
14599     scope: myObject
14600 });
14601
14602 // map multiple keys to one action by string
14603 var map = new Roo.KeyMap("my-element", {
14604     key: "a\r\n\t",
14605     fn: myHandler,
14606     scope: myObject
14607 });
14608
14609 // map multiple keys to multiple actions by strings and array of codes
14610 var map = new Roo.KeyMap("my-element", [
14611     {
14612         key: [10,13],
14613         fn: function(){ alert("Return was pressed"); }
14614     }, {
14615         key: "abc",
14616         fn: function(){ alert('a, b or c was pressed'); }
14617     }, {
14618         key: "\t",
14619         ctrl:true,
14620         shift:true,
14621         fn: function(){ alert('Control + shift + tab was pressed.'); }
14622     }
14623 ]);
14624 </code></pre>
14625  * <b>Note: A KeyMap starts enabled</b>
14626  * @constructor
14627  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14628  * @param {Object} config The config (see {@link #addBinding})
14629  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14630  */
14631 Roo.KeyMap = function(el, config, eventName){
14632     this.el  = Roo.get(el);
14633     this.eventName = eventName || "keydown";
14634     this.bindings = [];
14635     if(config){
14636         this.addBinding(config);
14637     }
14638     this.enable();
14639 };
14640
14641 Roo.KeyMap.prototype = {
14642     /**
14643      * True to stop the event from bubbling and prevent the default browser action if the
14644      * key was handled by the KeyMap (defaults to false)
14645      * @type Boolean
14646      */
14647     stopEvent : false,
14648
14649     /**
14650      * Add a new binding to this KeyMap. The following config object properties are supported:
14651      * <pre>
14652 Property    Type             Description
14653 ----------  ---------------  ----------------------------------------------------------------------
14654 key         String/Array     A single keycode or an array of keycodes to handle
14655 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14656 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14657 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14658 fn          Function         The function to call when KeyMap finds the expected key combination
14659 scope       Object           The scope of the callback function
14660 </pre>
14661      *
14662      * Usage:
14663      * <pre><code>
14664 // Create a KeyMap
14665 var map = new Roo.KeyMap(document, {
14666     key: Roo.EventObject.ENTER,
14667     fn: handleKey,
14668     scope: this
14669 });
14670
14671 //Add a new binding to the existing KeyMap later
14672 map.addBinding({
14673     key: 'abc',
14674     shift: true,
14675     fn: handleKey,
14676     scope: this
14677 });
14678 </code></pre>
14679      * @param {Object/Array} config A single KeyMap config or an array of configs
14680      */
14681         addBinding : function(config){
14682         if(config instanceof Array){
14683             for(var i = 0, len = config.length; i < len; i++){
14684                 this.addBinding(config[i]);
14685             }
14686             return;
14687         }
14688         var keyCode = config.key,
14689             shift = config.shift, 
14690             ctrl = config.ctrl, 
14691             alt = config.alt,
14692             fn = config.fn,
14693             scope = config.scope;
14694         if(typeof keyCode == "string"){
14695             var ks = [];
14696             var keyString = keyCode.toUpperCase();
14697             for(var j = 0, len = keyString.length; j < len; j++){
14698                 ks.push(keyString.charCodeAt(j));
14699             }
14700             keyCode = ks;
14701         }
14702         var keyArray = keyCode instanceof Array;
14703         var handler = function(e){
14704             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14705                 var k = e.getKey();
14706                 if(keyArray){
14707                     for(var i = 0, len = keyCode.length; i < len; i++){
14708                         if(keyCode[i] == k){
14709                           if(this.stopEvent){
14710                               e.stopEvent();
14711                           }
14712                           fn.call(scope || window, k, e);
14713                           return;
14714                         }
14715                     }
14716                 }else{
14717                     if(k == keyCode){
14718                         if(this.stopEvent){
14719                            e.stopEvent();
14720                         }
14721                         fn.call(scope || window, k, e);
14722                     }
14723                 }
14724             }
14725         };
14726         this.bindings.push(handler);  
14727         },
14728
14729     /**
14730      * Shorthand for adding a single key listener
14731      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14732      * following options:
14733      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14734      * @param {Function} fn The function to call
14735      * @param {Object} scope (optional) The scope of the function
14736      */
14737     on : function(key, fn, scope){
14738         var keyCode, shift, ctrl, alt;
14739         if(typeof key == "object" && !(key instanceof Array)){
14740             keyCode = key.key;
14741             shift = key.shift;
14742             ctrl = key.ctrl;
14743             alt = key.alt;
14744         }else{
14745             keyCode = key;
14746         }
14747         this.addBinding({
14748             key: keyCode,
14749             shift: shift,
14750             ctrl: ctrl,
14751             alt: alt,
14752             fn: fn,
14753             scope: scope
14754         })
14755     },
14756
14757     // private
14758     handleKeyDown : function(e){
14759             if(this.enabled){ //just in case
14760             var b = this.bindings;
14761             for(var i = 0, len = b.length; i < len; i++){
14762                 b[i].call(this, e);
14763             }
14764             }
14765         },
14766         
14767         /**
14768          * Returns true if this KeyMap is enabled
14769          * @return {Boolean} 
14770          */
14771         isEnabled : function(){
14772             return this.enabled;  
14773         },
14774         
14775         /**
14776          * Enables this KeyMap
14777          */
14778         enable: function(){
14779                 if(!this.enabled){
14780                     this.el.on(this.eventName, this.handleKeyDown, this);
14781                     this.enabled = true;
14782                 }
14783         },
14784
14785         /**
14786          * Disable this KeyMap
14787          */
14788         disable: function(){
14789                 if(this.enabled){
14790                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14791                     this.enabled = false;
14792                 }
14793         }
14794 };/*
14795  * Based on:
14796  * Ext JS Library 1.1.1
14797  * Copyright(c) 2006-2007, Ext JS, LLC.
14798  *
14799  * Originally Released Under LGPL - original licence link has changed is not relivant.
14800  *
14801  * Fork - LGPL
14802  * <script type="text/javascript">
14803  */
14804
14805  
14806 /**
14807  * @class Roo.util.TextMetrics
14808  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14809  * wide, in pixels, a given block of text will be.
14810  * @singleton
14811  */
14812 Roo.util.TextMetrics = function(){
14813     var shared;
14814     return {
14815         /**
14816          * Measures the size of the specified text
14817          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14818          * that can affect the size of the rendered text
14819          * @param {String} text The text to measure
14820          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14821          * in order to accurately measure the text height
14822          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14823          */
14824         measure : function(el, text, fixedWidth){
14825             if(!shared){
14826                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14827             }
14828             shared.bind(el);
14829             shared.setFixedWidth(fixedWidth || 'auto');
14830             return shared.getSize(text);
14831         },
14832
14833         /**
14834          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14835          * the overhead of multiple calls to initialize the style properties on each measurement.
14836          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14837          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14838          * in order to accurately measure the text height
14839          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14840          */
14841         createInstance : function(el, fixedWidth){
14842             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14843         }
14844     };
14845 }();
14846
14847  
14848
14849 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14850     var ml = new Roo.Element(document.createElement('div'));
14851     document.body.appendChild(ml.dom);
14852     ml.position('absolute');
14853     ml.setLeftTop(-1000, -1000);
14854     ml.hide();
14855
14856     if(fixedWidth){
14857         ml.setWidth(fixedWidth);
14858     }
14859      
14860     var instance = {
14861         /**
14862          * Returns the size of the specified text based on the internal element's style and width properties
14863          * @memberOf Roo.util.TextMetrics.Instance#
14864          * @param {String} text The text to measure
14865          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14866          */
14867         getSize : function(text){
14868             ml.update(text);
14869             var s = ml.getSize();
14870             ml.update('');
14871             return s;
14872         },
14873
14874         /**
14875          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14876          * that can affect the size of the rendered text
14877          * @memberOf Roo.util.TextMetrics.Instance#
14878          * @param {String/HTMLElement} el The element, dom node or id
14879          */
14880         bind : function(el){
14881             ml.setStyle(
14882                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14883             );
14884         },
14885
14886         /**
14887          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14888          * to set a fixed width in order to accurately measure the text height.
14889          * @memberOf Roo.util.TextMetrics.Instance#
14890          * @param {Number} width The width to set on the element
14891          */
14892         setFixedWidth : function(width){
14893             ml.setWidth(width);
14894         },
14895
14896         /**
14897          * Returns the measured width of the specified text
14898          * @memberOf Roo.util.TextMetrics.Instance#
14899          * @param {String} text The text to measure
14900          * @return {Number} width The width in pixels
14901          */
14902         getWidth : function(text){
14903             ml.dom.style.width = 'auto';
14904             return this.getSize(text).width;
14905         },
14906
14907         /**
14908          * Returns the measured height of the specified text.  For multiline text, be sure to call
14909          * {@link #setFixedWidth} if necessary.
14910          * @memberOf Roo.util.TextMetrics.Instance#
14911          * @param {String} text The text to measure
14912          * @return {Number} height The height in pixels
14913          */
14914         getHeight : function(text){
14915             return this.getSize(text).height;
14916         }
14917     };
14918
14919     instance.bind(bindTo);
14920
14921     return instance;
14922 };
14923
14924 // backwards compat
14925 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14926  * Based on:
14927  * Ext JS Library 1.1.1
14928  * Copyright(c) 2006-2007, Ext JS, LLC.
14929  *
14930  * Originally Released Under LGPL - original licence link has changed is not relivant.
14931  *
14932  * Fork - LGPL
14933  * <script type="text/javascript">
14934  */
14935
14936 /**
14937  * @class Roo.state.Provider
14938  * Abstract base class for state provider implementations. This class provides methods
14939  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14940  * Provider interface.
14941  */
14942 Roo.state.Provider = function(){
14943     /**
14944      * @event statechange
14945      * Fires when a state change occurs.
14946      * @param {Provider} this This state provider
14947      * @param {String} key The state key which was changed
14948      * @param {String} value The encoded value for the state
14949      */
14950     this.addEvents({
14951         "statechange": true
14952     });
14953     this.state = {};
14954     Roo.state.Provider.superclass.constructor.call(this);
14955 };
14956 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14957     /**
14958      * Returns the current value for a key
14959      * @param {String} name The key name
14960      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14961      * @return {Mixed} The state data
14962      */
14963     get : function(name, defaultValue){
14964         return typeof this.state[name] == "undefined" ?
14965             defaultValue : this.state[name];
14966     },
14967     
14968     /**
14969      * Clears a value from the state
14970      * @param {String} name The key name
14971      */
14972     clear : function(name){
14973         delete this.state[name];
14974         this.fireEvent("statechange", this, name, null);
14975     },
14976     
14977     /**
14978      * Sets the value for a key
14979      * @param {String} name The key name
14980      * @param {Mixed} value The value to set
14981      */
14982     set : function(name, value){
14983         this.state[name] = value;
14984         this.fireEvent("statechange", this, name, value);
14985     },
14986     
14987     /**
14988      * Decodes a string previously encoded with {@link #encodeValue}.
14989      * @param {String} value The value to decode
14990      * @return {Mixed} The decoded value
14991      */
14992     decodeValue : function(cookie){
14993         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14994         var matches = re.exec(unescape(cookie));
14995         if(!matches || !matches[1]) {
14996             return; // non state cookie
14997         }
14998         var type = matches[1];
14999         var v = matches[2];
15000         switch(type){
15001             case "n":
15002                 return parseFloat(v);
15003             case "d":
15004                 return new Date(Date.parse(v));
15005             case "b":
15006                 return (v == "1");
15007             case "a":
15008                 var all = [];
15009                 var values = v.split("^");
15010                 for(var i = 0, len = values.length; i < len; i++){
15011                     all.push(this.decodeValue(values[i]));
15012                 }
15013                 return all;
15014            case "o":
15015                 var all = {};
15016                 var values = v.split("^");
15017                 for(var i = 0, len = values.length; i < len; i++){
15018                     var kv = values[i].split("=");
15019                     all[kv[0]] = this.decodeValue(kv[1]);
15020                 }
15021                 return all;
15022            default:
15023                 return v;
15024         }
15025     },
15026     
15027     /**
15028      * Encodes a value including type information.  Decode with {@link #decodeValue}.
15029      * @param {Mixed} value The value to encode
15030      * @return {String} The encoded value
15031      */
15032     encodeValue : function(v){
15033         var enc;
15034         if(typeof v == "number"){
15035             enc = "n:" + v;
15036         }else if(typeof v == "boolean"){
15037             enc = "b:" + (v ? "1" : "0");
15038         }else if(v instanceof Date){
15039             enc = "d:" + v.toGMTString();
15040         }else if(v instanceof Array){
15041             var flat = "";
15042             for(var i = 0, len = v.length; i < len; i++){
15043                 flat += this.encodeValue(v[i]);
15044                 if(i != len-1) {
15045                     flat += "^";
15046                 }
15047             }
15048             enc = "a:" + flat;
15049         }else if(typeof v == "object"){
15050             var flat = "";
15051             for(var key in v){
15052                 if(typeof v[key] != "function"){
15053                     flat += key + "=" + this.encodeValue(v[key]) + "^";
15054                 }
15055             }
15056             enc = "o:" + flat.substring(0, flat.length-1);
15057         }else{
15058             enc = "s:" + v;
15059         }
15060         return escape(enc);        
15061     }
15062 });
15063
15064 /*
15065  * Based on:
15066  * Ext JS Library 1.1.1
15067  * Copyright(c) 2006-2007, Ext JS, LLC.
15068  *
15069  * Originally Released Under LGPL - original licence link has changed is not relivant.
15070  *
15071  * Fork - LGPL
15072  * <script type="text/javascript">
15073  */
15074 /**
15075  * @class Roo.state.Manager
15076  * This is the global state manager. By default all components that are "state aware" check this class
15077  * for state information if you don't pass them a custom state provider. In order for this class
15078  * to be useful, it must be initialized with a provider when your application initializes.
15079  <pre><code>
15080 // in your initialization function
15081 init : function(){
15082    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
15083    ...
15084    // supposed you have a {@link Roo.BorderLayout}
15085    var layout = new Roo.BorderLayout(...);
15086    layout.restoreState();
15087    // or a {Roo.BasicDialog}
15088    var dialog = new Roo.BasicDialog(...);
15089    dialog.restoreState();
15090  </code></pre>
15091  * @singleton
15092  */
15093 Roo.state.Manager = function(){
15094     var provider = new Roo.state.Provider();
15095     
15096     return {
15097         /**
15098          * Configures the default state provider for your application
15099          * @param {Provider} stateProvider The state provider to set
15100          */
15101         setProvider : function(stateProvider){
15102             provider = stateProvider;
15103         },
15104         
15105         /**
15106          * Returns the current value for a key
15107          * @param {String} name The key name
15108          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
15109          * @return {Mixed} The state data
15110          */
15111         get : function(key, defaultValue){
15112             return provider.get(key, defaultValue);
15113         },
15114         
15115         /**
15116          * Sets the value for a key
15117          * @param {String} name The key name
15118          * @param {Mixed} value The state data
15119          */
15120          set : function(key, value){
15121             provider.set(key, value);
15122         },
15123         
15124         /**
15125          * Clears a value from the state
15126          * @param {String} name The key name
15127          */
15128         clear : function(key){
15129             provider.clear(key);
15130         },
15131         
15132         /**
15133          * Gets the currently configured state provider
15134          * @return {Provider} The state provider
15135          */
15136         getProvider : function(){
15137             return provider;
15138         }
15139     };
15140 }();
15141 /*
15142  * Based on:
15143  * Ext JS Library 1.1.1
15144  * Copyright(c) 2006-2007, Ext JS, LLC.
15145  *
15146  * Originally Released Under LGPL - original licence link has changed is not relivant.
15147  *
15148  * Fork - LGPL
15149  * <script type="text/javascript">
15150  */
15151 /**
15152  * @class Roo.state.CookieProvider
15153  * @extends Roo.state.Provider
15154  * The default Provider implementation which saves state via cookies.
15155  * <br />Usage:
15156  <pre><code>
15157    var cp = new Roo.state.CookieProvider({
15158        path: "/cgi-bin/",
15159        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
15160        domain: "roojs.com"
15161    })
15162    Roo.state.Manager.setProvider(cp);
15163  </code></pre>
15164  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
15165  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
15166  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
15167  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
15168  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
15169  * domain the page is running on including the 'www' like 'www.roojs.com')
15170  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
15171  * @constructor
15172  * Create a new CookieProvider
15173  * @param {Object} config The configuration object
15174  */
15175 Roo.state.CookieProvider = function(config){
15176     Roo.state.CookieProvider.superclass.constructor.call(this);
15177     this.path = "/";
15178     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
15179     this.domain = null;
15180     this.secure = false;
15181     Roo.apply(this, config);
15182     this.state = this.readCookies();
15183 };
15184
15185 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
15186     // private
15187     set : function(name, value){
15188         if(typeof value == "undefined" || value === null){
15189             this.clear(name);
15190             return;
15191         }
15192         this.setCookie(name, value);
15193         Roo.state.CookieProvider.superclass.set.call(this, name, value);
15194     },
15195
15196     // private
15197     clear : function(name){
15198         this.clearCookie(name);
15199         Roo.state.CookieProvider.superclass.clear.call(this, name);
15200     },
15201
15202     // private
15203     readCookies : function(){
15204         var cookies = {};
15205         var c = document.cookie + ";";
15206         var re = /\s?(.*?)=(.*?);/g;
15207         var matches;
15208         while((matches = re.exec(c)) != null){
15209             var name = matches[1];
15210             var value = matches[2];
15211             if(name && name.substring(0,3) == "ys-"){
15212                 cookies[name.substr(3)] = this.decodeValue(value);
15213             }
15214         }
15215         return cookies;
15216     },
15217
15218     // private
15219     setCookie : function(name, value){
15220         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15221            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15222            ((this.path == null) ? "" : ("; path=" + this.path)) +
15223            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15224            ((this.secure == true) ? "; secure" : "");
15225     },
15226
15227     // private
15228     clearCookie : function(name){
15229         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15230            ((this.path == null) ? "" : ("; path=" + this.path)) +
15231            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15232            ((this.secure == true) ? "; secure" : "");
15233     }
15234 });/*
15235  * Based on:
15236  * Ext JS Library 1.1.1
15237  * Copyright(c) 2006-2007, Ext JS, LLC.
15238  *
15239  * Originally Released Under LGPL - original licence link has changed is not relivant.
15240  *
15241  * Fork - LGPL
15242  * <script type="text/javascript">
15243  */
15244  
15245
15246 /**
15247  * @class Roo.ComponentMgr
15248  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15249  * @singleton
15250  */
15251 Roo.ComponentMgr = function(){
15252     var all = new Roo.util.MixedCollection();
15253
15254     return {
15255         /**
15256          * Registers a component.
15257          * @param {Roo.Component} c The component
15258          */
15259         register : function(c){
15260             all.add(c);
15261         },
15262
15263         /**
15264          * Unregisters a component.
15265          * @param {Roo.Component} c The component
15266          */
15267         unregister : function(c){
15268             all.remove(c);
15269         },
15270
15271         /**
15272          * Returns a component by id
15273          * @param {String} id The component id
15274          */
15275         get : function(id){
15276             return all.get(id);
15277         },
15278
15279         /**
15280          * Registers a function that will be called when a specified component is added to ComponentMgr
15281          * @param {String} id The component id
15282          * @param {Funtction} fn The callback function
15283          * @param {Object} scope The scope of the callback
15284          */
15285         onAvailable : function(id, fn, scope){
15286             all.on("add", function(index, o){
15287                 if(o.id == id){
15288                     fn.call(scope || o, o);
15289                     all.un("add", fn, scope);
15290                 }
15291             });
15292         }
15293     };
15294 }();/*
15295  * Based on:
15296  * Ext JS Library 1.1.1
15297  * Copyright(c) 2006-2007, Ext JS, LLC.
15298  *
15299  * Originally Released Under LGPL - original licence link has changed is not relivant.
15300  *
15301  * Fork - LGPL
15302  * <script type="text/javascript">
15303  */
15304  
15305 /**
15306  * @class Roo.Component
15307  * @extends Roo.util.Observable
15308  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15309  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15310  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15311  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15312  * All visual components (widgets) that require rendering into a layout should subclass Component.
15313  * @constructor
15314  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15315  * 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
15316  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15317  */
15318 Roo.Component = function(config){
15319     config = config || {};
15320     if(config.tagName || config.dom || typeof config == "string"){ // element object
15321         config = {el: config, id: config.id || config};
15322     }
15323     this.initialConfig = config;
15324
15325     Roo.apply(this, config);
15326     this.addEvents({
15327         /**
15328          * @event disable
15329          * Fires after the component is disabled.
15330              * @param {Roo.Component} this
15331              */
15332         disable : true,
15333         /**
15334          * @event enable
15335          * Fires after the component is enabled.
15336              * @param {Roo.Component} this
15337              */
15338         enable : true,
15339         /**
15340          * @event beforeshow
15341          * Fires before the component is shown.  Return false to stop the show.
15342              * @param {Roo.Component} this
15343              */
15344         beforeshow : true,
15345         /**
15346          * @event show
15347          * Fires after the component is shown.
15348              * @param {Roo.Component} this
15349              */
15350         show : true,
15351         /**
15352          * @event beforehide
15353          * Fires before the component is hidden. Return false to stop the hide.
15354              * @param {Roo.Component} this
15355              */
15356         beforehide : true,
15357         /**
15358          * @event hide
15359          * Fires after the component is hidden.
15360              * @param {Roo.Component} this
15361              */
15362         hide : true,
15363         /**
15364          * @event beforerender
15365          * Fires before the component is rendered. Return false to stop the render.
15366              * @param {Roo.Component} this
15367              */
15368         beforerender : true,
15369         /**
15370          * @event render
15371          * Fires after the component is rendered.
15372              * @param {Roo.Component} this
15373              */
15374         render : true,
15375         /**
15376          * @event beforedestroy
15377          * Fires before the component is destroyed. Return false to stop the destroy.
15378              * @param {Roo.Component} this
15379              */
15380         beforedestroy : true,
15381         /**
15382          * @event destroy
15383          * Fires after the component is destroyed.
15384              * @param {Roo.Component} this
15385              */
15386         destroy : true
15387     });
15388     if(!this.id){
15389         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
15390     }
15391     Roo.ComponentMgr.register(this);
15392     Roo.Component.superclass.constructor.call(this);
15393     this.initComponent();
15394     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15395         this.render(this.renderTo);
15396         delete this.renderTo;
15397     }
15398 };
15399
15400 /** @private */
15401 Roo.Component.AUTO_ID = 1000;
15402
15403 Roo.extend(Roo.Component, Roo.util.Observable, {
15404     /**
15405      * @scope Roo.Component.prototype
15406      * @type {Boolean}
15407      * true if this component is hidden. Read-only.
15408      */
15409     hidden : false,
15410     /**
15411      * @type {Boolean}
15412      * true if this component is disabled. Read-only.
15413      */
15414     disabled : false,
15415     /**
15416      * @type {Boolean}
15417      * true if this component has been rendered. Read-only.
15418      */
15419     rendered : false,
15420     
15421     /** @cfg {String} disableClass
15422      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15423      */
15424     disabledClass : "x-item-disabled",
15425         /** @cfg {Boolean} allowDomMove
15426          * Whether the component can move the Dom node when rendering (defaults to true).
15427          */
15428     allowDomMove : true,
15429     /** @cfg {String} hideMode (display|visibility)
15430      * How this component should hidden. Supported values are
15431      * "visibility" (css visibility), "offsets" (negative offset position) and
15432      * "display" (css display) - defaults to "display".
15433      */
15434     hideMode: 'display',
15435
15436     /** @private */
15437     ctype : "Roo.Component",
15438
15439     /**
15440      * @cfg {String} actionMode 
15441      * which property holds the element that used for  hide() / show() / disable() / enable()
15442      * default is 'el' for forms you probably want to set this to fieldEl 
15443      */
15444     actionMode : "el",
15445
15446     /** @private */
15447     getActionEl : function(){
15448         return this[this.actionMode];
15449     },
15450
15451     initComponent : Roo.emptyFn,
15452     /**
15453      * If this is a lazy rendering component, render it to its container element.
15454      * @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.
15455      */
15456     render : function(container, position){
15457         
15458         if(this.rendered){
15459             return this;
15460         }
15461         
15462         if(this.fireEvent("beforerender", this) === false){
15463             return false;
15464         }
15465         
15466         if(!container && this.el){
15467             this.el = Roo.get(this.el);
15468             container = this.el.dom.parentNode;
15469             this.allowDomMove = false;
15470         }
15471         this.container = Roo.get(container);
15472         this.rendered = true;
15473         if(position !== undefined){
15474             if(typeof position == 'number'){
15475                 position = this.container.dom.childNodes[position];
15476             }else{
15477                 position = Roo.getDom(position);
15478             }
15479         }
15480         this.onRender(this.container, position || null);
15481         if(this.cls){
15482             this.el.addClass(this.cls);
15483             delete this.cls;
15484         }
15485         if(this.style){
15486             this.el.applyStyles(this.style);
15487             delete this.style;
15488         }
15489         this.fireEvent("render", this);
15490         this.afterRender(this.container);
15491         if(this.hidden){
15492             this.hide();
15493         }
15494         if(this.disabled){
15495             this.disable();
15496         }
15497
15498         return this;
15499         
15500     },
15501
15502     /** @private */
15503     // default function is not really useful
15504     onRender : function(ct, position){
15505         if(this.el){
15506             this.el = Roo.get(this.el);
15507             if(this.allowDomMove !== false){
15508                 ct.dom.insertBefore(this.el.dom, position);
15509             }
15510         }
15511     },
15512
15513     /** @private */
15514     getAutoCreate : function(){
15515         var cfg = typeof this.autoCreate == "object" ?
15516                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15517         if(this.id && !cfg.id){
15518             cfg.id = this.id;
15519         }
15520         return cfg;
15521     },
15522
15523     /** @private */
15524     afterRender : Roo.emptyFn,
15525
15526     /**
15527      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15528      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15529      */
15530     destroy : function(){
15531         if(this.fireEvent("beforedestroy", this) !== false){
15532             this.purgeListeners();
15533             this.beforeDestroy();
15534             if(this.rendered){
15535                 this.el.removeAllListeners();
15536                 this.el.remove();
15537                 if(this.actionMode == "container"){
15538                     this.container.remove();
15539                 }
15540             }
15541             this.onDestroy();
15542             Roo.ComponentMgr.unregister(this);
15543             this.fireEvent("destroy", this);
15544         }
15545     },
15546
15547         /** @private */
15548     beforeDestroy : function(){
15549
15550     },
15551
15552         /** @private */
15553         onDestroy : function(){
15554
15555     },
15556
15557     /**
15558      * Returns the underlying {@link Roo.Element}.
15559      * @return {Roo.Element} The element
15560      */
15561     getEl : function(){
15562         return this.el;
15563     },
15564
15565     /**
15566      * Returns the id of this component.
15567      * @return {String}
15568      */
15569     getId : function(){
15570         return this.id;
15571     },
15572
15573     /**
15574      * Try to focus this component.
15575      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15576      * @return {Roo.Component} this
15577      */
15578     focus : function(selectText){
15579         if(this.rendered){
15580             this.el.focus();
15581             if(selectText === true){
15582                 this.el.dom.select();
15583             }
15584         }
15585         return this;
15586     },
15587
15588     /** @private */
15589     blur : function(){
15590         if(this.rendered){
15591             this.el.blur();
15592         }
15593         return this;
15594     },
15595
15596     /**
15597      * Disable this component.
15598      * @return {Roo.Component} this
15599      */
15600     disable : function(){
15601         if(this.rendered){
15602             this.onDisable();
15603         }
15604         this.disabled = true;
15605         this.fireEvent("disable", this);
15606         return this;
15607     },
15608
15609         // private
15610     onDisable : function(){
15611         this.getActionEl().addClass(this.disabledClass);
15612         this.el.dom.disabled = true;
15613     },
15614
15615     /**
15616      * Enable this component.
15617      * @return {Roo.Component} this
15618      */
15619     enable : function(){
15620         if(this.rendered){
15621             this.onEnable();
15622         }
15623         this.disabled = false;
15624         this.fireEvent("enable", this);
15625         return this;
15626     },
15627
15628         // private
15629     onEnable : function(){
15630         this.getActionEl().removeClass(this.disabledClass);
15631         this.el.dom.disabled = false;
15632     },
15633
15634     /**
15635      * Convenience function for setting disabled/enabled by boolean.
15636      * @param {Boolean} disabled
15637      */
15638     setDisabled : function(disabled){
15639         this[disabled ? "disable" : "enable"]();
15640     },
15641
15642     /**
15643      * Show this component.
15644      * @return {Roo.Component} this
15645      */
15646     show: function(){
15647         if(this.fireEvent("beforeshow", this) !== false){
15648             this.hidden = false;
15649             if(this.rendered){
15650                 this.onShow();
15651             }
15652             this.fireEvent("show", this);
15653         }
15654         return this;
15655     },
15656
15657     // private
15658     onShow : function(){
15659         var ae = this.getActionEl();
15660         if(this.hideMode == 'visibility'){
15661             ae.dom.style.visibility = "visible";
15662         }else if(this.hideMode == 'offsets'){
15663             ae.removeClass('x-hidden');
15664         }else{
15665             ae.dom.style.display = "";
15666         }
15667     },
15668
15669     /**
15670      * Hide this component.
15671      * @return {Roo.Component} this
15672      */
15673     hide: function(){
15674         if(this.fireEvent("beforehide", this) !== false){
15675             this.hidden = true;
15676             if(this.rendered){
15677                 this.onHide();
15678             }
15679             this.fireEvent("hide", this);
15680         }
15681         return this;
15682     },
15683
15684     // private
15685     onHide : function(){
15686         var ae = this.getActionEl();
15687         if(this.hideMode == 'visibility'){
15688             ae.dom.style.visibility = "hidden";
15689         }else if(this.hideMode == 'offsets'){
15690             ae.addClass('x-hidden');
15691         }else{
15692             ae.dom.style.display = "none";
15693         }
15694     },
15695
15696     /**
15697      * Convenience function to hide or show this component by boolean.
15698      * @param {Boolean} visible True to show, false to hide
15699      * @return {Roo.Component} this
15700      */
15701     setVisible: function(visible){
15702         if(visible) {
15703             this.show();
15704         }else{
15705             this.hide();
15706         }
15707         return this;
15708     },
15709
15710     /**
15711      * Returns true if this component is visible.
15712      */
15713     isVisible : function(){
15714         return this.getActionEl().isVisible();
15715     },
15716
15717     cloneConfig : function(overrides){
15718         overrides = overrides || {};
15719         var id = overrides.id || Roo.id();
15720         var cfg = Roo.applyIf(overrides, this.initialConfig);
15721         cfg.id = id; // prevent dup id
15722         return new this.constructor(cfg);
15723     }
15724 });/*
15725  * Based on:
15726  * Ext JS Library 1.1.1
15727  * Copyright(c) 2006-2007, Ext JS, LLC.
15728  *
15729  * Originally Released Under LGPL - original licence link has changed is not relivant.
15730  *
15731  * Fork - LGPL
15732  * <script type="text/javascript">
15733  */
15734
15735 /**
15736  * @class Roo.BoxComponent
15737  * @extends Roo.Component
15738  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15739  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15740  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15741  * layout containers.
15742  * @constructor
15743  * @param {Roo.Element/String/Object} config The configuration options.
15744  */
15745 Roo.BoxComponent = function(config){
15746     Roo.Component.call(this, config);
15747     this.addEvents({
15748         /**
15749          * @event resize
15750          * Fires after the component is resized.
15751              * @param {Roo.Component} this
15752              * @param {Number} adjWidth The box-adjusted width that was set
15753              * @param {Number} adjHeight The box-adjusted height that was set
15754              * @param {Number} rawWidth The width that was originally specified
15755              * @param {Number} rawHeight The height that was originally specified
15756              */
15757         resize : true,
15758         /**
15759          * @event move
15760          * Fires after the component is moved.
15761              * @param {Roo.Component} this
15762              * @param {Number} x The new x position
15763              * @param {Number} y The new y position
15764              */
15765         move : true
15766     });
15767 };
15768
15769 Roo.extend(Roo.BoxComponent, Roo.Component, {
15770     // private, set in afterRender to signify that the component has been rendered
15771     boxReady : false,
15772     // private, used to defer height settings to subclasses
15773     deferHeight: false,
15774     /** @cfg {Number} width
15775      * width (optional) size of component
15776      */
15777      /** @cfg {Number} height
15778      * height (optional) size of component
15779      */
15780      
15781     /**
15782      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15783      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15784      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15785      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15786      * @return {Roo.BoxComponent} this
15787      */
15788     setSize : function(w, h){
15789         // support for standard size objects
15790         if(typeof w == 'object'){
15791             h = w.height;
15792             w = w.width;
15793         }
15794         // not rendered
15795         if(!this.boxReady){
15796             this.width = w;
15797             this.height = h;
15798             return this;
15799         }
15800
15801         // prevent recalcs when not needed
15802         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15803             return this;
15804         }
15805         this.lastSize = {width: w, height: h};
15806
15807         var adj = this.adjustSize(w, h);
15808         var aw = adj.width, ah = adj.height;
15809         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15810             var rz = this.getResizeEl();
15811             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15812                 rz.setSize(aw, ah);
15813             }else if(!this.deferHeight && ah !== undefined){
15814                 rz.setHeight(ah);
15815             }else if(aw !== undefined){
15816                 rz.setWidth(aw);
15817             }
15818             this.onResize(aw, ah, w, h);
15819             this.fireEvent('resize', this, aw, ah, w, h);
15820         }
15821         return this;
15822     },
15823
15824     /**
15825      * Gets the current size of the component's underlying element.
15826      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15827      */
15828     getSize : function(){
15829         return this.el.getSize();
15830     },
15831
15832     /**
15833      * Gets the current XY position of the component's underlying element.
15834      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15835      * @return {Array} The XY position of the element (e.g., [100, 200])
15836      */
15837     getPosition : function(local){
15838         if(local === true){
15839             return [this.el.getLeft(true), this.el.getTop(true)];
15840         }
15841         return this.xy || this.el.getXY();
15842     },
15843
15844     /**
15845      * Gets the current box measurements of the component's underlying element.
15846      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15847      * @returns {Object} box An object in the format {x, y, width, height}
15848      */
15849     getBox : function(local){
15850         var s = this.el.getSize();
15851         if(local){
15852             s.x = this.el.getLeft(true);
15853             s.y = this.el.getTop(true);
15854         }else{
15855             var xy = this.xy || this.el.getXY();
15856             s.x = xy[0];
15857             s.y = xy[1];
15858         }
15859         return s;
15860     },
15861
15862     /**
15863      * Sets the current box measurements of the component's underlying element.
15864      * @param {Object} box An object in the format {x, y, width, height}
15865      * @returns {Roo.BoxComponent} this
15866      */
15867     updateBox : function(box){
15868         this.setSize(box.width, box.height);
15869         this.setPagePosition(box.x, box.y);
15870         return this;
15871     },
15872
15873     // protected
15874     getResizeEl : function(){
15875         return this.resizeEl || this.el;
15876     },
15877
15878     // protected
15879     getPositionEl : function(){
15880         return this.positionEl || this.el;
15881     },
15882
15883     /**
15884      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15885      * This method fires the move event.
15886      * @param {Number} left The new left
15887      * @param {Number} top The new top
15888      * @returns {Roo.BoxComponent} this
15889      */
15890     setPosition : function(x, y){
15891         this.x = x;
15892         this.y = y;
15893         if(!this.boxReady){
15894             return this;
15895         }
15896         var adj = this.adjustPosition(x, y);
15897         var ax = adj.x, ay = adj.y;
15898
15899         var el = this.getPositionEl();
15900         if(ax !== undefined || ay !== undefined){
15901             if(ax !== undefined && ay !== undefined){
15902                 el.setLeftTop(ax, ay);
15903             }else if(ax !== undefined){
15904                 el.setLeft(ax);
15905             }else if(ay !== undefined){
15906                 el.setTop(ay);
15907             }
15908             this.onPosition(ax, ay);
15909             this.fireEvent('move', this, ax, ay);
15910         }
15911         return this;
15912     },
15913
15914     /**
15915      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15916      * This method fires the move event.
15917      * @param {Number} x The new x position
15918      * @param {Number} y The new y position
15919      * @returns {Roo.BoxComponent} this
15920      */
15921     setPagePosition : function(x, y){
15922         this.pageX = x;
15923         this.pageY = y;
15924         if(!this.boxReady){
15925             return;
15926         }
15927         if(x === undefined || y === undefined){ // cannot translate undefined points
15928             return;
15929         }
15930         var p = this.el.translatePoints(x, y);
15931         this.setPosition(p.left, p.top);
15932         return this;
15933     },
15934
15935     // private
15936     onRender : function(ct, position){
15937         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15938         if(this.resizeEl){
15939             this.resizeEl = Roo.get(this.resizeEl);
15940         }
15941         if(this.positionEl){
15942             this.positionEl = Roo.get(this.positionEl);
15943         }
15944     },
15945
15946     // private
15947     afterRender : function(){
15948         Roo.BoxComponent.superclass.afterRender.call(this);
15949         this.boxReady = true;
15950         this.setSize(this.width, this.height);
15951         if(this.x || this.y){
15952             this.setPosition(this.x, this.y);
15953         }
15954         if(this.pageX || this.pageY){
15955             this.setPagePosition(this.pageX, this.pageY);
15956         }
15957     },
15958
15959     /**
15960      * Force the component's size to recalculate based on the underlying element's current height and width.
15961      * @returns {Roo.BoxComponent} this
15962      */
15963     syncSize : function(){
15964         delete this.lastSize;
15965         this.setSize(this.el.getWidth(), this.el.getHeight());
15966         return this;
15967     },
15968
15969     /**
15970      * Called after the component is resized, this method is empty by default but can be implemented by any
15971      * subclass that needs to perform custom logic after a resize occurs.
15972      * @param {Number} adjWidth The box-adjusted width that was set
15973      * @param {Number} adjHeight The box-adjusted height that was set
15974      * @param {Number} rawWidth The width that was originally specified
15975      * @param {Number} rawHeight The height that was originally specified
15976      */
15977     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15978
15979     },
15980
15981     /**
15982      * Called after the component is moved, this method is empty by default but can be implemented by any
15983      * subclass that needs to perform custom logic after a move occurs.
15984      * @param {Number} x The new x position
15985      * @param {Number} y The new y position
15986      */
15987     onPosition : function(x, y){
15988
15989     },
15990
15991     // private
15992     adjustSize : function(w, h){
15993         if(this.autoWidth){
15994             w = 'auto';
15995         }
15996         if(this.autoHeight){
15997             h = 'auto';
15998         }
15999         return {width : w, height: h};
16000     },
16001
16002     // private
16003     adjustPosition : function(x, y){
16004         return {x : x, y: y};
16005     }
16006 });/*
16007  * Original code for Roojs - LGPL
16008  * <script type="text/javascript">
16009  */
16010  
16011 /**
16012  * @class Roo.XComponent
16013  * A delayed Element creator...
16014  * Or a way to group chunks of interface together.
16015  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
16016  *  used in conjunction with XComponent.build() it will create an instance of each element,
16017  *  then call addxtype() to build the User interface.
16018  * 
16019  * Mypart.xyx = new Roo.XComponent({
16020
16021     parent : 'Mypart.xyz', // empty == document.element.!!
16022     order : '001',
16023     name : 'xxxx'
16024     region : 'xxxx'
16025     disabled : function() {} 
16026      
16027     tree : function() { // return an tree of xtype declared components
16028         var MODULE = this;
16029         return 
16030         {
16031             xtype : 'NestedLayoutPanel',
16032             // technicall
16033         }
16034      ]
16035  *})
16036  *
16037  *
16038  * It can be used to build a big heiracy, with parent etc.
16039  * or you can just use this to render a single compoent to a dom element
16040  * MYPART.render(Roo.Element | String(id) | dom_element )
16041  *
16042  *
16043  * Usage patterns.
16044  *
16045  * Classic Roo
16046  *
16047  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
16048  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
16049  *
16050  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
16051  *
16052  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
16053  * - if mulitple topModules exist, the last one is defined as the top module.
16054  *
16055  * Embeded Roo
16056  * 
16057  * When the top level or multiple modules are to embedded into a existing HTML page,
16058  * the parent element can container '#id' of the element where the module will be drawn.
16059  *
16060  * Bootstrap Roo
16061  *
16062  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
16063  * it relies more on a include mechanism, where sub modules are included into an outer page.
16064  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
16065  * 
16066  * Bootstrap Roo Included elements
16067  *
16068  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
16069  * hence confusing the component builder as it thinks there are multiple top level elements. 
16070  *
16071  * String Over-ride & Translations
16072  *
16073  * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
16074  * and also the 'overlaying of string values - needed when different versions of the same application with different text content
16075  * are needed. @see Roo.XComponent.overlayString  
16076  * 
16077  * 
16078  * 
16079  * @extends Roo.util.Observable
16080  * @constructor
16081  * @param cfg {Object} configuration of component
16082  * 
16083  */
16084 Roo.XComponent = function(cfg) {
16085     Roo.apply(this, cfg);
16086     this.addEvents({ 
16087         /**
16088              * @event built
16089              * Fires when this the componnt is built
16090              * @param {Roo.XComponent} c the component
16091              */
16092         'built' : true
16093         
16094     });
16095     this.region = this.region || 'center'; // default..
16096     Roo.XComponent.register(this);
16097     this.modules = false;
16098     this.el = false; // where the layout goes..
16099     
16100     
16101 }
16102 Roo.extend(Roo.XComponent, Roo.util.Observable, {
16103     /**
16104      * @property el
16105      * The created element (with Roo.factory())
16106      * @type {Roo.Layout}
16107      */
16108     el  : false,
16109     
16110     /**
16111      * @property el
16112      * for BC  - use el in new code
16113      * @type {Roo.Layout}
16114      */
16115     panel : false,
16116     
16117     /**
16118      * @property layout
16119      * for BC  - use el in new code
16120      * @type {Roo.Layout}
16121      */
16122     layout : false,
16123     
16124      /**
16125      * @cfg {Function|boolean} disabled
16126      * If this module is disabled by some rule, return true from the funtion
16127      */
16128     disabled : false,
16129     
16130     /**
16131      * @cfg {String} parent 
16132      * Name of parent element which it get xtype added to..
16133      */
16134     parent: false,
16135     
16136     /**
16137      * @cfg {String} order
16138      * Used to set the order in which elements are created (usefull for multiple tabs)
16139      */
16140     
16141     order : false,
16142     /**
16143      * @cfg {String} name
16144      * String to display while loading.
16145      */
16146     name : false,
16147     /**
16148      * @cfg {String} region
16149      * Region to render component to (defaults to center)
16150      */
16151     region : 'center',
16152     
16153     /**
16154      * @cfg {Array} items
16155      * A single item array - the first element is the root of the tree..
16156      * It's done this way to stay compatible with the Xtype system...
16157      */
16158     items : false,
16159     
16160     /**
16161      * @property _tree
16162      * The method that retuns the tree of parts that make up this compoennt 
16163      * @type {function}
16164      */
16165     _tree  : false,
16166     
16167      /**
16168      * render
16169      * render element to dom or tree
16170      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
16171      */
16172     
16173     render : function(el)
16174     {
16175         
16176         el = el || false;
16177         var hp = this.parent ? 1 : 0;
16178         Roo.debug &&  Roo.log(this);
16179         
16180         var tree = this._tree ? this._tree() : this.tree();
16181
16182         
16183         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
16184             // if parent is a '#.....' string, then let's use that..
16185             var ename = this.parent.substr(1);
16186             this.parent = false;
16187             Roo.debug && Roo.log(ename);
16188             switch (ename) {
16189                 case 'bootstrap-body':
16190                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
16191                         // this is the BorderLayout standard?
16192                        this.parent = { el : true };
16193                        break;
16194                     }
16195                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
16196                         // need to insert stuff...
16197                         this.parent =  {
16198                              el : new Roo.bootstrap.layout.Border({
16199                                  el : document.body, 
16200                      
16201                                  center: {
16202                                     titlebar: false,
16203                                     autoScroll:false,
16204                                     closeOnTab: true,
16205                                     tabPosition: 'top',
16206                                       //resizeTabs: true,
16207                                     alwaysShowTabs: true,
16208                                     hideTabs: false
16209                                      //minTabWidth: 140
16210                                  }
16211                              })
16212                         
16213                          };
16214                          break;
16215                     }
16216                          
16217                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
16218                         this.parent = { el :  new  Roo.bootstrap.Body() };
16219                         Roo.debug && Roo.log("setting el to doc body");
16220                          
16221                     } else {
16222                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
16223                     }
16224                     break;
16225                 case 'bootstrap':
16226                     this.parent = { el : true};
16227                     // fall through
16228                 default:
16229                     el = Roo.get(ename);
16230                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
16231                         this.parent = { el : true};
16232                     }
16233                     
16234                     break;
16235             }
16236                 
16237             
16238             if (!el && !this.parent) {
16239                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
16240                 return;
16241             }
16242         }
16243         
16244         Roo.debug && Roo.log("EL:");
16245         Roo.debug && Roo.log(el);
16246         Roo.debug && Roo.log("this.parent.el:");
16247         Roo.debug && Roo.log(this.parent.el);
16248         
16249
16250         // altertive root elements ??? - we need a better way to indicate these.
16251         var is_alt = Roo.XComponent.is_alt ||
16252                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
16253                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
16254                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
16255         
16256         
16257         
16258         if (!this.parent && is_alt) {
16259             //el = Roo.get(document.body);
16260             this.parent = { el : true };
16261         }
16262             
16263             
16264         
16265         if (!this.parent) {
16266             
16267             Roo.debug && Roo.log("no parent - creating one");
16268             
16269             el = el ? Roo.get(el) : false;      
16270             
16271             if (typeof(Roo.BorderLayout) == 'undefined' ) {
16272                 
16273                 this.parent =  {
16274                     el : new Roo.bootstrap.layout.Border({
16275                         el: el || document.body,
16276                     
16277                         center: {
16278                             titlebar: false,
16279                             autoScroll:false,
16280                             closeOnTab: true,
16281                             tabPosition: 'top',
16282                              //resizeTabs: true,
16283                             alwaysShowTabs: false,
16284                             hideTabs: true,
16285                             minTabWidth: 140,
16286                             overflow: 'visible'
16287                          }
16288                      })
16289                 };
16290             } else {
16291             
16292                 // it's a top level one..
16293                 this.parent =  {
16294                     el : new Roo.BorderLayout(el || document.body, {
16295                         center: {
16296                             titlebar: false,
16297                             autoScroll:false,
16298                             closeOnTab: true,
16299                             tabPosition: 'top',
16300                              //resizeTabs: true,
16301                             alwaysShowTabs: el && hp? false :  true,
16302                             hideTabs: el || !hp ? true :  false,
16303                             minTabWidth: 140
16304                          }
16305                     })
16306                 };
16307             }
16308         }
16309         
16310         if (!this.parent.el) {
16311                 // probably an old style ctor, which has been disabled.
16312                 return;
16313
16314         }
16315                 // The 'tree' method is  '_tree now' 
16316             
16317         tree.region = tree.region || this.region;
16318         var is_body = false;
16319         if (this.parent.el === true) {
16320             // bootstrap... - body..
16321             if (el) {
16322                 tree.el = el;
16323             }
16324             this.parent.el = Roo.factory(tree);
16325             is_body = true;
16326         }
16327         
16328         this.el = this.parent.el.addxtype(tree, undefined, is_body);
16329         this.fireEvent('built', this);
16330         
16331         this.panel = this.el;
16332         this.layout = this.panel.layout;
16333         this.parentLayout = this.parent.layout  || false;  
16334          
16335     }
16336     
16337 });
16338
16339 Roo.apply(Roo.XComponent, {
16340     /**
16341      * @property  hideProgress
16342      * true to disable the building progress bar.. usefull on single page renders.
16343      * @type Boolean
16344      */
16345     hideProgress : false,
16346     /**
16347      * @property  buildCompleted
16348      * True when the builder has completed building the interface.
16349      * @type Boolean
16350      */
16351     buildCompleted : false,
16352      
16353     /**
16354      * @property  topModule
16355      * the upper most module - uses document.element as it's constructor.
16356      * @type Object
16357      */
16358      
16359     topModule  : false,
16360       
16361     /**
16362      * @property  modules
16363      * array of modules to be created by registration system.
16364      * @type {Array} of Roo.XComponent
16365      */
16366     
16367     modules : [],
16368     /**
16369      * @property  elmodules
16370      * array of modules to be created by which use #ID 
16371      * @type {Array} of Roo.XComponent
16372      */
16373      
16374     elmodules : [],
16375
16376      /**
16377      * @property  is_alt
16378      * Is an alternative Root - normally used by bootstrap or other systems,
16379      *    where the top element in the tree can wrap 'body' 
16380      * @type {boolean}  (default false)
16381      */
16382      
16383     is_alt : false,
16384     /**
16385      * @property  build_from_html
16386      * Build elements from html - used by bootstrap HTML stuff 
16387      *    - this is cleared after build is completed
16388      * @type {boolean}    (default false)
16389      */
16390      
16391     build_from_html : false,
16392     /**
16393      * Register components to be built later.
16394      *
16395      * This solves the following issues
16396      * - Building is not done on page load, but after an authentication process has occured.
16397      * - Interface elements are registered on page load
16398      * - Parent Interface elements may not be loaded before child, so this handles that..
16399      * 
16400      *
16401      * example:
16402      * 
16403      * MyApp.register({
16404           order : '000001',
16405           module : 'Pman.Tab.projectMgr',
16406           region : 'center',
16407           parent : 'Pman.layout',
16408           disabled : false,  // or use a function..
16409         })
16410      
16411      * * @param {Object} details about module
16412      */
16413     register : function(obj) {
16414                 
16415         Roo.XComponent.event.fireEvent('register', obj);
16416         switch(typeof(obj.disabled) ) {
16417                 
16418             case 'undefined':
16419                 break;
16420             
16421             case 'function':
16422                 if ( obj.disabled() ) {
16423                         return;
16424                 }
16425                 break;
16426             
16427             default:
16428                 if (obj.disabled || obj.region == '#disabled') {
16429                         return;
16430                 }
16431                 break;
16432         }
16433                 
16434         this.modules.push(obj);
16435          
16436     },
16437     /**
16438      * convert a string to an object..
16439      * eg. 'AAA.BBB' -> finds AAA.BBB
16440
16441      */
16442     
16443     toObject : function(str)
16444     {
16445         if (!str || typeof(str) == 'object') {
16446             return str;
16447         }
16448         if (str.substring(0,1) == '#') {
16449             return str;
16450         }
16451
16452         var ar = str.split('.');
16453         var rt, o;
16454         rt = ar.shift();
16455             /** eval:var:o */
16456         try {
16457             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16458         } catch (e) {
16459             throw "Module not found : " + str;
16460         }
16461         
16462         if (o === false) {
16463             throw "Module not found : " + str;
16464         }
16465         Roo.each(ar, function(e) {
16466             if (typeof(o[e]) == 'undefined') {
16467                 throw "Module not found : " + str;
16468             }
16469             o = o[e];
16470         });
16471         
16472         return o;
16473         
16474     },
16475     
16476     
16477     /**
16478      * move modules into their correct place in the tree..
16479      * 
16480      */
16481     preBuild : function ()
16482     {
16483         var _t = this;
16484         Roo.each(this.modules , function (obj)
16485         {
16486             Roo.XComponent.event.fireEvent('beforebuild', obj);
16487             
16488             var opar = obj.parent;
16489             try { 
16490                 obj.parent = this.toObject(opar);
16491             } catch(e) {
16492                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
16493                 return;
16494             }
16495             
16496             if (!obj.parent) {
16497                 Roo.debug && Roo.log("GOT top level module");
16498                 Roo.debug && Roo.log(obj);
16499                 obj.modules = new Roo.util.MixedCollection(false, 
16500                     function(o) { return o.order + '' }
16501                 );
16502                 this.topModule = obj;
16503                 return;
16504             }
16505                         // parent is a string (usually a dom element name..)
16506             if (typeof(obj.parent) == 'string') {
16507                 this.elmodules.push(obj);
16508                 return;
16509             }
16510             if (obj.parent.constructor != Roo.XComponent) {
16511                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16512             }
16513             if (!obj.parent.modules) {
16514                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16515                     function(o) { return o.order + '' }
16516                 );
16517             }
16518             if (obj.parent.disabled) {
16519                 obj.disabled = true;
16520             }
16521             obj.parent.modules.add(obj);
16522         }, this);
16523     },
16524     
16525      /**
16526      * make a list of modules to build.
16527      * @return {Array} list of modules. 
16528      */ 
16529     
16530     buildOrder : function()
16531     {
16532         var _this = this;
16533         var cmp = function(a,b) {   
16534             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16535         };
16536         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16537             throw "No top level modules to build";
16538         }
16539         
16540         // make a flat list in order of modules to build.
16541         var mods = this.topModule ? [ this.topModule ] : [];
16542                 
16543         
16544         // elmodules (is a list of DOM based modules )
16545         Roo.each(this.elmodules, function(e) {
16546             mods.push(e);
16547             if (!this.topModule &&
16548                 typeof(e.parent) == 'string' &&
16549                 e.parent.substring(0,1) == '#' &&
16550                 Roo.get(e.parent.substr(1))
16551                ) {
16552                 
16553                 _this.topModule = e;
16554             }
16555             
16556         });
16557
16558         
16559         // add modules to their parents..
16560         var addMod = function(m) {
16561             Roo.debug && Roo.log("build Order: add: " + m.name);
16562                 
16563             mods.push(m);
16564             if (m.modules && !m.disabled) {
16565                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16566                 m.modules.keySort('ASC',  cmp );
16567                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16568     
16569                 m.modules.each(addMod);
16570             } else {
16571                 Roo.debug && Roo.log("build Order: no child modules");
16572             }
16573             // not sure if this is used any more..
16574             if (m.finalize) {
16575                 m.finalize.name = m.name + " (clean up) ";
16576                 mods.push(m.finalize);
16577             }
16578             
16579         }
16580         if (this.topModule && this.topModule.modules) { 
16581             this.topModule.modules.keySort('ASC',  cmp );
16582             this.topModule.modules.each(addMod);
16583         } 
16584         return mods;
16585     },
16586     
16587      /**
16588      * Build the registered modules.
16589      * @param {Object} parent element.
16590      * @param {Function} optional method to call after module has been added.
16591      * 
16592      */ 
16593    
16594     build : function(opts) 
16595     {
16596         
16597         if (typeof(opts) != 'undefined') {
16598             Roo.apply(this,opts);
16599         }
16600         
16601         this.preBuild();
16602         var mods = this.buildOrder();
16603       
16604         //this.allmods = mods;
16605         //Roo.debug && Roo.log(mods);
16606         //return;
16607         if (!mods.length) { // should not happen
16608             throw "NO modules!!!";
16609         }
16610         
16611         
16612         var msg = "Building Interface...";
16613         // flash it up as modal - so we store the mask!?
16614         if (!this.hideProgress && Roo.MessageBox) {
16615             Roo.MessageBox.show({ title: 'loading' });
16616             Roo.MessageBox.show({
16617                title: "Please wait...",
16618                msg: msg,
16619                width:450,
16620                progress:true,
16621                buttons : false,
16622                closable:false,
16623                modal: false
16624               
16625             });
16626         }
16627         var total = mods.length;
16628         
16629         var _this = this;
16630         var progressRun = function() {
16631             if (!mods.length) {
16632                 Roo.debug && Roo.log('hide?');
16633                 if (!this.hideProgress && Roo.MessageBox) {
16634                     Roo.MessageBox.hide();
16635                 }
16636                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16637                 
16638                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16639                 
16640                 // THE END...
16641                 return false;   
16642             }
16643             
16644             var m = mods.shift();
16645             
16646             
16647             Roo.debug && Roo.log(m);
16648             // not sure if this is supported any more.. - modules that are are just function
16649             if (typeof(m) == 'function') { 
16650                 m.call(this);
16651                 return progressRun.defer(10, _this);
16652             } 
16653             
16654             
16655             msg = "Building Interface " + (total  - mods.length) + 
16656                     " of " + total + 
16657                     (m.name ? (' - ' + m.name) : '');
16658                         Roo.debug && Roo.log(msg);
16659             if (!_this.hideProgress &&  Roo.MessageBox) { 
16660                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16661             }
16662             
16663          
16664             // is the module disabled?
16665             var disabled = (typeof(m.disabled) == 'function') ?
16666                 m.disabled.call(m.module.disabled) : m.disabled;    
16667             
16668             
16669             if (disabled) {
16670                 return progressRun(); // we do not update the display!
16671             }
16672             
16673             // now build 
16674             
16675                         
16676                         
16677             m.render();
16678             // it's 10 on top level, and 1 on others??? why...
16679             return progressRun.defer(10, _this);
16680              
16681         }
16682         progressRun.defer(1, _this);
16683      
16684         
16685         
16686     },
16687     /**
16688      * Overlay a set of modified strings onto a component
16689      * This is dependant on our builder exporting the strings and 'named strings' elements.
16690      * 
16691      * @param {Object} element to overlay on - eg. Pman.Dialog.Login
16692      * @param {Object} associative array of 'named' string and it's new value.
16693      * 
16694      */
16695         overlayStrings : function( component, strings )
16696     {
16697         if (typeof(component['_named_strings']) == 'undefined') {
16698             throw "ERROR: component does not have _named_strings";
16699         }
16700         for ( var k in strings ) {
16701             var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
16702             if (md !== false) {
16703                 component['_strings'][md] = strings[k];
16704             } else {
16705                 Roo.log('could not find named string: ' + k + ' in');
16706                 Roo.log(component);
16707             }
16708             
16709         }
16710         
16711     },
16712     
16713         
16714         /**
16715          * Event Object.
16716          *
16717          *
16718          */
16719         event: false, 
16720     /**
16721          * wrapper for event.on - aliased later..  
16722          * Typically use to register a event handler for register:
16723          *
16724          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16725          *
16726          */
16727     on : false
16728    
16729     
16730     
16731 });
16732
16733 Roo.XComponent.event = new Roo.util.Observable({
16734                 events : { 
16735                         /**
16736                          * @event register
16737                          * Fires when an Component is registered,
16738                          * set the disable property on the Component to stop registration.
16739                          * @param {Roo.XComponent} c the component being registerd.
16740                          * 
16741                          */
16742                         'register' : true,
16743             /**
16744                          * @event beforebuild
16745                          * Fires before each Component is built
16746                          * can be used to apply permissions.
16747                          * @param {Roo.XComponent} c the component being registerd.
16748                          * 
16749                          */
16750                         'beforebuild' : true,
16751                         /**
16752                          * @event buildcomplete
16753                          * Fires on the top level element when all elements have been built
16754                          * @param {Roo.XComponent} the top level component.
16755                          */
16756                         'buildcomplete' : true
16757                         
16758                 }
16759 });
16760
16761 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16762  //
16763  /**
16764  * marked - a markdown parser
16765  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
16766  * https://github.com/chjj/marked
16767  */
16768
16769
16770 /**
16771  *
16772  * Roo.Markdown - is a very crude wrapper around marked..
16773  *
16774  * usage:
16775  * 
16776  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
16777  * 
16778  * Note: move the sample code to the bottom of this
16779  * file before uncommenting it.
16780  *
16781  */
16782
16783 Roo.Markdown = {};
16784 Roo.Markdown.toHtml = function(text) {
16785     
16786     var c = new Roo.Markdown.marked.setOptions({
16787             renderer: new Roo.Markdown.marked.Renderer(),
16788             gfm: true,
16789             tables: true,
16790             breaks: false,
16791             pedantic: false,
16792             sanitize: false,
16793             smartLists: true,
16794             smartypants: false
16795           });
16796     // A FEW HACKS!!?
16797     
16798     text = text.replace(/\\\n/g,' ');
16799     return Roo.Markdown.marked(text);
16800 };
16801 //
16802 // converter
16803 //
16804 // Wraps all "globals" so that the only thing
16805 // exposed is makeHtml().
16806 //
16807 (function() {
16808     
16809      /**
16810          * eval:var:escape
16811          * eval:var:unescape
16812          * eval:var:replace
16813          */
16814       
16815     /**
16816      * Helpers
16817      */
16818     
16819     var escape = function (html, encode) {
16820       return html
16821         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
16822         .replace(/</g, '&lt;')
16823         .replace(/>/g, '&gt;')
16824         .replace(/"/g, '&quot;')
16825         .replace(/'/g, '&#39;');
16826     }
16827     
16828     var unescape = function (html) {
16829         // explicitly match decimal, hex, and named HTML entities 
16830       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
16831         n = n.toLowerCase();
16832         if (n === 'colon') { return ':'; }
16833         if (n.charAt(0) === '#') {
16834           return n.charAt(1) === 'x'
16835             ? String.fromCharCode(parseInt(n.substring(2), 16))
16836             : String.fromCharCode(+n.substring(1));
16837         }
16838         return '';
16839       });
16840     }
16841     
16842     var replace = function (regex, opt) {
16843       regex = regex.source;
16844       opt = opt || '';
16845       return function self(name, val) {
16846         if (!name) { return new RegExp(regex, opt); }
16847         val = val.source || val;
16848         val = val.replace(/(^|[^\[])\^/g, '$1');
16849         regex = regex.replace(name, val);
16850         return self;
16851       };
16852     }
16853
16854
16855          /**
16856          * eval:var:noop
16857     */
16858     var noop = function () {}
16859     noop.exec = noop;
16860     
16861          /**
16862          * eval:var:merge
16863     */
16864     var merge = function (obj) {
16865       var i = 1
16866         , target
16867         , key;
16868     
16869       for (; i < arguments.length; i++) {
16870         target = arguments[i];
16871         for (key in target) {
16872           if (Object.prototype.hasOwnProperty.call(target, key)) {
16873             obj[key] = target[key];
16874           }
16875         }
16876       }
16877     
16878       return obj;
16879     }
16880     
16881     
16882     /**
16883      * Block-Level Grammar
16884      */
16885     
16886     
16887     
16888     
16889     var block = {
16890       newline: /^\n+/,
16891       code: /^( {4}[^\n]+\n*)+/,
16892       fences: noop,
16893       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
16894       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
16895       nptable: noop,
16896       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
16897       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
16898       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
16899       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
16900       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
16901       table: noop,
16902       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
16903       text: /^[^\n]+/
16904     };
16905     
16906     block.bullet = /(?:[*+-]|\d+\.)/;
16907     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
16908     block.item = replace(block.item, 'gm')
16909       (/bull/g, block.bullet)
16910       ();
16911     
16912     block.list = replace(block.list)
16913       (/bull/g, block.bullet)
16914       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
16915       ('def', '\\n+(?=' + block.def.source + ')')
16916       ();
16917     
16918     block.blockquote = replace(block.blockquote)
16919       ('def', block.def)
16920       ();
16921     
16922     block._tag = '(?!(?:'
16923       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
16924       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
16925       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
16926     
16927     block.html = replace(block.html)
16928       ('comment', /<!--[\s\S]*?-->/)
16929       ('closed', /<(tag)[\s\S]+?<\/\1>/)
16930       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
16931       (/tag/g, block._tag)
16932       ();
16933     
16934     block.paragraph = replace(block.paragraph)
16935       ('hr', block.hr)
16936       ('heading', block.heading)
16937       ('lheading', block.lheading)
16938       ('blockquote', block.blockquote)
16939       ('tag', '<' + block._tag)
16940       ('def', block.def)
16941       ();
16942     
16943     /**
16944      * Normal Block Grammar
16945      */
16946     
16947     block.normal = merge({}, block);
16948     
16949     /**
16950      * GFM Block Grammar
16951      */
16952     
16953     block.gfm = merge({}, block.normal, {
16954       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
16955       paragraph: /^/,
16956       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
16957     });
16958     
16959     block.gfm.paragraph = replace(block.paragraph)
16960       ('(?!', '(?!'
16961         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
16962         + block.list.source.replace('\\1', '\\3') + '|')
16963       ();
16964     
16965     /**
16966      * GFM + Tables Block Grammar
16967      */
16968     
16969     block.tables = merge({}, block.gfm, {
16970       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
16971       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
16972     });
16973     
16974     /**
16975      * Block Lexer
16976      */
16977     
16978     var Lexer = function (options) {
16979       this.tokens = [];
16980       this.tokens.links = {};
16981       this.options = options || marked.defaults;
16982       this.rules = block.normal;
16983     
16984       if (this.options.gfm) {
16985         if (this.options.tables) {
16986           this.rules = block.tables;
16987         } else {
16988           this.rules = block.gfm;
16989         }
16990       }
16991     }
16992     
16993     /**
16994      * Expose Block Rules
16995      */
16996     
16997     Lexer.rules = block;
16998     
16999     /**
17000      * Static Lex Method
17001      */
17002     
17003     Lexer.lex = function(src, options) {
17004       var lexer = new Lexer(options);
17005       return lexer.lex(src);
17006     };
17007     
17008     /**
17009      * Preprocessing
17010      */
17011     
17012     Lexer.prototype.lex = function(src) {
17013       src = src
17014         .replace(/\r\n|\r/g, '\n')
17015         .replace(/\t/g, '    ')
17016         .replace(/\u00a0/g, ' ')
17017         .replace(/\u2424/g, '\n');
17018     
17019       return this.token(src, true);
17020     };
17021     
17022     /**
17023      * Lexing
17024      */
17025     
17026     Lexer.prototype.token = function(src, top, bq) {
17027       var src = src.replace(/^ +$/gm, '')
17028         , next
17029         , loose
17030         , cap
17031         , bull
17032         , b
17033         , item
17034         , space
17035         , i
17036         , l;
17037     
17038       while (src) {
17039         // newline
17040         if (cap = this.rules.newline.exec(src)) {
17041           src = src.substring(cap[0].length);
17042           if (cap[0].length > 1) {
17043             this.tokens.push({
17044               type: 'space'
17045             });
17046           }
17047         }
17048     
17049         // code
17050         if (cap = this.rules.code.exec(src)) {
17051           src = src.substring(cap[0].length);
17052           cap = cap[0].replace(/^ {4}/gm, '');
17053           this.tokens.push({
17054             type: 'code',
17055             text: !this.options.pedantic
17056               ? cap.replace(/\n+$/, '')
17057               : cap
17058           });
17059           continue;
17060         }
17061     
17062         // fences (gfm)
17063         if (cap = this.rules.fences.exec(src)) {
17064           src = src.substring(cap[0].length);
17065           this.tokens.push({
17066             type: 'code',
17067             lang: cap[2],
17068             text: cap[3] || ''
17069           });
17070           continue;
17071         }
17072     
17073         // heading
17074         if (cap = this.rules.heading.exec(src)) {
17075           src = src.substring(cap[0].length);
17076           this.tokens.push({
17077             type: 'heading',
17078             depth: cap[1].length,
17079             text: cap[2]
17080           });
17081           continue;
17082         }
17083     
17084         // table no leading pipe (gfm)
17085         if (top && (cap = this.rules.nptable.exec(src))) {
17086           src = src.substring(cap[0].length);
17087     
17088           item = {
17089             type: 'table',
17090             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17091             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17092             cells: cap[3].replace(/\n$/, '').split('\n')
17093           };
17094     
17095           for (i = 0; i < item.align.length; i++) {
17096             if (/^ *-+: *$/.test(item.align[i])) {
17097               item.align[i] = 'right';
17098             } else if (/^ *:-+: *$/.test(item.align[i])) {
17099               item.align[i] = 'center';
17100             } else if (/^ *:-+ *$/.test(item.align[i])) {
17101               item.align[i] = 'left';
17102             } else {
17103               item.align[i] = null;
17104             }
17105           }
17106     
17107           for (i = 0; i < item.cells.length; i++) {
17108             item.cells[i] = item.cells[i].split(/ *\| */);
17109           }
17110     
17111           this.tokens.push(item);
17112     
17113           continue;
17114         }
17115     
17116         // lheading
17117         if (cap = this.rules.lheading.exec(src)) {
17118           src = src.substring(cap[0].length);
17119           this.tokens.push({
17120             type: 'heading',
17121             depth: cap[2] === '=' ? 1 : 2,
17122             text: cap[1]
17123           });
17124           continue;
17125         }
17126     
17127         // hr
17128         if (cap = this.rules.hr.exec(src)) {
17129           src = src.substring(cap[0].length);
17130           this.tokens.push({
17131             type: 'hr'
17132           });
17133           continue;
17134         }
17135     
17136         // blockquote
17137         if (cap = this.rules.blockquote.exec(src)) {
17138           src = src.substring(cap[0].length);
17139     
17140           this.tokens.push({
17141             type: 'blockquote_start'
17142           });
17143     
17144           cap = cap[0].replace(/^ *> ?/gm, '');
17145     
17146           // Pass `top` to keep the current
17147           // "toplevel" state. This is exactly
17148           // how markdown.pl works.
17149           this.token(cap, top, true);
17150     
17151           this.tokens.push({
17152             type: 'blockquote_end'
17153           });
17154     
17155           continue;
17156         }
17157     
17158         // list
17159         if (cap = this.rules.list.exec(src)) {
17160           src = src.substring(cap[0].length);
17161           bull = cap[2];
17162     
17163           this.tokens.push({
17164             type: 'list_start',
17165             ordered: bull.length > 1
17166           });
17167     
17168           // Get each top-level item.
17169           cap = cap[0].match(this.rules.item);
17170     
17171           next = false;
17172           l = cap.length;
17173           i = 0;
17174     
17175           for (; i < l; i++) {
17176             item = cap[i];
17177     
17178             // Remove the list item's bullet
17179             // so it is seen as the next token.
17180             space = item.length;
17181             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
17182     
17183             // Outdent whatever the
17184             // list item contains. Hacky.
17185             if (~item.indexOf('\n ')) {
17186               space -= item.length;
17187               item = !this.options.pedantic
17188                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
17189                 : item.replace(/^ {1,4}/gm, '');
17190             }
17191     
17192             // Determine whether the next list item belongs here.
17193             // Backpedal if it does not belong in this list.
17194             if (this.options.smartLists && i !== l - 1) {
17195               b = block.bullet.exec(cap[i + 1])[0];
17196               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
17197                 src = cap.slice(i + 1).join('\n') + src;
17198                 i = l - 1;
17199               }
17200             }
17201     
17202             // Determine whether item is loose or not.
17203             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
17204             // for discount behavior.
17205             loose = next || /\n\n(?!\s*$)/.test(item);
17206             if (i !== l - 1) {
17207               next = item.charAt(item.length - 1) === '\n';
17208               if (!loose) { loose = next; }
17209             }
17210     
17211             this.tokens.push({
17212               type: loose
17213                 ? 'loose_item_start'
17214                 : 'list_item_start'
17215             });
17216     
17217             // Recurse.
17218             this.token(item, false, bq);
17219     
17220             this.tokens.push({
17221               type: 'list_item_end'
17222             });
17223           }
17224     
17225           this.tokens.push({
17226             type: 'list_end'
17227           });
17228     
17229           continue;
17230         }
17231     
17232         // html
17233         if (cap = this.rules.html.exec(src)) {
17234           src = src.substring(cap[0].length);
17235           this.tokens.push({
17236             type: this.options.sanitize
17237               ? 'paragraph'
17238               : 'html',
17239             pre: !this.options.sanitizer
17240               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
17241             text: cap[0]
17242           });
17243           continue;
17244         }
17245     
17246         // def
17247         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
17248           src = src.substring(cap[0].length);
17249           this.tokens.links[cap[1].toLowerCase()] = {
17250             href: cap[2],
17251             title: cap[3]
17252           };
17253           continue;
17254         }
17255     
17256         // table (gfm)
17257         if (top && (cap = this.rules.table.exec(src))) {
17258           src = src.substring(cap[0].length);
17259     
17260           item = {
17261             type: 'table',
17262             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17263             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17264             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
17265           };
17266     
17267           for (i = 0; i < item.align.length; i++) {
17268             if (/^ *-+: *$/.test(item.align[i])) {
17269               item.align[i] = 'right';
17270             } else if (/^ *:-+: *$/.test(item.align[i])) {
17271               item.align[i] = 'center';
17272             } else if (/^ *:-+ *$/.test(item.align[i])) {
17273               item.align[i] = 'left';
17274             } else {
17275               item.align[i] = null;
17276             }
17277           }
17278     
17279           for (i = 0; i < item.cells.length; i++) {
17280             item.cells[i] = item.cells[i]
17281               .replace(/^ *\| *| *\| *$/g, '')
17282               .split(/ *\| */);
17283           }
17284     
17285           this.tokens.push(item);
17286     
17287           continue;
17288         }
17289     
17290         // top-level paragraph
17291         if (top && (cap = this.rules.paragraph.exec(src))) {
17292           src = src.substring(cap[0].length);
17293           this.tokens.push({
17294             type: 'paragraph',
17295             text: cap[1].charAt(cap[1].length - 1) === '\n'
17296               ? cap[1].slice(0, -1)
17297               : cap[1]
17298           });
17299           continue;
17300         }
17301     
17302         // text
17303         if (cap = this.rules.text.exec(src)) {
17304           // Top-level should never reach here.
17305           src = src.substring(cap[0].length);
17306           this.tokens.push({
17307             type: 'text',
17308             text: cap[0]
17309           });
17310           continue;
17311         }
17312     
17313         if (src) {
17314           throw new
17315             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17316         }
17317       }
17318     
17319       return this.tokens;
17320     };
17321     
17322     /**
17323      * Inline-Level Grammar
17324      */
17325     
17326     var inline = {
17327       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
17328       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
17329       url: noop,
17330       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
17331       link: /^!?\[(inside)\]\(href\)/,
17332       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
17333       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
17334       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
17335       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
17336       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
17337       br: /^ {2,}\n(?!\s*$)/,
17338       del: noop,
17339       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
17340     };
17341     
17342     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
17343     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
17344     
17345     inline.link = replace(inline.link)
17346       ('inside', inline._inside)
17347       ('href', inline._href)
17348       ();
17349     
17350     inline.reflink = replace(inline.reflink)
17351       ('inside', inline._inside)
17352       ();
17353     
17354     /**
17355      * Normal Inline Grammar
17356      */
17357     
17358     inline.normal = merge({}, inline);
17359     
17360     /**
17361      * Pedantic Inline Grammar
17362      */
17363     
17364     inline.pedantic = merge({}, inline.normal, {
17365       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
17366       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
17367     });
17368     
17369     /**
17370      * GFM Inline Grammar
17371      */
17372     
17373     inline.gfm = merge({}, inline.normal, {
17374       escape: replace(inline.escape)('])', '~|])')(),
17375       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
17376       del: /^~~(?=\S)([\s\S]*?\S)~~/,
17377       text: replace(inline.text)
17378         (']|', '~]|')
17379         ('|', '|https?://|')
17380         ()
17381     });
17382     
17383     /**
17384      * GFM + Line Breaks Inline Grammar
17385      */
17386     
17387     inline.breaks = merge({}, inline.gfm, {
17388       br: replace(inline.br)('{2,}', '*')(),
17389       text: replace(inline.gfm.text)('{2,}', '*')()
17390     });
17391     
17392     /**
17393      * Inline Lexer & Compiler
17394      */
17395     
17396     var InlineLexer  = function (links, options) {
17397       this.options = options || marked.defaults;
17398       this.links = links;
17399       this.rules = inline.normal;
17400       this.renderer = this.options.renderer || new Renderer;
17401       this.renderer.options = this.options;
17402     
17403       if (!this.links) {
17404         throw new
17405           Error('Tokens array requires a `links` property.');
17406       }
17407     
17408       if (this.options.gfm) {
17409         if (this.options.breaks) {
17410           this.rules = inline.breaks;
17411         } else {
17412           this.rules = inline.gfm;
17413         }
17414       } else if (this.options.pedantic) {
17415         this.rules = inline.pedantic;
17416       }
17417     }
17418     
17419     /**
17420      * Expose Inline Rules
17421      */
17422     
17423     InlineLexer.rules = inline;
17424     
17425     /**
17426      * Static Lexing/Compiling Method
17427      */
17428     
17429     InlineLexer.output = function(src, links, options) {
17430       var inline = new InlineLexer(links, options);
17431       return inline.output(src);
17432     };
17433     
17434     /**
17435      * Lexing/Compiling
17436      */
17437     
17438     InlineLexer.prototype.output = function(src) {
17439       var out = ''
17440         , link
17441         , text
17442         , href
17443         , cap;
17444     
17445       while (src) {
17446         // escape
17447         if (cap = this.rules.escape.exec(src)) {
17448           src = src.substring(cap[0].length);
17449           out += cap[1];
17450           continue;
17451         }
17452     
17453         // autolink
17454         if (cap = this.rules.autolink.exec(src)) {
17455           src = src.substring(cap[0].length);
17456           if (cap[2] === '@') {
17457             text = cap[1].charAt(6) === ':'
17458               ? this.mangle(cap[1].substring(7))
17459               : this.mangle(cap[1]);
17460             href = this.mangle('mailto:') + text;
17461           } else {
17462             text = escape(cap[1]);
17463             href = text;
17464           }
17465           out += this.renderer.link(href, null, text);
17466           continue;
17467         }
17468     
17469         // url (gfm)
17470         if (!this.inLink && (cap = this.rules.url.exec(src))) {
17471           src = src.substring(cap[0].length);
17472           text = escape(cap[1]);
17473           href = text;
17474           out += this.renderer.link(href, null, text);
17475           continue;
17476         }
17477     
17478         // tag
17479         if (cap = this.rules.tag.exec(src)) {
17480           if (!this.inLink && /^<a /i.test(cap[0])) {
17481             this.inLink = true;
17482           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
17483             this.inLink = false;
17484           }
17485           src = src.substring(cap[0].length);
17486           out += this.options.sanitize
17487             ? this.options.sanitizer
17488               ? this.options.sanitizer(cap[0])
17489               : escape(cap[0])
17490             : cap[0];
17491           continue;
17492         }
17493     
17494         // link
17495         if (cap = this.rules.link.exec(src)) {
17496           src = src.substring(cap[0].length);
17497           this.inLink = true;
17498           out += this.outputLink(cap, {
17499             href: cap[2],
17500             title: cap[3]
17501           });
17502           this.inLink = false;
17503           continue;
17504         }
17505     
17506         // reflink, nolink
17507         if ((cap = this.rules.reflink.exec(src))
17508             || (cap = this.rules.nolink.exec(src))) {
17509           src = src.substring(cap[0].length);
17510           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
17511           link = this.links[link.toLowerCase()];
17512           if (!link || !link.href) {
17513             out += cap[0].charAt(0);
17514             src = cap[0].substring(1) + src;
17515             continue;
17516           }
17517           this.inLink = true;
17518           out += this.outputLink(cap, link);
17519           this.inLink = false;
17520           continue;
17521         }
17522     
17523         // strong
17524         if (cap = this.rules.strong.exec(src)) {
17525           src = src.substring(cap[0].length);
17526           out += this.renderer.strong(this.output(cap[2] || cap[1]));
17527           continue;
17528         }
17529     
17530         // em
17531         if (cap = this.rules.em.exec(src)) {
17532           src = src.substring(cap[0].length);
17533           out += this.renderer.em(this.output(cap[2] || cap[1]));
17534           continue;
17535         }
17536     
17537         // code
17538         if (cap = this.rules.code.exec(src)) {
17539           src = src.substring(cap[0].length);
17540           out += this.renderer.codespan(escape(cap[2], true));
17541           continue;
17542         }
17543     
17544         // br
17545         if (cap = this.rules.br.exec(src)) {
17546           src = src.substring(cap[0].length);
17547           out += this.renderer.br();
17548           continue;
17549         }
17550     
17551         // del (gfm)
17552         if (cap = this.rules.del.exec(src)) {
17553           src = src.substring(cap[0].length);
17554           out += this.renderer.del(this.output(cap[1]));
17555           continue;
17556         }
17557     
17558         // text
17559         if (cap = this.rules.text.exec(src)) {
17560           src = src.substring(cap[0].length);
17561           out += this.renderer.text(escape(this.smartypants(cap[0])));
17562           continue;
17563         }
17564     
17565         if (src) {
17566           throw new
17567             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17568         }
17569       }
17570     
17571       return out;
17572     };
17573     
17574     /**
17575      * Compile Link
17576      */
17577     
17578     InlineLexer.prototype.outputLink = function(cap, link) {
17579       var href = escape(link.href)
17580         , title = link.title ? escape(link.title) : null;
17581     
17582       return cap[0].charAt(0) !== '!'
17583         ? this.renderer.link(href, title, this.output(cap[1]))
17584         : this.renderer.image(href, title, escape(cap[1]));
17585     };
17586     
17587     /**
17588      * Smartypants Transformations
17589      */
17590     
17591     InlineLexer.prototype.smartypants = function(text) {
17592       if (!this.options.smartypants)  { return text; }
17593       return text
17594         // em-dashes
17595         .replace(/---/g, '\u2014')
17596         // en-dashes
17597         .replace(/--/g, '\u2013')
17598         // opening singles
17599         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
17600         // closing singles & apostrophes
17601         .replace(/'/g, '\u2019')
17602         // opening doubles
17603         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
17604         // closing doubles
17605         .replace(/"/g, '\u201d')
17606         // ellipses
17607         .replace(/\.{3}/g, '\u2026');
17608     };
17609     
17610     /**
17611      * Mangle Links
17612      */
17613     
17614     InlineLexer.prototype.mangle = function(text) {
17615       if (!this.options.mangle) { return text; }
17616       var out = ''
17617         , l = text.length
17618         , i = 0
17619         , ch;
17620     
17621       for (; i < l; i++) {
17622         ch = text.charCodeAt(i);
17623         if (Math.random() > 0.5) {
17624           ch = 'x' + ch.toString(16);
17625         }
17626         out += '&#' + ch + ';';
17627       }
17628     
17629       return out;
17630     };
17631     
17632     /**
17633      * Renderer
17634      */
17635     
17636      /**
17637          * eval:var:Renderer
17638     */
17639     
17640     var Renderer   = function (options) {
17641       this.options = options || {};
17642     }
17643     
17644     Renderer.prototype.code = function(code, lang, escaped) {
17645       if (this.options.highlight) {
17646         var out = this.options.highlight(code, lang);
17647         if (out != null && out !== code) {
17648           escaped = true;
17649           code = out;
17650         }
17651       } else {
17652             // hack!!! - it's already escapeD?
17653             escaped = true;
17654       }
17655     
17656       if (!lang) {
17657         return '<pre><code>'
17658           + (escaped ? code : escape(code, true))
17659           + '\n</code></pre>';
17660       }
17661     
17662       return '<pre><code class="'
17663         + this.options.langPrefix
17664         + escape(lang, true)
17665         + '">'
17666         + (escaped ? code : escape(code, true))
17667         + '\n</code></pre>\n';
17668     };
17669     
17670     Renderer.prototype.blockquote = function(quote) {
17671       return '<blockquote>\n' + quote + '</blockquote>\n';
17672     };
17673     
17674     Renderer.prototype.html = function(html) {
17675       return html;
17676     };
17677     
17678     Renderer.prototype.heading = function(text, level, raw) {
17679       return '<h'
17680         + level
17681         + ' id="'
17682         + this.options.headerPrefix
17683         + raw.toLowerCase().replace(/[^\w]+/g, '-')
17684         + '">'
17685         + text
17686         + '</h'
17687         + level
17688         + '>\n';
17689     };
17690     
17691     Renderer.prototype.hr = function() {
17692       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
17693     };
17694     
17695     Renderer.prototype.list = function(body, ordered) {
17696       var type = ordered ? 'ol' : 'ul';
17697       return '<' + type + '>\n' + body + '</' + type + '>\n';
17698     };
17699     
17700     Renderer.prototype.listitem = function(text) {
17701       return '<li>' + text + '</li>\n';
17702     };
17703     
17704     Renderer.prototype.paragraph = function(text) {
17705       return '<p>' + text + '</p>\n';
17706     };
17707     
17708     Renderer.prototype.table = function(header, body) {
17709       return '<table class="table table-striped">\n'
17710         + '<thead>\n'
17711         + header
17712         + '</thead>\n'
17713         + '<tbody>\n'
17714         + body
17715         + '</tbody>\n'
17716         + '</table>\n';
17717     };
17718     
17719     Renderer.prototype.tablerow = function(content) {
17720       return '<tr>\n' + content + '</tr>\n';
17721     };
17722     
17723     Renderer.prototype.tablecell = function(content, flags) {
17724       var type = flags.header ? 'th' : 'td';
17725       var tag = flags.align
17726         ? '<' + type + ' style="text-align:' + flags.align + '">'
17727         : '<' + type + '>';
17728       return tag + content + '</' + type + '>\n';
17729     };
17730     
17731     // span level renderer
17732     Renderer.prototype.strong = function(text) {
17733       return '<strong>' + text + '</strong>';
17734     };
17735     
17736     Renderer.prototype.em = function(text) {
17737       return '<em>' + text + '</em>';
17738     };
17739     
17740     Renderer.prototype.codespan = function(text) {
17741       return '<code>' + text + '</code>';
17742     };
17743     
17744     Renderer.prototype.br = function() {
17745       return this.options.xhtml ? '<br/>' : '<br>';
17746     };
17747     
17748     Renderer.prototype.del = function(text) {
17749       return '<del>' + text + '</del>';
17750     };
17751     
17752     Renderer.prototype.link = function(href, title, text) {
17753       if (this.options.sanitize) {
17754         try {
17755           var prot = decodeURIComponent(unescape(href))
17756             .replace(/[^\w:]/g, '')
17757             .toLowerCase();
17758         } catch (e) {
17759           return '';
17760         }
17761         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
17762           return '';
17763         }
17764       }
17765       var out = '<a href="' + href + '"';
17766       if (title) {
17767         out += ' title="' + title + '"';
17768       }
17769       out += '>' + text + '</a>';
17770       return out;
17771     };
17772     
17773     Renderer.prototype.image = function(href, title, text) {
17774       var out = '<img src="' + href + '" alt="' + text + '"';
17775       if (title) {
17776         out += ' title="' + title + '"';
17777       }
17778       out += this.options.xhtml ? '/>' : '>';
17779       return out;
17780     };
17781     
17782     Renderer.prototype.text = function(text) {
17783       return text;
17784     };
17785     
17786     /**
17787      * Parsing & Compiling
17788      */
17789          /**
17790          * eval:var:Parser
17791     */
17792     
17793     var Parser= function (options) {
17794       this.tokens = [];
17795       this.token = null;
17796       this.options = options || marked.defaults;
17797       this.options.renderer = this.options.renderer || new Renderer;
17798       this.renderer = this.options.renderer;
17799       this.renderer.options = this.options;
17800     }
17801     
17802     /**
17803      * Static Parse Method
17804      */
17805     
17806     Parser.parse = function(src, options, renderer) {
17807       var parser = new Parser(options, renderer);
17808       return parser.parse(src);
17809     };
17810     
17811     /**
17812      * Parse Loop
17813      */
17814     
17815     Parser.prototype.parse = function(src) {
17816       this.inline = new InlineLexer(src.links, this.options, this.renderer);
17817       this.tokens = src.reverse();
17818     
17819       var out = '';
17820       while (this.next()) {
17821         out += this.tok();
17822       }
17823     
17824       return out;
17825     };
17826     
17827     /**
17828      * Next Token
17829      */
17830     
17831     Parser.prototype.next = function() {
17832       return this.token = this.tokens.pop();
17833     };
17834     
17835     /**
17836      * Preview Next Token
17837      */
17838     
17839     Parser.prototype.peek = function() {
17840       return this.tokens[this.tokens.length - 1] || 0;
17841     };
17842     
17843     /**
17844      * Parse Text Tokens
17845      */
17846     
17847     Parser.prototype.parseText = function() {
17848       var body = this.token.text;
17849     
17850       while (this.peek().type === 'text') {
17851         body += '\n' + this.next().text;
17852       }
17853     
17854       return this.inline.output(body);
17855     };
17856     
17857     /**
17858      * Parse Current Token
17859      */
17860     
17861     Parser.prototype.tok = function() {
17862       switch (this.token.type) {
17863         case 'space': {
17864           return '';
17865         }
17866         case 'hr': {
17867           return this.renderer.hr();
17868         }
17869         case 'heading': {
17870           return this.renderer.heading(
17871             this.inline.output(this.token.text),
17872             this.token.depth,
17873             this.token.text);
17874         }
17875         case 'code': {
17876           return this.renderer.code(this.token.text,
17877             this.token.lang,
17878             this.token.escaped);
17879         }
17880         case 'table': {
17881           var header = ''
17882             , body = ''
17883             , i
17884             , row
17885             , cell
17886             , flags
17887             , j;
17888     
17889           // header
17890           cell = '';
17891           for (i = 0; i < this.token.header.length; i++) {
17892             flags = { header: true, align: this.token.align[i] };
17893             cell += this.renderer.tablecell(
17894               this.inline.output(this.token.header[i]),
17895               { header: true, align: this.token.align[i] }
17896             );
17897           }
17898           header += this.renderer.tablerow(cell);
17899     
17900           for (i = 0; i < this.token.cells.length; i++) {
17901             row = this.token.cells[i];
17902     
17903             cell = '';
17904             for (j = 0; j < row.length; j++) {
17905               cell += this.renderer.tablecell(
17906                 this.inline.output(row[j]),
17907                 { header: false, align: this.token.align[j] }
17908               );
17909             }
17910     
17911             body += this.renderer.tablerow(cell);
17912           }
17913           return this.renderer.table(header, body);
17914         }
17915         case 'blockquote_start': {
17916           var body = '';
17917     
17918           while (this.next().type !== 'blockquote_end') {
17919             body += this.tok();
17920           }
17921     
17922           return this.renderer.blockquote(body);
17923         }
17924         case 'list_start': {
17925           var body = ''
17926             , ordered = this.token.ordered;
17927     
17928           while (this.next().type !== 'list_end') {
17929             body += this.tok();
17930           }
17931     
17932           return this.renderer.list(body, ordered);
17933         }
17934         case 'list_item_start': {
17935           var body = '';
17936     
17937           while (this.next().type !== 'list_item_end') {
17938             body += this.token.type === 'text'
17939               ? this.parseText()
17940               : this.tok();
17941           }
17942     
17943           return this.renderer.listitem(body);
17944         }
17945         case 'loose_item_start': {
17946           var body = '';
17947     
17948           while (this.next().type !== 'list_item_end') {
17949             body += this.tok();
17950           }
17951     
17952           return this.renderer.listitem(body);
17953         }
17954         case 'html': {
17955           var html = !this.token.pre && !this.options.pedantic
17956             ? this.inline.output(this.token.text)
17957             : this.token.text;
17958           return this.renderer.html(html);
17959         }
17960         case 'paragraph': {
17961           return this.renderer.paragraph(this.inline.output(this.token.text));
17962         }
17963         case 'text': {
17964           return this.renderer.paragraph(this.parseText());
17965         }
17966       }
17967     };
17968   
17969     
17970     /**
17971      * Marked
17972      */
17973          /**
17974          * eval:var:marked
17975     */
17976     var marked = function (src, opt, callback) {
17977       if (callback || typeof opt === 'function') {
17978         if (!callback) {
17979           callback = opt;
17980           opt = null;
17981         }
17982     
17983         opt = merge({}, marked.defaults, opt || {});
17984     
17985         var highlight = opt.highlight
17986           , tokens
17987           , pending
17988           , i = 0;
17989     
17990         try {
17991           tokens = Lexer.lex(src, opt)
17992         } catch (e) {
17993           return callback(e);
17994         }
17995     
17996         pending = tokens.length;
17997          /**
17998          * eval:var:done
17999     */
18000         var done = function(err) {
18001           if (err) {
18002             opt.highlight = highlight;
18003             return callback(err);
18004           }
18005     
18006           var out;
18007     
18008           try {
18009             out = Parser.parse(tokens, opt);
18010           } catch (e) {
18011             err = e;
18012           }
18013     
18014           opt.highlight = highlight;
18015     
18016           return err
18017             ? callback(err)
18018             : callback(null, out);
18019         };
18020     
18021         if (!highlight || highlight.length < 3) {
18022           return done();
18023         }
18024     
18025         delete opt.highlight;
18026     
18027         if (!pending) { return done(); }
18028     
18029         for (; i < tokens.length; i++) {
18030           (function(token) {
18031             if (token.type !== 'code') {
18032               return --pending || done();
18033             }
18034             return highlight(token.text, token.lang, function(err, code) {
18035               if (err) { return done(err); }
18036               if (code == null || code === token.text) {
18037                 return --pending || done();
18038               }
18039               token.text = code;
18040               token.escaped = true;
18041               --pending || done();
18042             });
18043           })(tokens[i]);
18044         }
18045     
18046         return;
18047       }
18048       try {
18049         if (opt) { opt = merge({}, marked.defaults, opt); }
18050         return Parser.parse(Lexer.lex(src, opt), opt);
18051       } catch (e) {
18052         e.message += '\nPlease report this to https://github.com/chjj/marked.';
18053         if ((opt || marked.defaults).silent) {
18054           return '<p>An error occured:</p><pre>'
18055             + escape(e.message + '', true)
18056             + '</pre>';
18057         }
18058         throw e;
18059       }
18060     }
18061     
18062     /**
18063      * Options
18064      */
18065     
18066     marked.options =
18067     marked.setOptions = function(opt) {
18068       merge(marked.defaults, opt);
18069       return marked;
18070     };
18071     
18072     marked.defaults = {
18073       gfm: true,
18074       tables: true,
18075       breaks: false,
18076       pedantic: false,
18077       sanitize: false,
18078       sanitizer: null,
18079       mangle: true,
18080       smartLists: false,
18081       silent: false,
18082       highlight: null,
18083       langPrefix: 'lang-',
18084       smartypants: false,
18085       headerPrefix: '',
18086       renderer: new Renderer,
18087       xhtml: false
18088     };
18089     
18090     /**
18091      * Expose
18092      */
18093     
18094     marked.Parser = Parser;
18095     marked.parser = Parser.parse;
18096     
18097     marked.Renderer = Renderer;
18098     
18099     marked.Lexer = Lexer;
18100     marked.lexer = Lexer.lex;
18101     
18102     marked.InlineLexer = InlineLexer;
18103     marked.inlineLexer = InlineLexer.output;
18104     
18105     marked.parse = marked;
18106     
18107     Roo.Markdown.marked = marked;
18108
18109 })();/*
18110  * Based on:
18111  * Ext JS Library 1.1.1
18112  * Copyright(c) 2006-2007, Ext JS, LLC.
18113  *
18114  * Originally Released Under LGPL - original licence link has changed is not relivant.
18115  *
18116  * Fork - LGPL
18117  * <script type="text/javascript">
18118  */
18119
18120
18121
18122 /*
18123  * These classes are derivatives of the similarly named classes in the YUI Library.
18124  * The original license:
18125  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18126  * Code licensed under the BSD License:
18127  * http://developer.yahoo.net/yui/license.txt
18128  */
18129
18130 (function() {
18131
18132 var Event=Roo.EventManager;
18133 var Dom=Roo.lib.Dom;
18134
18135 /**
18136  * @class Roo.dd.DragDrop
18137  * @extends Roo.util.Observable
18138  * Defines the interface and base operation of items that that can be
18139  * dragged or can be drop targets.  It was designed to be extended, overriding
18140  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
18141  * Up to three html elements can be associated with a DragDrop instance:
18142  * <ul>
18143  * <li>linked element: the element that is passed into the constructor.
18144  * This is the element which defines the boundaries for interaction with
18145  * other DragDrop objects.</li>
18146  * <li>handle element(s): The drag operation only occurs if the element that
18147  * was clicked matches a handle element.  By default this is the linked
18148  * element, but there are times that you will want only a portion of the
18149  * linked element to initiate the drag operation, and the setHandleElId()
18150  * method provides a way to define this.</li>
18151  * <li>drag element: this represents the element that would be moved along
18152  * with the cursor during a drag operation.  By default, this is the linked
18153  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
18154  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
18155  * </li>
18156  * </ul>
18157  * This class should not be instantiated until the onload event to ensure that
18158  * the associated elements are available.
18159  * The following would define a DragDrop obj that would interact with any
18160  * other DragDrop obj in the "group1" group:
18161  * <pre>
18162  *  dd = new Roo.dd.DragDrop("div1", "group1");
18163  * </pre>
18164  * Since none of the event handlers have been implemented, nothing would
18165  * actually happen if you were to run the code above.  Normally you would
18166  * override this class or one of the default implementations, but you can
18167  * also override the methods you want on an instance of the class...
18168  * <pre>
18169  *  dd.onDragDrop = function(e, id) {
18170  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
18171  *  }
18172  * </pre>
18173  * @constructor
18174  * @param {String} id of the element that is linked to this instance
18175  * @param {String} sGroup the group of related DragDrop objects
18176  * @param {object} config an object containing configurable attributes
18177  *                Valid properties for DragDrop:
18178  *                    padding, isTarget, maintainOffset, primaryButtonOnly
18179  */
18180 Roo.dd.DragDrop = function(id, sGroup, config) {
18181     if (id) {
18182         this.init(id, sGroup, config);
18183     }
18184     
18185 };
18186
18187 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
18188
18189     /**
18190      * The id of the element associated with this object.  This is what we
18191      * refer to as the "linked element" because the size and position of
18192      * this element is used to determine when the drag and drop objects have
18193      * interacted.
18194      * @property id
18195      * @type String
18196      */
18197     id: null,
18198
18199     /**
18200      * Configuration attributes passed into the constructor
18201      * @property config
18202      * @type object
18203      */
18204     config: null,
18205
18206     /**
18207      * The id of the element that will be dragged.  By default this is same
18208      * as the linked element , but could be changed to another element. Ex:
18209      * Roo.dd.DDProxy
18210      * @property dragElId
18211      * @type String
18212      * @private
18213      */
18214     dragElId: null,
18215
18216     /**
18217      * the id of the element that initiates the drag operation.  By default
18218      * this is the linked element, but could be changed to be a child of this
18219      * element.  This lets us do things like only starting the drag when the
18220      * header element within the linked html element is clicked.
18221      * @property handleElId
18222      * @type String
18223      * @private
18224      */
18225     handleElId: null,
18226
18227     /**
18228      * An associative array of HTML tags that will be ignored if clicked.
18229      * @property invalidHandleTypes
18230      * @type {string: string}
18231      */
18232     invalidHandleTypes: null,
18233
18234     /**
18235      * An associative array of ids for elements that will be ignored if clicked
18236      * @property invalidHandleIds
18237      * @type {string: string}
18238      */
18239     invalidHandleIds: null,
18240
18241     /**
18242      * An indexted array of css class names for elements that will be ignored
18243      * if clicked.
18244      * @property invalidHandleClasses
18245      * @type string[]
18246      */
18247     invalidHandleClasses: null,
18248
18249     /**
18250      * The linked element's absolute X position at the time the drag was
18251      * started
18252      * @property startPageX
18253      * @type int
18254      * @private
18255      */
18256     startPageX: 0,
18257
18258     /**
18259      * The linked element's absolute X position at the time the drag was
18260      * started
18261      * @property startPageY
18262      * @type int
18263      * @private
18264      */
18265     startPageY: 0,
18266
18267     /**
18268      * The group defines a logical collection of DragDrop objects that are
18269      * related.  Instances only get events when interacting with other
18270      * DragDrop object in the same group.  This lets us define multiple
18271      * groups using a single DragDrop subclass if we want.
18272      * @property groups
18273      * @type {string: string}
18274      */
18275     groups: null,
18276
18277     /**
18278      * Individual drag/drop instances can be locked.  This will prevent
18279      * onmousedown start drag.
18280      * @property locked
18281      * @type boolean
18282      * @private
18283      */
18284     locked: false,
18285
18286     /**
18287      * Lock this instance
18288      * @method lock
18289      */
18290     lock: function() { this.locked = true; },
18291
18292     /**
18293      * Unlock this instace
18294      * @method unlock
18295      */
18296     unlock: function() { this.locked = false; },
18297
18298     /**
18299      * By default, all insances can be a drop target.  This can be disabled by
18300      * setting isTarget to false.
18301      * @method isTarget
18302      * @type boolean
18303      */
18304     isTarget: true,
18305
18306     /**
18307      * The padding configured for this drag and drop object for calculating
18308      * the drop zone intersection with this object.
18309      * @method padding
18310      * @type int[]
18311      */
18312     padding: null,
18313
18314     /**
18315      * Cached reference to the linked element
18316      * @property _domRef
18317      * @private
18318      */
18319     _domRef: null,
18320
18321     /**
18322      * Internal typeof flag
18323      * @property __ygDragDrop
18324      * @private
18325      */
18326     __ygDragDrop: true,
18327
18328     /**
18329      * Set to true when horizontal contraints are applied
18330      * @property constrainX
18331      * @type boolean
18332      * @private
18333      */
18334     constrainX: false,
18335
18336     /**
18337      * Set to true when vertical contraints are applied
18338      * @property constrainY
18339      * @type boolean
18340      * @private
18341      */
18342     constrainY: false,
18343
18344     /**
18345      * The left constraint
18346      * @property minX
18347      * @type int
18348      * @private
18349      */
18350     minX: 0,
18351
18352     /**
18353      * The right constraint
18354      * @property maxX
18355      * @type int
18356      * @private
18357      */
18358     maxX: 0,
18359
18360     /**
18361      * The up constraint
18362      * @property minY
18363      * @type int
18364      * @type int
18365      * @private
18366      */
18367     minY: 0,
18368
18369     /**
18370      * The down constraint
18371      * @property maxY
18372      * @type int
18373      * @private
18374      */
18375     maxY: 0,
18376
18377     /**
18378      * Maintain offsets when we resetconstraints.  Set to true when you want
18379      * the position of the element relative to its parent to stay the same
18380      * when the page changes
18381      *
18382      * @property maintainOffset
18383      * @type boolean
18384      */
18385     maintainOffset: false,
18386
18387     /**
18388      * Array of pixel locations the element will snap to if we specified a
18389      * horizontal graduation/interval.  This array is generated automatically
18390      * when you define a tick interval.
18391      * @property xTicks
18392      * @type int[]
18393      */
18394     xTicks: null,
18395
18396     /**
18397      * Array of pixel locations the element will snap to if we specified a
18398      * vertical graduation/interval.  This array is generated automatically
18399      * when you define a tick interval.
18400      * @property yTicks
18401      * @type int[]
18402      */
18403     yTicks: null,
18404
18405     /**
18406      * By default the drag and drop instance will only respond to the primary
18407      * button click (left button for a right-handed mouse).  Set to true to
18408      * allow drag and drop to start with any mouse click that is propogated
18409      * by the browser
18410      * @property primaryButtonOnly
18411      * @type boolean
18412      */
18413     primaryButtonOnly: true,
18414
18415     /**
18416      * The availabe property is false until the linked dom element is accessible.
18417      * @property available
18418      * @type boolean
18419      */
18420     available: false,
18421
18422     /**
18423      * By default, drags can only be initiated if the mousedown occurs in the
18424      * region the linked element is.  This is done in part to work around a
18425      * bug in some browsers that mis-report the mousedown if the previous
18426      * mouseup happened outside of the window.  This property is set to true
18427      * if outer handles are defined.
18428      *
18429      * @property hasOuterHandles
18430      * @type boolean
18431      * @default false
18432      */
18433     hasOuterHandles: false,
18434
18435     /**
18436      * Code that executes immediately before the startDrag event
18437      * @method b4StartDrag
18438      * @private
18439      */
18440     b4StartDrag: function(x, y) { },
18441
18442     /**
18443      * Abstract method called after a drag/drop object is clicked
18444      * and the drag or mousedown time thresholds have beeen met.
18445      * @method startDrag
18446      * @param {int} X click location
18447      * @param {int} Y click location
18448      */
18449     startDrag: function(x, y) { /* override this */ },
18450
18451     /**
18452      * Code that executes immediately before the onDrag event
18453      * @method b4Drag
18454      * @private
18455      */
18456     b4Drag: function(e) { },
18457
18458     /**
18459      * Abstract method called during the onMouseMove event while dragging an
18460      * object.
18461      * @method onDrag
18462      * @param {Event} e the mousemove event
18463      */
18464     onDrag: function(e) { /* override this */ },
18465
18466     /**
18467      * Abstract method called when this element fist begins hovering over
18468      * another DragDrop obj
18469      * @method onDragEnter
18470      * @param {Event} e the mousemove event
18471      * @param {String|DragDrop[]} id In POINT mode, the element
18472      * id this is hovering over.  In INTERSECT mode, an array of one or more
18473      * dragdrop items being hovered over.
18474      */
18475     onDragEnter: function(e, id) { /* override this */ },
18476
18477     /**
18478      * Code that executes immediately before the onDragOver event
18479      * @method b4DragOver
18480      * @private
18481      */
18482     b4DragOver: function(e) { },
18483
18484     /**
18485      * Abstract method called when this element is hovering over another
18486      * DragDrop obj
18487      * @method onDragOver
18488      * @param {Event} e the mousemove event
18489      * @param {String|DragDrop[]} id In POINT mode, the element
18490      * id this is hovering over.  In INTERSECT mode, an array of dd items
18491      * being hovered over.
18492      */
18493     onDragOver: function(e, id) { /* override this */ },
18494
18495     /**
18496      * Code that executes immediately before the onDragOut event
18497      * @method b4DragOut
18498      * @private
18499      */
18500     b4DragOut: function(e) { },
18501
18502     /**
18503      * Abstract method called when we are no longer hovering over an element
18504      * @method onDragOut
18505      * @param {Event} e the mousemove event
18506      * @param {String|DragDrop[]} id In POINT mode, the element
18507      * id this was hovering over.  In INTERSECT mode, an array of dd items
18508      * that the mouse is no longer over.
18509      */
18510     onDragOut: function(e, id) { /* override this */ },
18511
18512     /**
18513      * Code that executes immediately before the onDragDrop event
18514      * @method b4DragDrop
18515      * @private
18516      */
18517     b4DragDrop: function(e) { },
18518
18519     /**
18520      * Abstract method called when this item is dropped on another DragDrop
18521      * obj
18522      * @method onDragDrop
18523      * @param {Event} e the mouseup event
18524      * @param {String|DragDrop[]} id In POINT mode, the element
18525      * id this was dropped on.  In INTERSECT mode, an array of dd items this
18526      * was dropped on.
18527      */
18528     onDragDrop: function(e, id) { /* override this */ },
18529
18530     /**
18531      * Abstract method called when this item is dropped on an area with no
18532      * drop target
18533      * @method onInvalidDrop
18534      * @param {Event} e the mouseup event
18535      */
18536     onInvalidDrop: function(e) { /* override this */ },
18537
18538     /**
18539      * Code that executes immediately before the endDrag event
18540      * @method b4EndDrag
18541      * @private
18542      */
18543     b4EndDrag: function(e) { },
18544
18545     /**
18546      * Fired when we are done dragging the object
18547      * @method endDrag
18548      * @param {Event} e the mouseup event
18549      */
18550     endDrag: function(e) { /* override this */ },
18551
18552     /**
18553      * Code executed immediately before the onMouseDown event
18554      * @method b4MouseDown
18555      * @param {Event} e the mousedown event
18556      * @private
18557      */
18558     b4MouseDown: function(e) {  },
18559
18560     /**
18561      * Event handler that fires when a drag/drop obj gets a mousedown
18562      * @method onMouseDown
18563      * @param {Event} e the mousedown event
18564      */
18565     onMouseDown: function(e) { /* override this */ },
18566
18567     /**
18568      * Event handler that fires when a drag/drop obj gets a mouseup
18569      * @method onMouseUp
18570      * @param {Event} e the mouseup event
18571      */
18572     onMouseUp: function(e) { /* override this */ },
18573
18574     /**
18575      * Override the onAvailable method to do what is needed after the initial
18576      * position was determined.
18577      * @method onAvailable
18578      */
18579     onAvailable: function () {
18580     },
18581
18582     /*
18583      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
18584      * @type Object
18585      */
18586     defaultPadding : {left:0, right:0, top:0, bottom:0},
18587
18588     /*
18589      * Initializes the drag drop object's constraints to restrict movement to a certain element.
18590  *
18591  * Usage:
18592  <pre><code>
18593  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
18594                 { dragElId: "existingProxyDiv" });
18595  dd.startDrag = function(){
18596      this.constrainTo("parent-id");
18597  };
18598  </code></pre>
18599  * Or you can initalize it using the {@link Roo.Element} object:
18600  <pre><code>
18601  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
18602      startDrag : function(){
18603          this.constrainTo("parent-id");
18604      }
18605  });
18606  </code></pre>
18607      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
18608      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
18609      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
18610      * an object containing the sides to pad. For example: {right:10, bottom:10}
18611      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
18612      */
18613     constrainTo : function(constrainTo, pad, inContent){
18614         if(typeof pad == "number"){
18615             pad = {left: pad, right:pad, top:pad, bottom:pad};
18616         }
18617         pad = pad || this.defaultPadding;
18618         var b = Roo.get(this.getEl()).getBox();
18619         var ce = Roo.get(constrainTo);
18620         var s = ce.getScroll();
18621         var c, cd = ce.dom;
18622         if(cd == document.body){
18623             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
18624         }else{
18625             xy = ce.getXY();
18626             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
18627         }
18628
18629
18630         var topSpace = b.y - c.y;
18631         var leftSpace = b.x - c.x;
18632
18633         this.resetConstraints();
18634         this.setXConstraint(leftSpace - (pad.left||0), // left
18635                 c.width - leftSpace - b.width - (pad.right||0) //right
18636         );
18637         this.setYConstraint(topSpace - (pad.top||0), //top
18638                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
18639         );
18640     },
18641
18642     /**
18643      * Returns a reference to the linked element
18644      * @method getEl
18645      * @return {HTMLElement} the html element
18646      */
18647     getEl: function() {
18648         if (!this._domRef) {
18649             this._domRef = Roo.getDom(this.id);
18650         }
18651
18652         return this._domRef;
18653     },
18654
18655     /**
18656      * Returns a reference to the actual element to drag.  By default this is
18657      * the same as the html element, but it can be assigned to another
18658      * element. An example of this can be found in Roo.dd.DDProxy
18659      * @method getDragEl
18660      * @return {HTMLElement} the html element
18661      */
18662     getDragEl: function() {
18663         return Roo.getDom(this.dragElId);
18664     },
18665
18666     /**
18667      * Sets up the DragDrop object.  Must be called in the constructor of any
18668      * Roo.dd.DragDrop subclass
18669      * @method init
18670      * @param id the id of the linked element
18671      * @param {String} sGroup the group of related items
18672      * @param {object} config configuration attributes
18673      */
18674     init: function(id, sGroup, config) {
18675         this.initTarget(id, sGroup, config);
18676         if (!Roo.isTouch) {
18677             Event.on(this.id, "mousedown", this.handleMouseDown, this);
18678         }
18679         Event.on(this.id, "touchstart", this.handleMouseDown, this);
18680         // Event.on(this.id, "selectstart", Event.preventDefault);
18681     },
18682
18683     /**
18684      * Initializes Targeting functionality only... the object does not
18685      * get a mousedown handler.
18686      * @method initTarget
18687      * @param id the id of the linked element
18688      * @param {String} sGroup the group of related items
18689      * @param {object} config configuration attributes
18690      */
18691     initTarget: function(id, sGroup, config) {
18692
18693         // configuration attributes
18694         this.config = config || {};
18695
18696         // create a local reference to the drag and drop manager
18697         this.DDM = Roo.dd.DDM;
18698         // initialize the groups array
18699         this.groups = {};
18700
18701         // assume that we have an element reference instead of an id if the
18702         // parameter is not a string
18703         if (typeof id !== "string") {
18704             id = Roo.id(id);
18705         }
18706
18707         // set the id
18708         this.id = id;
18709
18710         // add to an interaction group
18711         this.addToGroup((sGroup) ? sGroup : "default");
18712
18713         // We don't want to register this as the handle with the manager
18714         // so we just set the id rather than calling the setter.
18715         this.handleElId = id;
18716
18717         // the linked element is the element that gets dragged by default
18718         this.setDragElId(id);
18719
18720         // by default, clicked anchors will not start drag operations.
18721         this.invalidHandleTypes = { A: "A" };
18722         this.invalidHandleIds = {};
18723         this.invalidHandleClasses = [];
18724
18725         this.applyConfig();
18726
18727         this.handleOnAvailable();
18728     },
18729
18730     /**
18731      * Applies the configuration parameters that were passed into the constructor.
18732      * This is supposed to happen at each level through the inheritance chain.  So
18733      * a DDProxy implentation will execute apply config on DDProxy, DD, and
18734      * DragDrop in order to get all of the parameters that are available in
18735      * each object.
18736      * @method applyConfig
18737      */
18738     applyConfig: function() {
18739
18740         // configurable properties:
18741         //    padding, isTarget, maintainOffset, primaryButtonOnly
18742         this.padding           = this.config.padding || [0, 0, 0, 0];
18743         this.isTarget          = (this.config.isTarget !== false);
18744         this.maintainOffset    = (this.config.maintainOffset);
18745         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
18746
18747     },
18748
18749     /**
18750      * Executed when the linked element is available
18751      * @method handleOnAvailable
18752      * @private
18753      */
18754     handleOnAvailable: function() {
18755         this.available = true;
18756         this.resetConstraints();
18757         this.onAvailable();
18758     },
18759
18760      /**
18761      * Configures the padding for the target zone in px.  Effectively expands
18762      * (or reduces) the virtual object size for targeting calculations.
18763      * Supports css-style shorthand; if only one parameter is passed, all sides
18764      * will have that padding, and if only two are passed, the top and bottom
18765      * will have the first param, the left and right the second.
18766      * @method setPadding
18767      * @param {int} iTop    Top pad
18768      * @param {int} iRight  Right pad
18769      * @param {int} iBot    Bot pad
18770      * @param {int} iLeft   Left pad
18771      */
18772     setPadding: function(iTop, iRight, iBot, iLeft) {
18773         // this.padding = [iLeft, iRight, iTop, iBot];
18774         if (!iRight && 0 !== iRight) {
18775             this.padding = [iTop, iTop, iTop, iTop];
18776         } else if (!iBot && 0 !== iBot) {
18777             this.padding = [iTop, iRight, iTop, iRight];
18778         } else {
18779             this.padding = [iTop, iRight, iBot, iLeft];
18780         }
18781     },
18782
18783     /**
18784      * Stores the initial placement of the linked element.
18785      * @method setInitialPosition
18786      * @param {int} diffX   the X offset, default 0
18787      * @param {int} diffY   the Y offset, default 0
18788      */
18789     setInitPosition: function(diffX, diffY) {
18790         var el = this.getEl();
18791
18792         if (!this.DDM.verifyEl(el)) {
18793             return;
18794         }
18795
18796         var dx = diffX || 0;
18797         var dy = diffY || 0;
18798
18799         var p = Dom.getXY( el );
18800
18801         this.initPageX = p[0] - dx;
18802         this.initPageY = p[1] - dy;
18803
18804         this.lastPageX = p[0];
18805         this.lastPageY = p[1];
18806
18807
18808         this.setStartPosition(p);
18809     },
18810
18811     /**
18812      * Sets the start position of the element.  This is set when the obj
18813      * is initialized, the reset when a drag is started.
18814      * @method setStartPosition
18815      * @param pos current position (from previous lookup)
18816      * @private
18817      */
18818     setStartPosition: function(pos) {
18819         var p = pos || Dom.getXY( this.getEl() );
18820         this.deltaSetXY = null;
18821
18822         this.startPageX = p[0];
18823         this.startPageY = p[1];
18824     },
18825
18826     /**
18827      * Add this instance to a group of related drag/drop objects.  All
18828      * instances belong to at least one group, and can belong to as many
18829      * groups as needed.
18830      * @method addToGroup
18831      * @param sGroup {string} the name of the group
18832      */
18833     addToGroup: function(sGroup) {
18834         this.groups[sGroup] = true;
18835         this.DDM.regDragDrop(this, sGroup);
18836     },
18837
18838     /**
18839      * Remove's this instance from the supplied interaction group
18840      * @method removeFromGroup
18841      * @param {string}  sGroup  The group to drop
18842      */
18843     removeFromGroup: function(sGroup) {
18844         if (this.groups[sGroup]) {
18845             delete this.groups[sGroup];
18846         }
18847
18848         this.DDM.removeDDFromGroup(this, sGroup);
18849     },
18850
18851     /**
18852      * Allows you to specify that an element other than the linked element
18853      * will be moved with the cursor during a drag
18854      * @method setDragElId
18855      * @param id {string} the id of the element that will be used to initiate the drag
18856      */
18857     setDragElId: function(id) {
18858         this.dragElId = id;
18859     },
18860
18861     /**
18862      * Allows you to specify a child of the linked element that should be
18863      * used to initiate the drag operation.  An example of this would be if
18864      * you have a content div with text and links.  Clicking anywhere in the
18865      * content area would normally start the drag operation.  Use this method
18866      * to specify that an element inside of the content div is the element
18867      * that starts the drag operation.
18868      * @method setHandleElId
18869      * @param id {string} the id of the element that will be used to
18870      * initiate the drag.
18871      */
18872     setHandleElId: function(id) {
18873         if (typeof id !== "string") {
18874             id = Roo.id(id);
18875         }
18876         this.handleElId = id;
18877         this.DDM.regHandle(this.id, id);
18878     },
18879
18880     /**
18881      * Allows you to set an element outside of the linked element as a drag
18882      * handle
18883      * @method setOuterHandleElId
18884      * @param id the id of the element that will be used to initiate the drag
18885      */
18886     setOuterHandleElId: function(id) {
18887         if (typeof id !== "string") {
18888             id = Roo.id(id);
18889         }
18890         Event.on(id, "mousedown",
18891                 this.handleMouseDown, this);
18892         this.setHandleElId(id);
18893
18894         this.hasOuterHandles = true;
18895     },
18896
18897     /**
18898      * Remove all drag and drop hooks for this element
18899      * @method unreg
18900      */
18901     unreg: function() {
18902         Event.un(this.id, "mousedown",
18903                 this.handleMouseDown);
18904         Event.un(this.id, "touchstart",
18905                 this.handleMouseDown);
18906         this._domRef = null;
18907         this.DDM._remove(this);
18908     },
18909
18910     destroy : function(){
18911         this.unreg();
18912     },
18913
18914     /**
18915      * Returns true if this instance is locked, or the drag drop mgr is locked
18916      * (meaning that all drag/drop is disabled on the page.)
18917      * @method isLocked
18918      * @return {boolean} true if this obj or all drag/drop is locked, else
18919      * false
18920      */
18921     isLocked: function() {
18922         return (this.DDM.isLocked() || this.locked);
18923     },
18924
18925     /**
18926      * Fired when this object is clicked
18927      * @method handleMouseDown
18928      * @param {Event} e
18929      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
18930      * @private
18931      */
18932     handleMouseDown: function(e, oDD){
18933      
18934         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
18935             //Roo.log('not touch/ button !=0');
18936             return;
18937         }
18938         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
18939             return; // double touch..
18940         }
18941         
18942
18943         if (this.isLocked()) {
18944             //Roo.log('locked');
18945             return;
18946         }
18947
18948         this.DDM.refreshCache(this.groups);
18949 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
18950         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
18951         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
18952             //Roo.log('no outer handes or not over target');
18953                 // do nothing.
18954         } else {
18955 //            Roo.log('check validator');
18956             if (this.clickValidator(e)) {
18957 //                Roo.log('validate success');
18958                 // set the initial element position
18959                 this.setStartPosition();
18960
18961
18962                 this.b4MouseDown(e);
18963                 this.onMouseDown(e);
18964
18965                 this.DDM.handleMouseDown(e, this);
18966
18967                 this.DDM.stopEvent(e);
18968             } else {
18969
18970
18971             }
18972         }
18973     },
18974
18975     clickValidator: function(e) {
18976         var target = e.getTarget();
18977         return ( this.isValidHandleChild(target) &&
18978                     (this.id == this.handleElId ||
18979                         this.DDM.handleWasClicked(target, this.id)) );
18980     },
18981
18982     /**
18983      * Allows you to specify a tag name that should not start a drag operation
18984      * when clicked.  This is designed to facilitate embedding links within a
18985      * drag handle that do something other than start the drag.
18986      * @method addInvalidHandleType
18987      * @param {string} tagName the type of element to exclude
18988      */
18989     addInvalidHandleType: function(tagName) {
18990         var type = tagName.toUpperCase();
18991         this.invalidHandleTypes[type] = type;
18992     },
18993
18994     /**
18995      * Lets you to specify an element id for a child of a drag handle
18996      * that should not initiate a drag
18997      * @method addInvalidHandleId
18998      * @param {string} id the element id of the element you wish to ignore
18999      */
19000     addInvalidHandleId: function(id) {
19001         if (typeof id !== "string") {
19002             id = Roo.id(id);
19003         }
19004         this.invalidHandleIds[id] = id;
19005     },
19006
19007     /**
19008      * Lets you specify a css class of elements that will not initiate a drag
19009      * @method addInvalidHandleClass
19010      * @param {string} cssClass the class of the elements you wish to ignore
19011      */
19012     addInvalidHandleClass: function(cssClass) {
19013         this.invalidHandleClasses.push(cssClass);
19014     },
19015
19016     /**
19017      * Unsets an excluded tag name set by addInvalidHandleType
19018      * @method removeInvalidHandleType
19019      * @param {string} tagName the type of element to unexclude
19020      */
19021     removeInvalidHandleType: function(tagName) {
19022         var type = tagName.toUpperCase();
19023         // this.invalidHandleTypes[type] = null;
19024         delete this.invalidHandleTypes[type];
19025     },
19026
19027     /**
19028      * Unsets an invalid handle id
19029      * @method removeInvalidHandleId
19030      * @param {string} id the id of the element to re-enable
19031      */
19032     removeInvalidHandleId: function(id) {
19033         if (typeof id !== "string") {
19034             id = Roo.id(id);
19035         }
19036         delete this.invalidHandleIds[id];
19037     },
19038
19039     /**
19040      * Unsets an invalid css class
19041      * @method removeInvalidHandleClass
19042      * @param {string} cssClass the class of the element(s) you wish to
19043      * re-enable
19044      */
19045     removeInvalidHandleClass: function(cssClass) {
19046         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
19047             if (this.invalidHandleClasses[i] == cssClass) {
19048                 delete this.invalidHandleClasses[i];
19049             }
19050         }
19051     },
19052
19053     /**
19054      * Checks the tag exclusion list to see if this click should be ignored
19055      * @method isValidHandleChild
19056      * @param {HTMLElement} node the HTMLElement to evaluate
19057      * @return {boolean} true if this is a valid tag type, false if not
19058      */
19059     isValidHandleChild: function(node) {
19060
19061         var valid = true;
19062         // var n = (node.nodeName == "#text") ? node.parentNode : node;
19063         var nodeName;
19064         try {
19065             nodeName = node.nodeName.toUpperCase();
19066         } catch(e) {
19067             nodeName = node.nodeName;
19068         }
19069         valid = valid && !this.invalidHandleTypes[nodeName];
19070         valid = valid && !this.invalidHandleIds[node.id];
19071
19072         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
19073             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
19074         }
19075
19076
19077         return valid;
19078
19079     },
19080
19081     /**
19082      * Create the array of horizontal tick marks if an interval was specified
19083      * in setXConstraint().
19084      * @method setXTicks
19085      * @private
19086      */
19087     setXTicks: function(iStartX, iTickSize) {
19088         this.xTicks = [];
19089         this.xTickSize = iTickSize;
19090
19091         var tickMap = {};
19092
19093         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
19094             if (!tickMap[i]) {
19095                 this.xTicks[this.xTicks.length] = i;
19096                 tickMap[i] = true;
19097             }
19098         }
19099
19100         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
19101             if (!tickMap[i]) {
19102                 this.xTicks[this.xTicks.length] = i;
19103                 tickMap[i] = true;
19104             }
19105         }
19106
19107         this.xTicks.sort(this.DDM.numericSort) ;
19108     },
19109
19110     /**
19111      * Create the array of vertical tick marks if an interval was specified in
19112      * setYConstraint().
19113      * @method setYTicks
19114      * @private
19115      */
19116     setYTicks: function(iStartY, iTickSize) {
19117         this.yTicks = [];
19118         this.yTickSize = iTickSize;
19119
19120         var tickMap = {};
19121
19122         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
19123             if (!tickMap[i]) {
19124                 this.yTicks[this.yTicks.length] = i;
19125                 tickMap[i] = true;
19126             }
19127         }
19128
19129         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
19130             if (!tickMap[i]) {
19131                 this.yTicks[this.yTicks.length] = i;
19132                 tickMap[i] = true;
19133             }
19134         }
19135
19136         this.yTicks.sort(this.DDM.numericSort) ;
19137     },
19138
19139     /**
19140      * By default, the element can be dragged any place on the screen.  Use
19141      * this method to limit the horizontal travel of the element.  Pass in
19142      * 0,0 for the parameters if you want to lock the drag to the y axis.
19143      * @method setXConstraint
19144      * @param {int} iLeft the number of pixels the element can move to the left
19145      * @param {int} iRight the number of pixels the element can move to the
19146      * right
19147      * @param {int} iTickSize optional parameter for specifying that the
19148      * element
19149      * should move iTickSize pixels at a time.
19150      */
19151     setXConstraint: function(iLeft, iRight, iTickSize) {
19152         this.leftConstraint = iLeft;
19153         this.rightConstraint = iRight;
19154
19155         this.minX = this.initPageX - iLeft;
19156         this.maxX = this.initPageX + iRight;
19157         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
19158
19159         this.constrainX = true;
19160     },
19161
19162     /**
19163      * Clears any constraints applied to this instance.  Also clears ticks
19164      * since they can't exist independent of a constraint at this time.
19165      * @method clearConstraints
19166      */
19167     clearConstraints: function() {
19168         this.constrainX = false;
19169         this.constrainY = false;
19170         this.clearTicks();
19171     },
19172
19173     /**
19174      * Clears any tick interval defined for this instance
19175      * @method clearTicks
19176      */
19177     clearTicks: function() {
19178         this.xTicks = null;
19179         this.yTicks = null;
19180         this.xTickSize = 0;
19181         this.yTickSize = 0;
19182     },
19183
19184     /**
19185      * By default, the element can be dragged any place on the screen.  Set
19186      * this to limit the vertical travel of the element.  Pass in 0,0 for the
19187      * parameters if you want to lock the drag to the x axis.
19188      * @method setYConstraint
19189      * @param {int} iUp the number of pixels the element can move up
19190      * @param {int} iDown the number of pixels the element can move down
19191      * @param {int} iTickSize optional parameter for specifying that the
19192      * element should move iTickSize pixels at a time.
19193      */
19194     setYConstraint: function(iUp, iDown, iTickSize) {
19195         this.topConstraint = iUp;
19196         this.bottomConstraint = iDown;
19197
19198         this.minY = this.initPageY - iUp;
19199         this.maxY = this.initPageY + iDown;
19200         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
19201
19202         this.constrainY = true;
19203
19204     },
19205
19206     /**
19207      * resetConstraints must be called if you manually reposition a dd element.
19208      * @method resetConstraints
19209      * @param {boolean} maintainOffset
19210      */
19211     resetConstraints: function() {
19212
19213
19214         // Maintain offsets if necessary
19215         if (this.initPageX || this.initPageX === 0) {
19216             // figure out how much this thing has moved
19217             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
19218             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
19219
19220             this.setInitPosition(dx, dy);
19221
19222         // This is the first time we have detected the element's position
19223         } else {
19224             this.setInitPosition();
19225         }
19226
19227         if (this.constrainX) {
19228             this.setXConstraint( this.leftConstraint,
19229                                  this.rightConstraint,
19230                                  this.xTickSize        );
19231         }
19232
19233         if (this.constrainY) {
19234             this.setYConstraint( this.topConstraint,
19235                                  this.bottomConstraint,
19236                                  this.yTickSize         );
19237         }
19238     },
19239
19240     /**
19241      * Normally the drag element is moved pixel by pixel, but we can specify
19242      * that it move a number of pixels at a time.  This method resolves the
19243      * location when we have it set up like this.
19244      * @method getTick
19245      * @param {int} val where we want to place the object
19246      * @param {int[]} tickArray sorted array of valid points
19247      * @return {int} the closest tick
19248      * @private
19249      */
19250     getTick: function(val, tickArray) {
19251
19252         if (!tickArray) {
19253             // If tick interval is not defined, it is effectively 1 pixel,
19254             // so we return the value passed to us.
19255             return val;
19256         } else if (tickArray[0] >= val) {
19257             // The value is lower than the first tick, so we return the first
19258             // tick.
19259             return tickArray[0];
19260         } else {
19261             for (var i=0, len=tickArray.length; i<len; ++i) {
19262                 var next = i + 1;
19263                 if (tickArray[next] && tickArray[next] >= val) {
19264                     var diff1 = val - tickArray[i];
19265                     var diff2 = tickArray[next] - val;
19266                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
19267                 }
19268             }
19269
19270             // The value is larger than the last tick, so we return the last
19271             // tick.
19272             return tickArray[tickArray.length - 1];
19273         }
19274     },
19275
19276     /**
19277      * toString method
19278      * @method toString
19279      * @return {string} string representation of the dd obj
19280      */
19281     toString: function() {
19282         return ("DragDrop " + this.id);
19283     }
19284
19285 });
19286
19287 })();
19288 /*
19289  * Based on:
19290  * Ext JS Library 1.1.1
19291  * Copyright(c) 2006-2007, Ext JS, LLC.
19292  *
19293  * Originally Released Under LGPL - original licence link has changed is not relivant.
19294  *
19295  * Fork - LGPL
19296  * <script type="text/javascript">
19297  */
19298
19299
19300 /**
19301  * The drag and drop utility provides a framework for building drag and drop
19302  * applications.  In addition to enabling drag and drop for specific elements,
19303  * the drag and drop elements are tracked by the manager class, and the
19304  * interactions between the various elements are tracked during the drag and
19305  * the implementing code is notified about these important moments.
19306  */
19307
19308 // Only load the library once.  Rewriting the manager class would orphan
19309 // existing drag and drop instances.
19310 if (!Roo.dd.DragDropMgr) {
19311
19312 /**
19313  * @class Roo.dd.DragDropMgr
19314  * DragDropMgr is a singleton that tracks the element interaction for
19315  * all DragDrop items in the window.  Generally, you will not call
19316  * this class directly, but it does have helper methods that could
19317  * be useful in your DragDrop implementations.
19318  * @singleton
19319  */
19320 Roo.dd.DragDropMgr = function() {
19321
19322     var Event = Roo.EventManager;
19323
19324     return {
19325
19326         /**
19327          * Two dimensional Array of registered DragDrop objects.  The first
19328          * dimension is the DragDrop item group, the second the DragDrop
19329          * object.
19330          * @property ids
19331          * @type {string: string}
19332          * @private
19333          * @static
19334          */
19335         ids: {},
19336
19337         /**
19338          * Array of element ids defined as drag handles.  Used to determine
19339          * if the element that generated the mousedown event is actually the
19340          * handle and not the html element itself.
19341          * @property handleIds
19342          * @type {string: string}
19343          * @private
19344          * @static
19345          */
19346         handleIds: {},
19347
19348         /**
19349          * the DragDrop object that is currently being dragged
19350          * @property dragCurrent
19351          * @type DragDrop
19352          * @private
19353          * @static
19354          **/
19355         dragCurrent: null,
19356
19357         /**
19358          * the DragDrop object(s) that are being hovered over
19359          * @property dragOvers
19360          * @type Array
19361          * @private
19362          * @static
19363          */
19364         dragOvers: {},
19365
19366         /**
19367          * the X distance between the cursor and the object being dragged
19368          * @property deltaX
19369          * @type int
19370          * @private
19371          * @static
19372          */
19373         deltaX: 0,
19374
19375         /**
19376          * the Y distance between the cursor and the object being dragged
19377          * @property deltaY
19378          * @type int
19379          * @private
19380          * @static
19381          */
19382         deltaY: 0,
19383
19384         /**
19385          * Flag to determine if we should prevent the default behavior of the
19386          * events we define. By default this is true, but this can be set to
19387          * false if you need the default behavior (not recommended)
19388          * @property preventDefault
19389          * @type boolean
19390          * @static
19391          */
19392         preventDefault: true,
19393
19394         /**
19395          * Flag to determine if we should stop the propagation of the events
19396          * we generate. This is true by default but you may want to set it to
19397          * false if the html element contains other features that require the
19398          * mouse click.
19399          * @property stopPropagation
19400          * @type boolean
19401          * @static
19402          */
19403         stopPropagation: true,
19404
19405         /**
19406          * Internal flag that is set to true when drag and drop has been
19407          * intialized
19408          * @property initialized
19409          * @private
19410          * @static
19411          */
19412         initalized: false,
19413
19414         /**
19415          * All drag and drop can be disabled.
19416          * @property locked
19417          * @private
19418          * @static
19419          */
19420         locked: false,
19421
19422         /**
19423          * Called the first time an element is registered.
19424          * @method init
19425          * @private
19426          * @static
19427          */
19428         init: function() {
19429             this.initialized = true;
19430         },
19431
19432         /**
19433          * In point mode, drag and drop interaction is defined by the
19434          * location of the cursor during the drag/drop
19435          * @property POINT
19436          * @type int
19437          * @static
19438          */
19439         POINT: 0,
19440
19441         /**
19442          * In intersect mode, drag and drop interactio nis defined by the
19443          * overlap of two or more drag and drop objects.
19444          * @property INTERSECT
19445          * @type int
19446          * @static
19447          */
19448         INTERSECT: 1,
19449
19450         /**
19451          * The current drag and drop mode.  Default: POINT
19452          * @property mode
19453          * @type int
19454          * @static
19455          */
19456         mode: 0,
19457
19458         /**
19459          * Runs method on all drag and drop objects
19460          * @method _execOnAll
19461          * @private
19462          * @static
19463          */
19464         _execOnAll: function(sMethod, args) {
19465             for (var i in this.ids) {
19466                 for (var j in this.ids[i]) {
19467                     var oDD = this.ids[i][j];
19468                     if (! this.isTypeOfDD(oDD)) {
19469                         continue;
19470                     }
19471                     oDD[sMethod].apply(oDD, args);
19472                 }
19473             }
19474         },
19475
19476         /**
19477          * Drag and drop initialization.  Sets up the global event handlers
19478          * @method _onLoad
19479          * @private
19480          * @static
19481          */
19482         _onLoad: function() {
19483
19484             this.init();
19485
19486             if (!Roo.isTouch) {
19487                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
19488                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
19489             }
19490             Event.on(document, "touchend",   this.handleMouseUp, this, true);
19491             Event.on(document, "touchmove", this.handleMouseMove, this, true);
19492             
19493             Event.on(window,   "unload",    this._onUnload, this, true);
19494             Event.on(window,   "resize",    this._onResize, this, true);
19495             // Event.on(window,   "mouseout",    this._test);
19496
19497         },
19498
19499         /**
19500          * Reset constraints on all drag and drop objs
19501          * @method _onResize
19502          * @private
19503          * @static
19504          */
19505         _onResize: function(e) {
19506             this._execOnAll("resetConstraints", []);
19507         },
19508
19509         /**
19510          * Lock all drag and drop functionality
19511          * @method lock
19512          * @static
19513          */
19514         lock: function() { this.locked = true; },
19515
19516         /**
19517          * Unlock all drag and drop functionality
19518          * @method unlock
19519          * @static
19520          */
19521         unlock: function() { this.locked = false; },
19522
19523         /**
19524          * Is drag and drop locked?
19525          * @method isLocked
19526          * @return {boolean} True if drag and drop is locked, false otherwise.
19527          * @static
19528          */
19529         isLocked: function() { return this.locked; },
19530
19531         /**
19532          * Location cache that is set for all drag drop objects when a drag is
19533          * initiated, cleared when the drag is finished.
19534          * @property locationCache
19535          * @private
19536          * @static
19537          */
19538         locationCache: {},
19539
19540         /**
19541          * Set useCache to false if you want to force object the lookup of each
19542          * drag and drop linked element constantly during a drag.
19543          * @property useCache
19544          * @type boolean
19545          * @static
19546          */
19547         useCache: true,
19548
19549         /**
19550          * The number of pixels that the mouse needs to move after the
19551          * mousedown before the drag is initiated.  Default=3;
19552          * @property clickPixelThresh
19553          * @type int
19554          * @static
19555          */
19556         clickPixelThresh: 3,
19557
19558         /**
19559          * The number of milliseconds after the mousedown event to initiate the
19560          * drag if we don't get a mouseup event. Default=1000
19561          * @property clickTimeThresh
19562          * @type int
19563          * @static
19564          */
19565         clickTimeThresh: 350,
19566
19567         /**
19568          * Flag that indicates that either the drag pixel threshold or the
19569          * mousdown time threshold has been met
19570          * @property dragThreshMet
19571          * @type boolean
19572          * @private
19573          * @static
19574          */
19575         dragThreshMet: false,
19576
19577         /**
19578          * Timeout used for the click time threshold
19579          * @property clickTimeout
19580          * @type Object
19581          * @private
19582          * @static
19583          */
19584         clickTimeout: null,
19585
19586         /**
19587          * The X position of the mousedown event stored for later use when a
19588          * drag threshold is met.
19589          * @property startX
19590          * @type int
19591          * @private
19592          * @static
19593          */
19594         startX: 0,
19595
19596         /**
19597          * The Y position of the mousedown event stored for later use when a
19598          * drag threshold is met.
19599          * @property startY
19600          * @type int
19601          * @private
19602          * @static
19603          */
19604         startY: 0,
19605
19606         /**
19607          * Each DragDrop instance must be registered with the DragDropMgr.
19608          * This is executed in DragDrop.init()
19609          * @method regDragDrop
19610          * @param {DragDrop} oDD the DragDrop object to register
19611          * @param {String} sGroup the name of the group this element belongs to
19612          * @static
19613          */
19614         regDragDrop: function(oDD, sGroup) {
19615             if (!this.initialized) { this.init(); }
19616
19617             if (!this.ids[sGroup]) {
19618                 this.ids[sGroup] = {};
19619             }
19620             this.ids[sGroup][oDD.id] = oDD;
19621         },
19622
19623         /**
19624          * Removes the supplied dd instance from the supplied group. Executed
19625          * by DragDrop.removeFromGroup, so don't call this function directly.
19626          * @method removeDDFromGroup
19627          * @private
19628          * @static
19629          */
19630         removeDDFromGroup: function(oDD, sGroup) {
19631             if (!this.ids[sGroup]) {
19632                 this.ids[sGroup] = {};
19633             }
19634
19635             var obj = this.ids[sGroup];
19636             if (obj && obj[oDD.id]) {
19637                 delete obj[oDD.id];
19638             }
19639         },
19640
19641         /**
19642          * Unregisters a drag and drop item.  This is executed in
19643          * DragDrop.unreg, use that method instead of calling this directly.
19644          * @method _remove
19645          * @private
19646          * @static
19647          */
19648         _remove: function(oDD) {
19649             for (var g in oDD.groups) {
19650                 if (g && this.ids[g][oDD.id]) {
19651                     delete this.ids[g][oDD.id];
19652                 }
19653             }
19654             delete this.handleIds[oDD.id];
19655         },
19656
19657         /**
19658          * Each DragDrop handle element must be registered.  This is done
19659          * automatically when executing DragDrop.setHandleElId()
19660          * @method regHandle
19661          * @param {String} sDDId the DragDrop id this element is a handle for
19662          * @param {String} sHandleId the id of the element that is the drag
19663          * handle
19664          * @static
19665          */
19666         regHandle: function(sDDId, sHandleId) {
19667             if (!this.handleIds[sDDId]) {
19668                 this.handleIds[sDDId] = {};
19669             }
19670             this.handleIds[sDDId][sHandleId] = sHandleId;
19671         },
19672
19673         /**
19674          * Utility function to determine if a given element has been
19675          * registered as a drag drop item.
19676          * @method isDragDrop
19677          * @param {String} id the element id to check
19678          * @return {boolean} true if this element is a DragDrop item,
19679          * false otherwise
19680          * @static
19681          */
19682         isDragDrop: function(id) {
19683             return ( this.getDDById(id) ) ? true : false;
19684         },
19685
19686         /**
19687          * Returns the drag and drop instances that are in all groups the
19688          * passed in instance belongs to.
19689          * @method getRelated
19690          * @param {DragDrop} p_oDD the obj to get related data for
19691          * @param {boolean} bTargetsOnly if true, only return targetable objs
19692          * @return {DragDrop[]} the related instances
19693          * @static
19694          */
19695         getRelated: function(p_oDD, bTargetsOnly) {
19696             var oDDs = [];
19697             for (var i in p_oDD.groups) {
19698                 for (j in this.ids[i]) {
19699                     var dd = this.ids[i][j];
19700                     if (! this.isTypeOfDD(dd)) {
19701                         continue;
19702                     }
19703                     if (!bTargetsOnly || dd.isTarget) {
19704                         oDDs[oDDs.length] = dd;
19705                     }
19706                 }
19707             }
19708
19709             return oDDs;
19710         },
19711
19712         /**
19713          * Returns true if the specified dd target is a legal target for
19714          * the specifice drag obj
19715          * @method isLegalTarget
19716          * @param {DragDrop} the drag obj
19717          * @param {DragDrop} the target
19718          * @return {boolean} true if the target is a legal target for the
19719          * dd obj
19720          * @static
19721          */
19722         isLegalTarget: function (oDD, oTargetDD) {
19723             var targets = this.getRelated(oDD, true);
19724             for (var i=0, len=targets.length;i<len;++i) {
19725                 if (targets[i].id == oTargetDD.id) {
19726                     return true;
19727                 }
19728             }
19729
19730             return false;
19731         },
19732
19733         /**
19734          * My goal is to be able to transparently determine if an object is
19735          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
19736          * returns "object", oDD.constructor.toString() always returns
19737          * "DragDrop" and not the name of the subclass.  So for now it just
19738          * evaluates a well-known variable in DragDrop.
19739          * @method isTypeOfDD
19740          * @param {Object} the object to evaluate
19741          * @return {boolean} true if typeof oDD = DragDrop
19742          * @static
19743          */
19744         isTypeOfDD: function (oDD) {
19745             return (oDD && oDD.__ygDragDrop);
19746         },
19747
19748         /**
19749          * Utility function to determine if a given element has been
19750          * registered as a drag drop handle for the given Drag Drop object.
19751          * @method isHandle
19752          * @param {String} id the element id to check
19753          * @return {boolean} true if this element is a DragDrop handle, false
19754          * otherwise
19755          * @static
19756          */
19757         isHandle: function(sDDId, sHandleId) {
19758             return ( this.handleIds[sDDId] &&
19759                             this.handleIds[sDDId][sHandleId] );
19760         },
19761
19762         /**
19763          * Returns the DragDrop instance for a given id
19764          * @method getDDById
19765          * @param {String} id the id of the DragDrop object
19766          * @return {DragDrop} the drag drop object, null if it is not found
19767          * @static
19768          */
19769         getDDById: function(id) {
19770             for (var i in this.ids) {
19771                 if (this.ids[i][id]) {
19772                     return this.ids[i][id];
19773                 }
19774             }
19775             return null;
19776         },
19777
19778         /**
19779          * Fired after a registered DragDrop object gets the mousedown event.
19780          * Sets up the events required to track the object being dragged
19781          * @method handleMouseDown
19782          * @param {Event} e the event
19783          * @param oDD the DragDrop object being dragged
19784          * @private
19785          * @static
19786          */
19787         handleMouseDown: function(e, oDD) {
19788             if(Roo.QuickTips){
19789                 Roo.QuickTips.disable();
19790             }
19791             this.currentTarget = e.getTarget();
19792
19793             this.dragCurrent = oDD;
19794
19795             var el = oDD.getEl();
19796
19797             // track start position
19798             this.startX = e.getPageX();
19799             this.startY = e.getPageY();
19800
19801             this.deltaX = this.startX - el.offsetLeft;
19802             this.deltaY = this.startY - el.offsetTop;
19803
19804             this.dragThreshMet = false;
19805
19806             this.clickTimeout = setTimeout(
19807                     function() {
19808                         var DDM = Roo.dd.DDM;
19809                         DDM.startDrag(DDM.startX, DDM.startY);
19810                     },
19811                     this.clickTimeThresh );
19812         },
19813
19814         /**
19815          * Fired when either the drag pixel threshol or the mousedown hold
19816          * time threshold has been met.
19817          * @method startDrag
19818          * @param x {int} the X position of the original mousedown
19819          * @param y {int} the Y position of the original mousedown
19820          * @static
19821          */
19822         startDrag: function(x, y) {
19823             clearTimeout(this.clickTimeout);
19824             if (this.dragCurrent) {
19825                 this.dragCurrent.b4StartDrag(x, y);
19826                 this.dragCurrent.startDrag(x, y);
19827             }
19828             this.dragThreshMet = true;
19829         },
19830
19831         /**
19832          * Internal function to handle the mouseup event.  Will be invoked
19833          * from the context of the document.
19834          * @method handleMouseUp
19835          * @param {Event} e the event
19836          * @private
19837          * @static
19838          */
19839         handleMouseUp: function(e) {
19840
19841             if(Roo.QuickTips){
19842                 Roo.QuickTips.enable();
19843             }
19844             if (! this.dragCurrent) {
19845                 return;
19846             }
19847
19848             clearTimeout(this.clickTimeout);
19849
19850             if (this.dragThreshMet) {
19851                 this.fireEvents(e, true);
19852             } else {
19853             }
19854
19855             this.stopDrag(e);
19856
19857             this.stopEvent(e);
19858         },
19859
19860         /**
19861          * Utility to stop event propagation and event default, if these
19862          * features are turned on.
19863          * @method stopEvent
19864          * @param {Event} e the event as returned by this.getEvent()
19865          * @static
19866          */
19867         stopEvent: function(e){
19868             if(this.stopPropagation) {
19869                 e.stopPropagation();
19870             }
19871
19872             if (this.preventDefault) {
19873                 e.preventDefault();
19874             }
19875         },
19876
19877         /**
19878          * Internal function to clean up event handlers after the drag
19879          * operation is complete
19880          * @method stopDrag
19881          * @param {Event} e the event
19882          * @private
19883          * @static
19884          */
19885         stopDrag: function(e) {
19886             // Fire the drag end event for the item that was dragged
19887             if (this.dragCurrent) {
19888                 if (this.dragThreshMet) {
19889                     this.dragCurrent.b4EndDrag(e);
19890                     this.dragCurrent.endDrag(e);
19891                 }
19892
19893                 this.dragCurrent.onMouseUp(e);
19894             }
19895
19896             this.dragCurrent = null;
19897             this.dragOvers = {};
19898         },
19899
19900         /**
19901          * Internal function to handle the mousemove event.  Will be invoked
19902          * from the context of the html element.
19903          *
19904          * @TODO figure out what we can do about mouse events lost when the
19905          * user drags objects beyond the window boundary.  Currently we can
19906          * detect this in internet explorer by verifying that the mouse is
19907          * down during the mousemove event.  Firefox doesn't give us the
19908          * button state on the mousemove event.
19909          * @method handleMouseMove
19910          * @param {Event} e the event
19911          * @private
19912          * @static
19913          */
19914         handleMouseMove: function(e) {
19915             if (! this.dragCurrent) {
19916                 return true;
19917             }
19918
19919             // var button = e.which || e.button;
19920
19921             // check for IE mouseup outside of page boundary
19922             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
19923                 this.stopEvent(e);
19924                 return this.handleMouseUp(e);
19925             }
19926
19927             if (!this.dragThreshMet) {
19928                 var diffX = Math.abs(this.startX - e.getPageX());
19929                 var diffY = Math.abs(this.startY - e.getPageY());
19930                 if (diffX > this.clickPixelThresh ||
19931                             diffY > this.clickPixelThresh) {
19932                     this.startDrag(this.startX, this.startY);
19933                 }
19934             }
19935
19936             if (this.dragThreshMet) {
19937                 this.dragCurrent.b4Drag(e);
19938                 this.dragCurrent.onDrag(e);
19939                 if(!this.dragCurrent.moveOnly){
19940                     this.fireEvents(e, false);
19941                 }
19942             }
19943
19944             this.stopEvent(e);
19945
19946             return true;
19947         },
19948
19949         /**
19950          * Iterates over all of the DragDrop elements to find ones we are
19951          * hovering over or dropping on
19952          * @method fireEvents
19953          * @param {Event} e the event
19954          * @param {boolean} isDrop is this a drop op or a mouseover op?
19955          * @private
19956          * @static
19957          */
19958         fireEvents: function(e, isDrop) {
19959             var dc = this.dragCurrent;
19960
19961             // If the user did the mouse up outside of the window, we could
19962             // get here even though we have ended the drag.
19963             if (!dc || dc.isLocked()) {
19964                 return;
19965             }
19966
19967             var pt = e.getPoint();
19968
19969             // cache the previous dragOver array
19970             var oldOvers = [];
19971
19972             var outEvts   = [];
19973             var overEvts  = [];
19974             var dropEvts  = [];
19975             var enterEvts = [];
19976
19977             // Check to see if the object(s) we were hovering over is no longer
19978             // being hovered over so we can fire the onDragOut event
19979             for (var i in this.dragOvers) {
19980
19981                 var ddo = this.dragOvers[i];
19982
19983                 if (! this.isTypeOfDD(ddo)) {
19984                     continue;
19985                 }
19986
19987                 if (! this.isOverTarget(pt, ddo, this.mode)) {
19988                     outEvts.push( ddo );
19989                 }
19990
19991                 oldOvers[i] = true;
19992                 delete this.dragOvers[i];
19993             }
19994
19995             for (var sGroup in dc.groups) {
19996
19997                 if ("string" != typeof sGroup) {
19998                     continue;
19999                 }
20000
20001                 for (i in this.ids[sGroup]) {
20002                     var oDD = this.ids[sGroup][i];
20003                     if (! this.isTypeOfDD(oDD)) {
20004                         continue;
20005                     }
20006
20007                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
20008                         if (this.isOverTarget(pt, oDD, this.mode)) {
20009                             // look for drop interactions
20010                             if (isDrop) {
20011                                 dropEvts.push( oDD );
20012                             // look for drag enter and drag over interactions
20013                             } else {
20014
20015                                 // initial drag over: dragEnter fires
20016                                 if (!oldOvers[oDD.id]) {
20017                                     enterEvts.push( oDD );
20018                                 // subsequent drag overs: dragOver fires
20019                                 } else {
20020                                     overEvts.push( oDD );
20021                                 }
20022
20023                                 this.dragOvers[oDD.id] = oDD;
20024                             }
20025                         }
20026                     }
20027                 }
20028             }
20029
20030             if (this.mode) {
20031                 if (outEvts.length) {
20032                     dc.b4DragOut(e, outEvts);
20033                     dc.onDragOut(e, outEvts);
20034                 }
20035
20036                 if (enterEvts.length) {
20037                     dc.onDragEnter(e, enterEvts);
20038                 }
20039
20040                 if (overEvts.length) {
20041                     dc.b4DragOver(e, overEvts);
20042                     dc.onDragOver(e, overEvts);
20043                 }
20044
20045                 if (dropEvts.length) {
20046                     dc.b4DragDrop(e, dropEvts);
20047                     dc.onDragDrop(e, dropEvts);
20048                 }
20049
20050             } else {
20051                 // fire dragout events
20052                 var len = 0;
20053                 for (i=0, len=outEvts.length; i<len; ++i) {
20054                     dc.b4DragOut(e, outEvts[i].id);
20055                     dc.onDragOut(e, outEvts[i].id);
20056                 }
20057
20058                 // fire enter events
20059                 for (i=0,len=enterEvts.length; i<len; ++i) {
20060                     // dc.b4DragEnter(e, oDD.id);
20061                     dc.onDragEnter(e, enterEvts[i].id);
20062                 }
20063
20064                 // fire over events
20065                 for (i=0,len=overEvts.length; i<len; ++i) {
20066                     dc.b4DragOver(e, overEvts[i].id);
20067                     dc.onDragOver(e, overEvts[i].id);
20068                 }
20069
20070                 // fire drop events
20071                 for (i=0, len=dropEvts.length; i<len; ++i) {
20072                     dc.b4DragDrop(e, dropEvts[i].id);
20073                     dc.onDragDrop(e, dropEvts[i].id);
20074                 }
20075
20076             }
20077
20078             // notify about a drop that did not find a target
20079             if (isDrop && !dropEvts.length) {
20080                 dc.onInvalidDrop(e);
20081             }
20082
20083         },
20084
20085         /**
20086          * Helper function for getting the best match from the list of drag
20087          * and drop objects returned by the drag and drop events when we are
20088          * in INTERSECT mode.  It returns either the first object that the
20089          * cursor is over, or the object that has the greatest overlap with
20090          * the dragged element.
20091          * @method getBestMatch
20092          * @param  {DragDrop[]} dds The array of drag and drop objects
20093          * targeted
20094          * @return {DragDrop}       The best single match
20095          * @static
20096          */
20097         getBestMatch: function(dds) {
20098             var winner = null;
20099             // Return null if the input is not what we expect
20100             //if (!dds || !dds.length || dds.length == 0) {
20101                // winner = null;
20102             // If there is only one item, it wins
20103             //} else if (dds.length == 1) {
20104
20105             var len = dds.length;
20106
20107             if (len == 1) {
20108                 winner = dds[0];
20109             } else {
20110                 // Loop through the targeted items
20111                 for (var i=0; i<len; ++i) {
20112                     var dd = dds[i];
20113                     // If the cursor is over the object, it wins.  If the
20114                     // cursor is over multiple matches, the first one we come
20115                     // to wins.
20116                     if (dd.cursorIsOver) {
20117                         winner = dd;
20118                         break;
20119                     // Otherwise the object with the most overlap wins
20120                     } else {
20121                         if (!winner ||
20122                             winner.overlap.getArea() < dd.overlap.getArea()) {
20123                             winner = dd;
20124                         }
20125                     }
20126                 }
20127             }
20128
20129             return winner;
20130         },
20131
20132         /**
20133          * Refreshes the cache of the top-left and bottom-right points of the
20134          * drag and drop objects in the specified group(s).  This is in the
20135          * format that is stored in the drag and drop instance, so typical
20136          * usage is:
20137          * <code>
20138          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
20139          * </code>
20140          * Alternatively:
20141          * <code>
20142          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
20143          * </code>
20144          * @TODO this really should be an indexed array.  Alternatively this
20145          * method could accept both.
20146          * @method refreshCache
20147          * @param {Object} groups an associative array of groups to refresh
20148          * @static
20149          */
20150         refreshCache: function(groups) {
20151             for (var sGroup in groups) {
20152                 if ("string" != typeof sGroup) {
20153                     continue;
20154                 }
20155                 for (var i in this.ids[sGroup]) {
20156                     var oDD = this.ids[sGroup][i];
20157
20158                     if (this.isTypeOfDD(oDD)) {
20159                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
20160                         var loc = this.getLocation(oDD);
20161                         if (loc) {
20162                             this.locationCache[oDD.id] = loc;
20163                         } else {
20164                             delete this.locationCache[oDD.id];
20165                             // this will unregister the drag and drop object if
20166                             // the element is not in a usable state
20167                             // oDD.unreg();
20168                         }
20169                     }
20170                 }
20171             }
20172         },
20173
20174         /**
20175          * This checks to make sure an element exists and is in the DOM.  The
20176          * main purpose is to handle cases where innerHTML is used to remove
20177          * drag and drop objects from the DOM.  IE provides an 'unspecified
20178          * error' when trying to access the offsetParent of such an element
20179          * @method verifyEl
20180          * @param {HTMLElement} el the element to check
20181          * @return {boolean} true if the element looks usable
20182          * @static
20183          */
20184         verifyEl: function(el) {
20185             if (el) {
20186                 var parent;
20187                 if(Roo.isIE){
20188                     try{
20189                         parent = el.offsetParent;
20190                     }catch(e){}
20191                 }else{
20192                     parent = el.offsetParent;
20193                 }
20194                 if (parent) {
20195                     return true;
20196                 }
20197             }
20198
20199             return false;
20200         },
20201
20202         /**
20203          * Returns a Region object containing the drag and drop element's position
20204          * and size, including the padding configured for it
20205          * @method getLocation
20206          * @param {DragDrop} oDD the drag and drop object to get the
20207          *                       location for
20208          * @return {Roo.lib.Region} a Region object representing the total area
20209          *                             the element occupies, including any padding
20210          *                             the instance is configured for.
20211          * @static
20212          */
20213         getLocation: function(oDD) {
20214             if (! this.isTypeOfDD(oDD)) {
20215                 return null;
20216             }
20217
20218             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
20219
20220             try {
20221                 pos= Roo.lib.Dom.getXY(el);
20222             } catch (e) { }
20223
20224             if (!pos) {
20225                 return null;
20226             }
20227
20228             x1 = pos[0];
20229             x2 = x1 + el.offsetWidth;
20230             y1 = pos[1];
20231             y2 = y1 + el.offsetHeight;
20232
20233             t = y1 - oDD.padding[0];
20234             r = x2 + oDD.padding[1];
20235             b = y2 + oDD.padding[2];
20236             l = x1 - oDD.padding[3];
20237
20238             return new Roo.lib.Region( t, r, b, l );
20239         },
20240
20241         /**
20242          * Checks the cursor location to see if it over the target
20243          * @method isOverTarget
20244          * @param {Roo.lib.Point} pt The point to evaluate
20245          * @param {DragDrop} oTarget the DragDrop object we are inspecting
20246          * @return {boolean} true if the mouse is over the target
20247          * @private
20248          * @static
20249          */
20250         isOverTarget: function(pt, oTarget, intersect) {
20251             // use cache if available
20252             var loc = this.locationCache[oTarget.id];
20253             if (!loc || !this.useCache) {
20254                 loc = this.getLocation(oTarget);
20255                 this.locationCache[oTarget.id] = loc;
20256
20257             }
20258
20259             if (!loc) {
20260                 return false;
20261             }
20262
20263             oTarget.cursorIsOver = loc.contains( pt );
20264
20265             // DragDrop is using this as a sanity check for the initial mousedown
20266             // in this case we are done.  In POINT mode, if the drag obj has no
20267             // contraints, we are also done. Otherwise we need to evaluate the
20268             // location of the target as related to the actual location of the
20269             // dragged element.
20270             var dc = this.dragCurrent;
20271             if (!dc || !dc.getTargetCoord ||
20272                     (!intersect && !dc.constrainX && !dc.constrainY)) {
20273                 return oTarget.cursorIsOver;
20274             }
20275
20276             oTarget.overlap = null;
20277
20278             // Get the current location of the drag element, this is the
20279             // location of the mouse event less the delta that represents
20280             // where the original mousedown happened on the element.  We
20281             // need to consider constraints and ticks as well.
20282             var pos = dc.getTargetCoord(pt.x, pt.y);
20283
20284             var el = dc.getDragEl();
20285             var curRegion = new Roo.lib.Region( pos.y,
20286                                                    pos.x + el.offsetWidth,
20287                                                    pos.y + el.offsetHeight,
20288                                                    pos.x );
20289
20290             var overlap = curRegion.intersect(loc);
20291
20292             if (overlap) {
20293                 oTarget.overlap = overlap;
20294                 return (intersect) ? true : oTarget.cursorIsOver;
20295             } else {
20296                 return false;
20297             }
20298         },
20299
20300         /**
20301          * unload event handler
20302          * @method _onUnload
20303          * @private
20304          * @static
20305          */
20306         _onUnload: function(e, me) {
20307             Roo.dd.DragDropMgr.unregAll();
20308         },
20309
20310         /**
20311          * Cleans up the drag and drop events and objects.
20312          * @method unregAll
20313          * @private
20314          * @static
20315          */
20316         unregAll: function() {
20317
20318             if (this.dragCurrent) {
20319                 this.stopDrag();
20320                 this.dragCurrent = null;
20321             }
20322
20323             this._execOnAll("unreg", []);
20324
20325             for (i in this.elementCache) {
20326                 delete this.elementCache[i];
20327             }
20328
20329             this.elementCache = {};
20330             this.ids = {};
20331         },
20332
20333         /**
20334          * A cache of DOM elements
20335          * @property elementCache
20336          * @private
20337          * @static
20338          */
20339         elementCache: {},
20340
20341         /**
20342          * Get the wrapper for the DOM element specified
20343          * @method getElWrapper
20344          * @param {String} id the id of the element to get
20345          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
20346          * @private
20347          * @deprecated This wrapper isn't that useful
20348          * @static
20349          */
20350         getElWrapper: function(id) {
20351             var oWrapper = this.elementCache[id];
20352             if (!oWrapper || !oWrapper.el) {
20353                 oWrapper = this.elementCache[id] =
20354                     new this.ElementWrapper(Roo.getDom(id));
20355             }
20356             return oWrapper;
20357         },
20358
20359         /**
20360          * Returns the actual DOM element
20361          * @method getElement
20362          * @param {String} id the id of the elment to get
20363          * @return {Object} The element
20364          * @deprecated use Roo.getDom instead
20365          * @static
20366          */
20367         getElement: function(id) {
20368             return Roo.getDom(id);
20369         },
20370
20371         /**
20372          * Returns the style property for the DOM element (i.e.,
20373          * document.getElById(id).style)
20374          * @method getCss
20375          * @param {String} id the id of the elment to get
20376          * @return {Object} The style property of the element
20377          * @deprecated use Roo.getDom instead
20378          * @static
20379          */
20380         getCss: function(id) {
20381             var el = Roo.getDom(id);
20382             return (el) ? el.style : null;
20383         },
20384
20385         /**
20386          * Inner class for cached elements
20387          * @class DragDropMgr.ElementWrapper
20388          * @for DragDropMgr
20389          * @private
20390          * @deprecated
20391          */
20392         ElementWrapper: function(el) {
20393                 /**
20394                  * The element
20395                  * @property el
20396                  */
20397                 this.el = el || null;
20398                 /**
20399                  * The element id
20400                  * @property id
20401                  */
20402                 this.id = this.el && el.id;
20403                 /**
20404                  * A reference to the style property
20405                  * @property css
20406                  */
20407                 this.css = this.el && el.style;
20408             },
20409
20410         /**
20411          * Returns the X position of an html element
20412          * @method getPosX
20413          * @param el the element for which to get the position
20414          * @return {int} the X coordinate
20415          * @for DragDropMgr
20416          * @deprecated use Roo.lib.Dom.getX instead
20417          * @static
20418          */
20419         getPosX: function(el) {
20420             return Roo.lib.Dom.getX(el);
20421         },
20422
20423         /**
20424          * Returns the Y position of an html element
20425          * @method getPosY
20426          * @param el the element for which to get the position
20427          * @return {int} the Y coordinate
20428          * @deprecated use Roo.lib.Dom.getY instead
20429          * @static
20430          */
20431         getPosY: function(el) {
20432             return Roo.lib.Dom.getY(el);
20433         },
20434
20435         /**
20436          * Swap two nodes.  In IE, we use the native method, for others we
20437          * emulate the IE behavior
20438          * @method swapNode
20439          * @param n1 the first node to swap
20440          * @param n2 the other node to swap
20441          * @static
20442          */
20443         swapNode: function(n1, n2) {
20444             if (n1.swapNode) {
20445                 n1.swapNode(n2);
20446             } else {
20447                 var p = n2.parentNode;
20448                 var s = n2.nextSibling;
20449
20450                 if (s == n1) {
20451                     p.insertBefore(n1, n2);
20452                 } else if (n2 == n1.nextSibling) {
20453                     p.insertBefore(n2, n1);
20454                 } else {
20455                     n1.parentNode.replaceChild(n2, n1);
20456                     p.insertBefore(n1, s);
20457                 }
20458             }
20459         },
20460
20461         /**
20462          * Returns the current scroll position
20463          * @method getScroll
20464          * @private
20465          * @static
20466          */
20467         getScroll: function () {
20468             var t, l, dde=document.documentElement, db=document.body;
20469             if (dde && (dde.scrollTop || dde.scrollLeft)) {
20470                 t = dde.scrollTop;
20471                 l = dde.scrollLeft;
20472             } else if (db) {
20473                 t = db.scrollTop;
20474                 l = db.scrollLeft;
20475             } else {
20476
20477             }
20478             return { top: t, left: l };
20479         },
20480
20481         /**
20482          * Returns the specified element style property
20483          * @method getStyle
20484          * @param {HTMLElement} el          the element
20485          * @param {string}      styleProp   the style property
20486          * @return {string} The value of the style property
20487          * @deprecated use Roo.lib.Dom.getStyle
20488          * @static
20489          */
20490         getStyle: function(el, styleProp) {
20491             return Roo.fly(el).getStyle(styleProp);
20492         },
20493
20494         /**
20495          * Gets the scrollTop
20496          * @method getScrollTop
20497          * @return {int} the document's scrollTop
20498          * @static
20499          */
20500         getScrollTop: function () { return this.getScroll().top; },
20501
20502         /**
20503          * Gets the scrollLeft
20504          * @method getScrollLeft
20505          * @return {int} the document's scrollTop
20506          * @static
20507          */
20508         getScrollLeft: function () { return this.getScroll().left; },
20509
20510         /**
20511          * Sets the x/y position of an element to the location of the
20512          * target element.
20513          * @method moveToEl
20514          * @param {HTMLElement} moveEl      The element to move
20515          * @param {HTMLElement} targetEl    The position reference element
20516          * @static
20517          */
20518         moveToEl: function (moveEl, targetEl) {
20519             var aCoord = Roo.lib.Dom.getXY(targetEl);
20520             Roo.lib.Dom.setXY(moveEl, aCoord);
20521         },
20522
20523         /**
20524          * Numeric array sort function
20525          * @method numericSort
20526          * @static
20527          */
20528         numericSort: function(a, b) { return (a - b); },
20529
20530         /**
20531          * Internal counter
20532          * @property _timeoutCount
20533          * @private
20534          * @static
20535          */
20536         _timeoutCount: 0,
20537
20538         /**
20539          * Trying to make the load order less important.  Without this we get
20540          * an error if this file is loaded before the Event Utility.
20541          * @method _addListeners
20542          * @private
20543          * @static
20544          */
20545         _addListeners: function() {
20546             var DDM = Roo.dd.DDM;
20547             if ( Roo.lib.Event && document ) {
20548                 DDM._onLoad();
20549             } else {
20550                 if (DDM._timeoutCount > 2000) {
20551                 } else {
20552                     setTimeout(DDM._addListeners, 10);
20553                     if (document && document.body) {
20554                         DDM._timeoutCount += 1;
20555                     }
20556                 }
20557             }
20558         },
20559
20560         /**
20561          * Recursively searches the immediate parent and all child nodes for
20562          * the handle element in order to determine wheter or not it was
20563          * clicked.
20564          * @method handleWasClicked
20565          * @param node the html element to inspect
20566          * @static
20567          */
20568         handleWasClicked: function(node, id) {
20569             if (this.isHandle(id, node.id)) {
20570                 return true;
20571             } else {
20572                 // check to see if this is a text node child of the one we want
20573                 var p = node.parentNode;
20574
20575                 while (p) {
20576                     if (this.isHandle(id, p.id)) {
20577                         return true;
20578                     } else {
20579                         p = p.parentNode;
20580                     }
20581                 }
20582             }
20583
20584             return false;
20585         }
20586
20587     };
20588
20589 }();
20590
20591 // shorter alias, save a few bytes
20592 Roo.dd.DDM = Roo.dd.DragDropMgr;
20593 Roo.dd.DDM._addListeners();
20594
20595 }/*
20596  * Based on:
20597  * Ext JS Library 1.1.1
20598  * Copyright(c) 2006-2007, Ext JS, LLC.
20599  *
20600  * Originally Released Under LGPL - original licence link has changed is not relivant.
20601  *
20602  * Fork - LGPL
20603  * <script type="text/javascript">
20604  */
20605
20606 /**
20607  * @class Roo.dd.DD
20608  * A DragDrop implementation where the linked element follows the
20609  * mouse cursor during a drag.
20610  * @extends Roo.dd.DragDrop
20611  * @constructor
20612  * @param {String} id the id of the linked element
20613  * @param {String} sGroup the group of related DragDrop items
20614  * @param {object} config an object containing configurable attributes
20615  *                Valid properties for DD:
20616  *                    scroll
20617  */
20618 Roo.dd.DD = function(id, sGroup, config) {
20619     if (id) {
20620         this.init(id, sGroup, config);
20621     }
20622 };
20623
20624 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
20625
20626     /**
20627      * When set to true, the utility automatically tries to scroll the browser
20628      * window wehn a drag and drop element is dragged near the viewport boundary.
20629      * Defaults to true.
20630      * @property scroll
20631      * @type boolean
20632      */
20633     scroll: true,
20634
20635     /**
20636      * Sets the pointer offset to the distance between the linked element's top
20637      * left corner and the location the element was clicked
20638      * @method autoOffset
20639      * @param {int} iPageX the X coordinate of the click
20640      * @param {int} iPageY the Y coordinate of the click
20641      */
20642     autoOffset: function(iPageX, iPageY) {
20643         var x = iPageX - this.startPageX;
20644         var y = iPageY - this.startPageY;
20645         this.setDelta(x, y);
20646     },
20647
20648     /**
20649      * Sets the pointer offset.  You can call this directly to force the
20650      * offset to be in a particular location (e.g., pass in 0,0 to set it
20651      * to the center of the object)
20652      * @method setDelta
20653      * @param {int} iDeltaX the distance from the left
20654      * @param {int} iDeltaY the distance from the top
20655      */
20656     setDelta: function(iDeltaX, iDeltaY) {
20657         this.deltaX = iDeltaX;
20658         this.deltaY = iDeltaY;
20659     },
20660
20661     /**
20662      * Sets the drag element to the location of the mousedown or click event,
20663      * maintaining the cursor location relative to the location on the element
20664      * that was clicked.  Override this if you want to place the element in a
20665      * location other than where the cursor is.
20666      * @method setDragElPos
20667      * @param {int} iPageX the X coordinate of the mousedown or drag event
20668      * @param {int} iPageY the Y coordinate of the mousedown or drag event
20669      */
20670     setDragElPos: function(iPageX, iPageY) {
20671         // the first time we do this, we are going to check to make sure
20672         // the element has css positioning
20673
20674         var el = this.getDragEl();
20675         this.alignElWithMouse(el, iPageX, iPageY);
20676     },
20677
20678     /**
20679      * Sets the element to the location of the mousedown or click event,
20680      * maintaining the cursor location relative to the location on the element
20681      * that was clicked.  Override this if you want to place the element in a
20682      * location other than where the cursor is.
20683      * @method alignElWithMouse
20684      * @param {HTMLElement} el the element to move
20685      * @param {int} iPageX the X coordinate of the mousedown or drag event
20686      * @param {int} iPageY the Y coordinate of the mousedown or drag event
20687      */
20688     alignElWithMouse: function(el, iPageX, iPageY) {
20689         var oCoord = this.getTargetCoord(iPageX, iPageY);
20690         var fly = el.dom ? el : Roo.fly(el);
20691         if (!this.deltaSetXY) {
20692             var aCoord = [oCoord.x, oCoord.y];
20693             fly.setXY(aCoord);
20694             var newLeft = fly.getLeft(true);
20695             var newTop  = fly.getTop(true);
20696             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
20697         } else {
20698             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
20699         }
20700
20701         this.cachePosition(oCoord.x, oCoord.y);
20702         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
20703         return oCoord;
20704     },
20705
20706     /**
20707      * Saves the most recent position so that we can reset the constraints and
20708      * tick marks on-demand.  We need to know this so that we can calculate the
20709      * number of pixels the element is offset from its original position.
20710      * @method cachePosition
20711      * @param iPageX the current x position (optional, this just makes it so we
20712      * don't have to look it up again)
20713      * @param iPageY the current y position (optional, this just makes it so we
20714      * don't have to look it up again)
20715      */
20716     cachePosition: function(iPageX, iPageY) {
20717         if (iPageX) {
20718             this.lastPageX = iPageX;
20719             this.lastPageY = iPageY;
20720         } else {
20721             var aCoord = Roo.lib.Dom.getXY(this.getEl());
20722             this.lastPageX = aCoord[0];
20723             this.lastPageY = aCoord[1];
20724         }
20725     },
20726
20727     /**
20728      * Auto-scroll the window if the dragged object has been moved beyond the
20729      * visible window boundary.
20730      * @method autoScroll
20731      * @param {int} x the drag element's x position
20732      * @param {int} y the drag element's y position
20733      * @param {int} h the height of the drag element
20734      * @param {int} w the width of the drag element
20735      * @private
20736      */
20737     autoScroll: function(x, y, h, w) {
20738
20739         if (this.scroll) {
20740             // The client height
20741             var clientH = Roo.lib.Dom.getViewWidth();
20742
20743             // The client width
20744             var clientW = Roo.lib.Dom.getViewHeight();
20745
20746             // The amt scrolled down
20747             var st = this.DDM.getScrollTop();
20748
20749             // The amt scrolled right
20750             var sl = this.DDM.getScrollLeft();
20751
20752             // Location of the bottom of the element
20753             var bot = h + y;
20754
20755             // Location of the right of the element
20756             var right = w + x;
20757
20758             // The distance from the cursor to the bottom of the visible area,
20759             // adjusted so that we don't scroll if the cursor is beyond the
20760             // element drag constraints
20761             var toBot = (clientH + st - y - this.deltaY);
20762
20763             // The distance from the cursor to the right of the visible area
20764             var toRight = (clientW + sl - x - this.deltaX);
20765
20766
20767             // How close to the edge the cursor must be before we scroll
20768             // var thresh = (document.all) ? 100 : 40;
20769             var thresh = 40;
20770
20771             // How many pixels to scroll per autoscroll op.  This helps to reduce
20772             // clunky scrolling. IE is more sensitive about this ... it needs this
20773             // value to be higher.
20774             var scrAmt = (document.all) ? 80 : 30;
20775
20776             // Scroll down if we are near the bottom of the visible page and the
20777             // obj extends below the crease
20778             if ( bot > clientH && toBot < thresh ) {
20779                 window.scrollTo(sl, st + scrAmt);
20780             }
20781
20782             // Scroll up if the window is scrolled down and the top of the object
20783             // goes above the top border
20784             if ( y < st && st > 0 && y - st < thresh ) {
20785                 window.scrollTo(sl, st - scrAmt);
20786             }
20787
20788             // Scroll right if the obj is beyond the right border and the cursor is
20789             // near the border.
20790             if ( right > clientW && toRight < thresh ) {
20791                 window.scrollTo(sl + scrAmt, st);
20792             }
20793
20794             // Scroll left if the window has been scrolled to the right and the obj
20795             // extends past the left border
20796             if ( x < sl && sl > 0 && x - sl < thresh ) {
20797                 window.scrollTo(sl - scrAmt, st);
20798             }
20799         }
20800     },
20801
20802     /**
20803      * Finds the location the element should be placed if we want to move
20804      * it to where the mouse location less the click offset would place us.
20805      * @method getTargetCoord
20806      * @param {int} iPageX the X coordinate of the click
20807      * @param {int} iPageY the Y coordinate of the click
20808      * @return an object that contains the coordinates (Object.x and Object.y)
20809      * @private
20810      */
20811     getTargetCoord: function(iPageX, iPageY) {
20812
20813
20814         var x = iPageX - this.deltaX;
20815         var y = iPageY - this.deltaY;
20816
20817         if (this.constrainX) {
20818             if (x < this.minX) { x = this.minX; }
20819             if (x > this.maxX) { x = this.maxX; }
20820         }
20821
20822         if (this.constrainY) {
20823             if (y < this.minY) { y = this.minY; }
20824             if (y > this.maxY) { y = this.maxY; }
20825         }
20826
20827         x = this.getTick(x, this.xTicks);
20828         y = this.getTick(y, this.yTicks);
20829
20830
20831         return {x:x, y:y};
20832     },
20833
20834     /*
20835      * Sets up config options specific to this class. Overrides
20836      * Roo.dd.DragDrop, but all versions of this method through the
20837      * inheritance chain are called
20838      */
20839     applyConfig: function() {
20840         Roo.dd.DD.superclass.applyConfig.call(this);
20841         this.scroll = (this.config.scroll !== false);
20842     },
20843
20844     /*
20845      * Event that fires prior to the onMouseDown event.  Overrides
20846      * Roo.dd.DragDrop.
20847      */
20848     b4MouseDown: function(e) {
20849         // this.resetConstraints();
20850         this.autoOffset(e.getPageX(),
20851                             e.getPageY());
20852     },
20853
20854     /*
20855      * Event that fires prior to the onDrag event.  Overrides
20856      * Roo.dd.DragDrop.
20857      */
20858     b4Drag: function(e) {
20859         this.setDragElPos(e.getPageX(),
20860                             e.getPageY());
20861     },
20862
20863     toString: function() {
20864         return ("DD " + this.id);
20865     }
20866
20867     //////////////////////////////////////////////////////////////////////////
20868     // Debugging ygDragDrop events that can be overridden
20869     //////////////////////////////////////////////////////////////////////////
20870     /*
20871     startDrag: function(x, y) {
20872     },
20873
20874     onDrag: function(e) {
20875     },
20876
20877     onDragEnter: function(e, id) {
20878     },
20879
20880     onDragOver: function(e, id) {
20881     },
20882
20883     onDragOut: function(e, id) {
20884     },
20885
20886     onDragDrop: function(e, id) {
20887     },
20888
20889     endDrag: function(e) {
20890     }
20891
20892     */
20893
20894 });/*
20895  * Based on:
20896  * Ext JS Library 1.1.1
20897  * Copyright(c) 2006-2007, Ext JS, LLC.
20898  *
20899  * Originally Released Under LGPL - original licence link has changed is not relivant.
20900  *
20901  * Fork - LGPL
20902  * <script type="text/javascript">
20903  */
20904
20905 /**
20906  * @class Roo.dd.DDProxy
20907  * A DragDrop implementation that inserts an empty, bordered div into
20908  * the document that follows the cursor during drag operations.  At the time of
20909  * the click, the frame div is resized to the dimensions of the linked html
20910  * element, and moved to the exact location of the linked element.
20911  *
20912  * References to the "frame" element refer to the single proxy element that
20913  * was created to be dragged in place of all DDProxy elements on the
20914  * page.
20915  *
20916  * @extends Roo.dd.DD
20917  * @constructor
20918  * @param {String} id the id of the linked html element
20919  * @param {String} sGroup the group of related DragDrop objects
20920  * @param {object} config an object containing configurable attributes
20921  *                Valid properties for DDProxy in addition to those in DragDrop:
20922  *                   resizeFrame, centerFrame, dragElId
20923  */
20924 Roo.dd.DDProxy = function(id, sGroup, config) {
20925     if (id) {
20926         this.init(id, sGroup, config);
20927         this.initFrame();
20928     }
20929 };
20930
20931 /**
20932  * The default drag frame div id
20933  * @property Roo.dd.DDProxy.dragElId
20934  * @type String
20935  * @static
20936  */
20937 Roo.dd.DDProxy.dragElId = "ygddfdiv";
20938
20939 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
20940
20941     /**
20942      * By default we resize the drag frame to be the same size as the element
20943      * we want to drag (this is to get the frame effect).  We can turn it off
20944      * if we want a different behavior.
20945      * @property resizeFrame
20946      * @type boolean
20947      */
20948     resizeFrame: true,
20949
20950     /**
20951      * By default the frame is positioned exactly where the drag element is, so
20952      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
20953      * you do not have constraints on the obj is to have the drag frame centered
20954      * around the cursor.  Set centerFrame to true for this effect.
20955      * @property centerFrame
20956      * @type boolean
20957      */
20958     centerFrame: false,
20959
20960     /**
20961      * Creates the proxy element if it does not yet exist
20962      * @method createFrame
20963      */
20964     createFrame: function() {
20965         var self = this;
20966         var body = document.body;
20967
20968         if (!body || !body.firstChild) {
20969             setTimeout( function() { self.createFrame(); }, 50 );
20970             return;
20971         }
20972
20973         var div = this.getDragEl();
20974
20975         if (!div) {
20976             div    = document.createElement("div");
20977             div.id = this.dragElId;
20978             var s  = div.style;
20979
20980             s.position   = "absolute";
20981             s.visibility = "hidden";
20982             s.cursor     = "move";
20983             s.border     = "2px solid #aaa";
20984             s.zIndex     = 999;
20985
20986             // appendChild can blow up IE if invoked prior to the window load event
20987             // while rendering a table.  It is possible there are other scenarios
20988             // that would cause this to happen as well.
20989             body.insertBefore(div, body.firstChild);
20990         }
20991     },
20992
20993     /**
20994      * Initialization for the drag frame element.  Must be called in the
20995      * constructor of all subclasses
20996      * @method initFrame
20997      */
20998     initFrame: function() {
20999         this.createFrame();
21000     },
21001
21002     applyConfig: function() {
21003         Roo.dd.DDProxy.superclass.applyConfig.call(this);
21004
21005         this.resizeFrame = (this.config.resizeFrame !== false);
21006         this.centerFrame = (this.config.centerFrame);
21007         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
21008     },
21009
21010     /**
21011      * Resizes the drag frame to the dimensions of the clicked object, positions
21012      * it over the object, and finally displays it
21013      * @method showFrame
21014      * @param {int} iPageX X click position
21015      * @param {int} iPageY Y click position
21016      * @private
21017      */
21018     showFrame: function(iPageX, iPageY) {
21019         var el = this.getEl();
21020         var dragEl = this.getDragEl();
21021         var s = dragEl.style;
21022
21023         this._resizeProxy();
21024
21025         if (this.centerFrame) {
21026             this.setDelta( Math.round(parseInt(s.width,  10)/2),
21027                            Math.round(parseInt(s.height, 10)/2) );
21028         }
21029
21030         this.setDragElPos(iPageX, iPageY);
21031
21032         Roo.fly(dragEl).show();
21033     },
21034
21035     /**
21036      * The proxy is automatically resized to the dimensions of the linked
21037      * element when a drag is initiated, unless resizeFrame is set to false
21038      * @method _resizeProxy
21039      * @private
21040      */
21041     _resizeProxy: function() {
21042         if (this.resizeFrame) {
21043             var el = this.getEl();
21044             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
21045         }
21046     },
21047
21048     // overrides Roo.dd.DragDrop
21049     b4MouseDown: function(e) {
21050         var x = e.getPageX();
21051         var y = e.getPageY();
21052         this.autoOffset(x, y);
21053         this.setDragElPos(x, y);
21054     },
21055
21056     // overrides Roo.dd.DragDrop
21057     b4StartDrag: function(x, y) {
21058         // show the drag frame
21059         this.showFrame(x, y);
21060     },
21061
21062     // overrides Roo.dd.DragDrop
21063     b4EndDrag: function(e) {
21064         Roo.fly(this.getDragEl()).hide();
21065     },
21066
21067     // overrides Roo.dd.DragDrop
21068     // By default we try to move the element to the last location of the frame.
21069     // This is so that the default behavior mirrors that of Roo.dd.DD.
21070     endDrag: function(e) {
21071
21072         var lel = this.getEl();
21073         var del = this.getDragEl();
21074
21075         // Show the drag frame briefly so we can get its position
21076         del.style.visibility = "";
21077
21078         this.beforeMove();
21079         // Hide the linked element before the move to get around a Safari
21080         // rendering bug.
21081         lel.style.visibility = "hidden";
21082         Roo.dd.DDM.moveToEl(lel, del);
21083         del.style.visibility = "hidden";
21084         lel.style.visibility = "";
21085
21086         this.afterDrag();
21087     },
21088
21089     beforeMove : function(){
21090
21091     },
21092
21093     afterDrag : function(){
21094
21095     },
21096
21097     toString: function() {
21098         return ("DDProxy " + this.id);
21099     }
21100
21101 });
21102 /*
21103  * Based on:
21104  * Ext JS Library 1.1.1
21105  * Copyright(c) 2006-2007, Ext JS, LLC.
21106  *
21107  * Originally Released Under LGPL - original licence link has changed is not relivant.
21108  *
21109  * Fork - LGPL
21110  * <script type="text/javascript">
21111  */
21112
21113  /**
21114  * @class Roo.dd.DDTarget
21115  * A DragDrop implementation that does not move, but can be a drop
21116  * target.  You would get the same result by simply omitting implementation
21117  * for the event callbacks, but this way we reduce the processing cost of the
21118  * event listener and the callbacks.
21119  * @extends Roo.dd.DragDrop
21120  * @constructor
21121  * @param {String} id the id of the element that is a drop target
21122  * @param {String} sGroup the group of related DragDrop objects
21123  * @param {object} config an object containing configurable attributes
21124  *                 Valid properties for DDTarget in addition to those in
21125  *                 DragDrop:
21126  *                    none
21127  */
21128 Roo.dd.DDTarget = function(id, sGroup, config) {
21129     if (id) {
21130         this.initTarget(id, sGroup, config);
21131     }
21132     if (config && (config.listeners || config.events)) { 
21133         Roo.dd.DragDrop.superclass.constructor.call(this,  { 
21134             listeners : config.listeners || {}, 
21135             events : config.events || {} 
21136         });    
21137     }
21138 };
21139
21140 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
21141 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
21142     toString: function() {
21143         return ("DDTarget " + this.id);
21144     }
21145 });
21146 /*
21147  * Based on:
21148  * Ext JS Library 1.1.1
21149  * Copyright(c) 2006-2007, Ext JS, LLC.
21150  *
21151  * Originally Released Under LGPL - original licence link has changed is not relivant.
21152  *
21153  * Fork - LGPL
21154  * <script type="text/javascript">
21155  */
21156  
21157
21158 /**
21159  * @class Roo.dd.ScrollManager
21160  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
21161  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
21162  * @singleton
21163  */
21164 Roo.dd.ScrollManager = function(){
21165     var ddm = Roo.dd.DragDropMgr;
21166     var els = {};
21167     var dragEl = null;
21168     var proc = {};
21169     
21170     
21171     
21172     var onStop = function(e){
21173         dragEl = null;
21174         clearProc();
21175     };
21176     
21177     var triggerRefresh = function(){
21178         if(ddm.dragCurrent){
21179              ddm.refreshCache(ddm.dragCurrent.groups);
21180         }
21181     };
21182     
21183     var doScroll = function(){
21184         if(ddm.dragCurrent){
21185             var dds = Roo.dd.ScrollManager;
21186             if(!dds.animate){
21187                 if(proc.el.scroll(proc.dir, dds.increment)){
21188                     triggerRefresh();
21189                 }
21190             }else{
21191                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
21192             }
21193         }
21194     };
21195     
21196     var clearProc = function(){
21197         if(proc.id){
21198             clearInterval(proc.id);
21199         }
21200         proc.id = 0;
21201         proc.el = null;
21202         proc.dir = "";
21203     };
21204     
21205     var startProc = function(el, dir){
21206          Roo.log('scroll startproc');
21207         clearProc();
21208         proc.el = el;
21209         proc.dir = dir;
21210         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
21211     };
21212     
21213     var onFire = function(e, isDrop){
21214        
21215         if(isDrop || !ddm.dragCurrent){ return; }
21216         var dds = Roo.dd.ScrollManager;
21217         if(!dragEl || dragEl != ddm.dragCurrent){
21218             dragEl = ddm.dragCurrent;
21219             // refresh regions on drag start
21220             dds.refreshCache();
21221         }
21222         
21223         var xy = Roo.lib.Event.getXY(e);
21224         var pt = new Roo.lib.Point(xy[0], xy[1]);
21225         for(var id in els){
21226             var el = els[id], r = el._region;
21227             if(r && r.contains(pt) && el.isScrollable()){
21228                 if(r.bottom - pt.y <= dds.thresh){
21229                     if(proc.el != el){
21230                         startProc(el, "down");
21231                     }
21232                     return;
21233                 }else if(r.right - pt.x <= dds.thresh){
21234                     if(proc.el != el){
21235                         startProc(el, "left");
21236                     }
21237                     return;
21238                 }else if(pt.y - r.top <= dds.thresh){
21239                     if(proc.el != el){
21240                         startProc(el, "up");
21241                     }
21242                     return;
21243                 }else if(pt.x - r.left <= dds.thresh){
21244                     if(proc.el != el){
21245                         startProc(el, "right");
21246                     }
21247                     return;
21248                 }
21249             }
21250         }
21251         clearProc();
21252     };
21253     
21254     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
21255     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
21256     
21257     return {
21258         /**
21259          * Registers new overflow element(s) to auto scroll
21260          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
21261          */
21262         register : function(el){
21263             if(el instanceof Array){
21264                 for(var i = 0, len = el.length; i < len; i++) {
21265                         this.register(el[i]);
21266                 }
21267             }else{
21268                 el = Roo.get(el);
21269                 els[el.id] = el;
21270             }
21271             Roo.dd.ScrollManager.els = els;
21272         },
21273         
21274         /**
21275          * Unregisters overflow element(s) so they are no longer scrolled
21276          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
21277          */
21278         unregister : function(el){
21279             if(el instanceof Array){
21280                 for(var i = 0, len = el.length; i < len; i++) {
21281                         this.unregister(el[i]);
21282                 }
21283             }else{
21284                 el = Roo.get(el);
21285                 delete els[el.id];
21286             }
21287         },
21288         
21289         /**
21290          * The number of pixels from the edge of a container the pointer needs to be to 
21291          * trigger scrolling (defaults to 25)
21292          * @type Number
21293          */
21294         thresh : 25,
21295         
21296         /**
21297          * The number of pixels to scroll in each scroll increment (defaults to 50)
21298          * @type Number
21299          */
21300         increment : 100,
21301         
21302         /**
21303          * The frequency of scrolls in milliseconds (defaults to 500)
21304          * @type Number
21305          */
21306         frequency : 500,
21307         
21308         /**
21309          * True to animate the scroll (defaults to true)
21310          * @type Boolean
21311          */
21312         animate: true,
21313         
21314         /**
21315          * The animation duration in seconds - 
21316          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
21317          * @type Number
21318          */
21319         animDuration: .4,
21320         
21321         /**
21322          * Manually trigger a cache refresh.
21323          */
21324         refreshCache : function(){
21325             for(var id in els){
21326                 if(typeof els[id] == 'object'){ // for people extending the object prototype
21327                     els[id]._region = els[id].getRegion();
21328                 }
21329             }
21330         }
21331     };
21332 }();/*
21333  * Based on:
21334  * Ext JS Library 1.1.1
21335  * Copyright(c) 2006-2007, Ext JS, LLC.
21336  *
21337  * Originally Released Under LGPL - original licence link has changed is not relivant.
21338  *
21339  * Fork - LGPL
21340  * <script type="text/javascript">
21341  */
21342  
21343
21344 /**
21345  * @class Roo.dd.Registry
21346  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
21347  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
21348  * @singleton
21349  */
21350 Roo.dd.Registry = function(){
21351     var elements = {}; 
21352     var handles = {}; 
21353     var autoIdSeed = 0;
21354
21355     var getId = function(el, autogen){
21356         if(typeof el == "string"){
21357             return el;
21358         }
21359         var id = el.id;
21360         if(!id && autogen !== false){
21361             id = "roodd-" + (++autoIdSeed);
21362             el.id = id;
21363         }
21364         return id;
21365     };
21366     
21367     return {
21368     /**
21369      * Register a drag drop element
21370      * @param {String|HTMLElement} element The id or DOM node to register
21371      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
21372      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
21373      * knows how to interpret, plus there are some specific properties known to the Registry that should be
21374      * populated in the data object (if applicable):
21375      * <pre>
21376 Value      Description<br />
21377 ---------  ------------------------------------------<br />
21378 handles    Array of DOM nodes that trigger dragging<br />
21379            for the element being registered<br />
21380 isHandle   True if the element passed in triggers<br />
21381            dragging itself, else false
21382 </pre>
21383      */
21384         register : function(el, data){
21385             data = data || {};
21386             if(typeof el == "string"){
21387                 el = document.getElementById(el);
21388             }
21389             data.ddel = el;
21390             elements[getId(el)] = data;
21391             if(data.isHandle !== false){
21392                 handles[data.ddel.id] = data;
21393             }
21394             if(data.handles){
21395                 var hs = data.handles;
21396                 for(var i = 0, len = hs.length; i < len; i++){
21397                         handles[getId(hs[i])] = data;
21398                 }
21399             }
21400         },
21401
21402     /**
21403      * Unregister a drag drop element
21404      * @param {String|HTMLElement}  element The id or DOM node to unregister
21405      */
21406         unregister : function(el){
21407             var id = getId(el, false);
21408             var data = elements[id];
21409             if(data){
21410                 delete elements[id];
21411                 if(data.handles){
21412                     var hs = data.handles;
21413                     for(var i = 0, len = hs.length; i < len; i++){
21414                         delete handles[getId(hs[i], false)];
21415                     }
21416                 }
21417             }
21418         },
21419
21420     /**
21421      * Returns the handle registered for a DOM Node by id
21422      * @param {String|HTMLElement} id The DOM node or id to look up
21423      * @return {Object} handle The custom handle data
21424      */
21425         getHandle : function(id){
21426             if(typeof id != "string"){ // must be element?
21427                 id = id.id;
21428             }
21429             return handles[id];
21430         },
21431
21432     /**
21433      * Returns the handle that is registered for the DOM node that is the target of the event
21434      * @param {Event} e The event
21435      * @return {Object} handle The custom handle data
21436      */
21437         getHandleFromEvent : function(e){
21438             var t = Roo.lib.Event.getTarget(e);
21439             return t ? handles[t.id] : null;
21440         },
21441
21442     /**
21443      * Returns a custom data object that is registered for a DOM node by id
21444      * @param {String|HTMLElement} id The DOM node or id to look up
21445      * @return {Object} data The custom data
21446      */
21447         getTarget : function(id){
21448             if(typeof id != "string"){ // must be element?
21449                 id = id.id;
21450             }
21451             return elements[id];
21452         },
21453
21454     /**
21455      * Returns a custom data object that is registered for the DOM node that is the target of the event
21456      * @param {Event} e The event
21457      * @return {Object} data The custom data
21458      */
21459         getTargetFromEvent : function(e){
21460             var t = Roo.lib.Event.getTarget(e);
21461             return t ? elements[t.id] || handles[t.id] : null;
21462         }
21463     };
21464 }();/*
21465  * Based on:
21466  * Ext JS Library 1.1.1
21467  * Copyright(c) 2006-2007, Ext JS, LLC.
21468  *
21469  * Originally Released Under LGPL - original licence link has changed is not relivant.
21470  *
21471  * Fork - LGPL
21472  * <script type="text/javascript">
21473  */
21474  
21475
21476 /**
21477  * @class Roo.dd.StatusProxy
21478  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
21479  * default drag proxy used by all Roo.dd components.
21480  * @constructor
21481  * @param {Object} config
21482  */
21483 Roo.dd.StatusProxy = function(config){
21484     Roo.apply(this, config);
21485     this.id = this.id || Roo.id();
21486     this.el = new Roo.Layer({
21487         dh: {
21488             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
21489                 {tag: "div", cls: "x-dd-drop-icon"},
21490                 {tag: "div", cls: "x-dd-drag-ghost"}
21491             ]
21492         }, 
21493         shadow: !config || config.shadow !== false
21494     });
21495     this.ghost = Roo.get(this.el.dom.childNodes[1]);
21496     this.dropStatus = this.dropNotAllowed;
21497 };
21498
21499 Roo.dd.StatusProxy.prototype = {
21500     /**
21501      * @cfg {String} dropAllowed
21502      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
21503      */
21504     dropAllowed : "x-dd-drop-ok",
21505     /**
21506      * @cfg {String} dropNotAllowed
21507      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
21508      */
21509     dropNotAllowed : "x-dd-drop-nodrop",
21510
21511     /**
21512      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
21513      * over the current target element.
21514      * @param {String} cssClass The css class for the new drop status indicator image
21515      */
21516     setStatus : function(cssClass){
21517         cssClass = cssClass || this.dropNotAllowed;
21518         if(this.dropStatus != cssClass){
21519             this.el.replaceClass(this.dropStatus, cssClass);
21520             this.dropStatus = cssClass;
21521         }
21522     },
21523
21524     /**
21525      * Resets the status indicator to the default dropNotAllowed value
21526      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
21527      */
21528     reset : function(clearGhost){
21529         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
21530         this.dropStatus = this.dropNotAllowed;
21531         if(clearGhost){
21532             this.ghost.update("");
21533         }
21534     },
21535
21536     /**
21537      * Updates the contents of the ghost element
21538      * @param {String} html The html that will replace the current innerHTML of the ghost element
21539      */
21540     update : function(html){
21541         if(typeof html == "string"){
21542             this.ghost.update(html);
21543         }else{
21544             this.ghost.update("");
21545             html.style.margin = "0";
21546             this.ghost.dom.appendChild(html);
21547         }
21548         // ensure float = none set?? cant remember why though.
21549         var el = this.ghost.dom.firstChild;
21550                 if(el){
21551                         Roo.fly(el).setStyle('float', 'none');
21552                 }
21553     },
21554     
21555     /**
21556      * Returns the underlying proxy {@link Roo.Layer}
21557      * @return {Roo.Layer} el
21558     */
21559     getEl : function(){
21560         return this.el;
21561     },
21562
21563     /**
21564      * Returns the ghost element
21565      * @return {Roo.Element} el
21566      */
21567     getGhost : function(){
21568         return this.ghost;
21569     },
21570
21571     /**
21572      * Hides the proxy
21573      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
21574      */
21575     hide : function(clear){
21576         this.el.hide();
21577         if(clear){
21578             this.reset(true);
21579         }
21580     },
21581
21582     /**
21583      * Stops the repair animation if it's currently running
21584      */
21585     stop : function(){
21586         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
21587             this.anim.stop();
21588         }
21589     },
21590
21591     /**
21592      * Displays this proxy
21593      */
21594     show : function(){
21595         this.el.show();
21596     },
21597
21598     /**
21599      * Force the Layer to sync its shadow and shim positions to the element
21600      */
21601     sync : function(){
21602         this.el.sync();
21603     },
21604
21605     /**
21606      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
21607      * invalid drop operation by the item being dragged.
21608      * @param {Array} xy The XY position of the element ([x, y])
21609      * @param {Function} callback The function to call after the repair is complete
21610      * @param {Object} scope The scope in which to execute the callback
21611      */
21612     repair : function(xy, callback, scope){
21613         this.callback = callback;
21614         this.scope = scope;
21615         if(xy && this.animRepair !== false){
21616             this.el.addClass("x-dd-drag-repair");
21617             this.el.hideUnders(true);
21618             this.anim = this.el.shift({
21619                 duration: this.repairDuration || .5,
21620                 easing: 'easeOut',
21621                 xy: xy,
21622                 stopFx: true,
21623                 callback: this.afterRepair,
21624                 scope: this
21625             });
21626         }else{
21627             this.afterRepair();
21628         }
21629     },
21630
21631     // private
21632     afterRepair : function(){
21633         this.hide(true);
21634         if(typeof this.callback == "function"){
21635             this.callback.call(this.scope || this);
21636         }
21637         this.callback = null;
21638         this.scope = null;
21639     }
21640 };/*
21641  * Based on:
21642  * Ext JS Library 1.1.1
21643  * Copyright(c) 2006-2007, Ext JS, LLC.
21644  *
21645  * Originally Released Under LGPL - original licence link has changed is not relivant.
21646  *
21647  * Fork - LGPL
21648  * <script type="text/javascript">
21649  */
21650
21651 /**
21652  * @class Roo.dd.DragSource
21653  * @extends Roo.dd.DDProxy
21654  * A simple class that provides the basic implementation needed to make any element draggable.
21655  * @constructor
21656  * @param {String/HTMLElement/Element} el The container element
21657  * @param {Object} config
21658  */
21659 Roo.dd.DragSource = function(el, config){
21660     this.el = Roo.get(el);
21661     this.dragData = {};
21662     
21663     Roo.apply(this, config);
21664     
21665     if(!this.proxy){
21666         this.proxy = new Roo.dd.StatusProxy();
21667     }
21668
21669     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
21670           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
21671     
21672     this.dragging = false;
21673 };
21674
21675 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
21676     /**
21677      * @cfg {String} dropAllowed
21678      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
21679      */
21680     dropAllowed : "x-dd-drop-ok",
21681     /**
21682      * @cfg {String} dropNotAllowed
21683      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
21684      */
21685     dropNotAllowed : "x-dd-drop-nodrop",
21686
21687     /**
21688      * Returns the data object associated with this drag source
21689      * @return {Object} data An object containing arbitrary data
21690      */
21691     getDragData : function(e){
21692         return this.dragData;
21693     },
21694
21695     // private
21696     onDragEnter : function(e, id){
21697         var target = Roo.dd.DragDropMgr.getDDById(id);
21698         this.cachedTarget = target;
21699         if(this.beforeDragEnter(target, e, id) !== false){
21700             if(target.isNotifyTarget){
21701                 var status = target.notifyEnter(this, e, this.dragData);
21702                 this.proxy.setStatus(status);
21703             }else{
21704                 this.proxy.setStatus(this.dropAllowed);
21705             }
21706             
21707             if(this.afterDragEnter){
21708                 /**
21709                  * An empty function by default, but provided so that you can perform a custom action
21710                  * when the dragged item enters the drop target by providing an implementation.
21711                  * @param {Roo.dd.DragDrop} target The drop target
21712                  * @param {Event} e The event object
21713                  * @param {String} id The id of the dragged element
21714                  * @method afterDragEnter
21715                  */
21716                 this.afterDragEnter(target, e, id);
21717             }
21718         }
21719     },
21720
21721     /**
21722      * An empty function by default, but provided so that you can perform a custom action
21723      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
21724      * @param {Roo.dd.DragDrop} target The drop target
21725      * @param {Event} e The event object
21726      * @param {String} id The id of the dragged element
21727      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21728      */
21729     beforeDragEnter : function(target, e, id){
21730         return true;
21731     },
21732
21733     // private
21734     alignElWithMouse: function() {
21735         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
21736         this.proxy.sync();
21737     },
21738
21739     // private
21740     onDragOver : function(e, id){
21741         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21742         if(this.beforeDragOver(target, e, id) !== false){
21743             if(target.isNotifyTarget){
21744                 var status = target.notifyOver(this, e, this.dragData);
21745                 this.proxy.setStatus(status);
21746             }
21747
21748             if(this.afterDragOver){
21749                 /**
21750                  * An empty function by default, but provided so that you can perform a custom action
21751                  * while the dragged item is over the drop target by providing an implementation.
21752                  * @param {Roo.dd.DragDrop} target The drop target
21753                  * @param {Event} e The event object
21754                  * @param {String} id The id of the dragged element
21755                  * @method afterDragOver
21756                  */
21757                 this.afterDragOver(target, e, id);
21758             }
21759         }
21760     },
21761
21762     /**
21763      * An empty function by default, but provided so that you can perform a custom action
21764      * while the dragged item is over the drop target and optionally cancel the onDragOver.
21765      * @param {Roo.dd.DragDrop} target The drop target
21766      * @param {Event} e The event object
21767      * @param {String} id The id of the dragged element
21768      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21769      */
21770     beforeDragOver : function(target, e, id){
21771         return true;
21772     },
21773
21774     // private
21775     onDragOut : function(e, id){
21776         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21777         if(this.beforeDragOut(target, e, id) !== false){
21778             if(target.isNotifyTarget){
21779                 target.notifyOut(this, e, this.dragData);
21780             }
21781             this.proxy.reset();
21782             if(this.afterDragOut){
21783                 /**
21784                  * An empty function by default, but provided so that you can perform a custom action
21785                  * after the dragged item is dragged out of the target without dropping.
21786                  * @param {Roo.dd.DragDrop} target The drop target
21787                  * @param {Event} e The event object
21788                  * @param {String} id The id of the dragged element
21789                  * @method afterDragOut
21790                  */
21791                 this.afterDragOut(target, e, id);
21792             }
21793         }
21794         this.cachedTarget = null;
21795     },
21796
21797     /**
21798      * An empty function by default, but provided so that you can perform a custom action before the dragged
21799      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
21800      * @param {Roo.dd.DragDrop} target The drop target
21801      * @param {Event} e The event object
21802      * @param {String} id The id of the dragged element
21803      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21804      */
21805     beforeDragOut : function(target, e, id){
21806         return true;
21807     },
21808     
21809     // private
21810     onDragDrop : function(e, id){
21811         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21812         if(this.beforeDragDrop(target, e, id) !== false){
21813             if(target.isNotifyTarget){
21814                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
21815                     this.onValidDrop(target, e, id);
21816                 }else{
21817                     this.onInvalidDrop(target, e, id);
21818                 }
21819             }else{
21820                 this.onValidDrop(target, e, id);
21821             }
21822             
21823             if(this.afterDragDrop){
21824                 /**
21825                  * An empty function by default, but provided so that you can perform a custom action
21826                  * after a valid drag drop has occurred by providing an implementation.
21827                  * @param {Roo.dd.DragDrop} target The drop target
21828                  * @param {Event} e The event object
21829                  * @param {String} id The id of the dropped element
21830                  * @method afterDragDrop
21831                  */
21832                 this.afterDragDrop(target, e, id);
21833             }
21834         }
21835         delete this.cachedTarget;
21836     },
21837
21838     /**
21839      * An empty function by default, but provided so that you can perform a custom action before the dragged
21840      * item is dropped onto the target and optionally cancel the onDragDrop.
21841      * @param {Roo.dd.DragDrop} target The drop target
21842      * @param {Event} e The event object
21843      * @param {String} id The id of the dragged element
21844      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
21845      */
21846     beforeDragDrop : function(target, e, id){
21847         return true;
21848     },
21849
21850     // private
21851     onValidDrop : function(target, e, id){
21852         this.hideProxy();
21853         if(this.afterValidDrop){
21854             /**
21855              * An empty function by default, but provided so that you can perform a custom action
21856              * after a valid drop has occurred by providing an implementation.
21857              * @param {Object} target The target DD 
21858              * @param {Event} e The event object
21859              * @param {String} id The id of the dropped element
21860              * @method afterInvalidDrop
21861              */
21862             this.afterValidDrop(target, e, id);
21863         }
21864     },
21865
21866     // private
21867     getRepairXY : function(e, data){
21868         return this.el.getXY();  
21869     },
21870
21871     // private
21872     onInvalidDrop : function(target, e, id){
21873         this.beforeInvalidDrop(target, e, id);
21874         if(this.cachedTarget){
21875             if(this.cachedTarget.isNotifyTarget){
21876                 this.cachedTarget.notifyOut(this, e, this.dragData);
21877             }
21878             this.cacheTarget = null;
21879         }
21880         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
21881
21882         if(this.afterInvalidDrop){
21883             /**
21884              * An empty function by default, but provided so that you can perform a custom action
21885              * after an invalid drop has occurred by providing an implementation.
21886              * @param {Event} e The event object
21887              * @param {String} id The id of the dropped element
21888              * @method afterInvalidDrop
21889              */
21890             this.afterInvalidDrop(e, id);
21891         }
21892     },
21893
21894     // private
21895     afterRepair : function(){
21896         if(Roo.enableFx){
21897             this.el.highlight(this.hlColor || "c3daf9");
21898         }
21899         this.dragging = false;
21900     },
21901
21902     /**
21903      * An empty function by default, but provided so that you can perform a custom action after an invalid
21904      * drop has occurred.
21905      * @param {Roo.dd.DragDrop} target The drop target
21906      * @param {Event} e The event object
21907      * @param {String} id The id of the dragged element
21908      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
21909      */
21910     beforeInvalidDrop : function(target, e, id){
21911         return true;
21912     },
21913
21914     // private
21915     handleMouseDown : function(e){
21916         if(this.dragging) {
21917             return;
21918         }
21919         var data = this.getDragData(e);
21920         if(data && this.onBeforeDrag(data, e) !== false){
21921             this.dragData = data;
21922             this.proxy.stop();
21923             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
21924         } 
21925     },
21926
21927     /**
21928      * An empty function by default, but provided so that you can perform a custom action before the initial
21929      * drag event begins and optionally cancel it.
21930      * @param {Object} data An object containing arbitrary data to be shared with drop targets
21931      * @param {Event} e The event object
21932      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21933      */
21934     onBeforeDrag : function(data, e){
21935         return true;
21936     },
21937
21938     /**
21939      * An empty function by default, but provided so that you can perform a custom action once the initial
21940      * drag event has begun.  The drag cannot be canceled from this function.
21941      * @param {Number} x The x position of the click on the dragged object
21942      * @param {Number} y The y position of the click on the dragged object
21943      */
21944     onStartDrag : Roo.emptyFn,
21945
21946     // private - YUI override
21947     startDrag : function(x, y){
21948         this.proxy.reset();
21949         this.dragging = true;
21950         this.proxy.update("");
21951         this.onInitDrag(x, y);
21952         this.proxy.show();
21953     },
21954
21955     // private
21956     onInitDrag : function(x, y){
21957         var clone = this.el.dom.cloneNode(true);
21958         clone.id = Roo.id(); // prevent duplicate ids
21959         this.proxy.update(clone);
21960         this.onStartDrag(x, y);
21961         return true;
21962     },
21963
21964     /**
21965      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
21966      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
21967      */
21968     getProxy : function(){
21969         return this.proxy;  
21970     },
21971
21972     /**
21973      * Hides the drag source's {@link Roo.dd.StatusProxy}
21974      */
21975     hideProxy : function(){
21976         this.proxy.hide();  
21977         this.proxy.reset(true);
21978         this.dragging = false;
21979     },
21980
21981     // private
21982     triggerCacheRefresh : function(){
21983         Roo.dd.DDM.refreshCache(this.groups);
21984     },
21985
21986     // private - override to prevent hiding
21987     b4EndDrag: function(e) {
21988     },
21989
21990     // private - override to prevent moving
21991     endDrag : function(e){
21992         this.onEndDrag(this.dragData, e);
21993     },
21994
21995     // private
21996     onEndDrag : function(data, e){
21997     },
21998     
21999     // private - pin to cursor
22000     autoOffset : function(x, y) {
22001         this.setDelta(-12, -20);
22002     }    
22003 });/*
22004  * Based on:
22005  * Ext JS Library 1.1.1
22006  * Copyright(c) 2006-2007, Ext JS, LLC.
22007  *
22008  * Originally Released Under LGPL - original licence link has changed is not relivant.
22009  *
22010  * Fork - LGPL
22011  * <script type="text/javascript">
22012  */
22013
22014
22015 /**
22016  * @class Roo.dd.DropTarget
22017  * @extends Roo.dd.DDTarget
22018  * A simple class that provides the basic implementation needed to make any element a drop target that can have
22019  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
22020  * @constructor
22021  * @param {String/HTMLElement/Element} el The container element
22022  * @param {Object} config
22023  */
22024 Roo.dd.DropTarget = function(el, config){
22025     this.el = Roo.get(el);
22026     
22027     var listeners = false; ;
22028     if (config && config.listeners) {
22029         listeners= config.listeners;
22030         delete config.listeners;
22031     }
22032     Roo.apply(this, config);
22033     
22034     if(this.containerScroll){
22035         Roo.dd.ScrollManager.register(this.el);
22036     }
22037     this.addEvents( {
22038          /**
22039          * @scope Roo.dd.DropTarget
22040          */
22041          
22042          /**
22043          * @event enter
22044          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
22045          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
22046          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
22047          * 
22048          * IMPORTANT : it should set this.overClass and this.dropAllowed
22049          * 
22050          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22051          * @param {Event} e The event
22052          * @param {Object} data An object containing arbitrary data supplied by the drag source
22053          */
22054         "enter" : true,
22055         
22056          /**
22057          * @event over
22058          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
22059          * This method will be called on every mouse movement while the drag source is over the drop target.
22060          * This default implementation simply returns the dropAllowed config value.
22061          * 
22062          * IMPORTANT : it should set this.dropAllowed
22063          * 
22064          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22065          * @param {Event} e The event
22066          * @param {Object} data An object containing arbitrary data supplied by the drag source
22067          
22068          */
22069         "over" : true,
22070         /**
22071          * @event out
22072          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
22073          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
22074          * overClass (if any) from the drop element.
22075          * 
22076          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22077          * @param {Event} e The event
22078          * @param {Object} data An object containing arbitrary data supplied by the drag source
22079          */
22080          "out" : true,
22081          
22082         /**
22083          * @event drop
22084          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
22085          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
22086          * implementation that does something to process the drop event and returns true so that the drag source's
22087          * repair action does not run.
22088          * 
22089          * IMPORTANT : it should set this.success
22090          * 
22091          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22092          * @param {Event} e The event
22093          * @param {Object} data An object containing arbitrary data supplied by the drag source
22094         */
22095          "drop" : true
22096     });
22097             
22098      
22099     Roo.dd.DropTarget.superclass.constructor.call(  this, 
22100         this.el.dom, 
22101         this.ddGroup || this.group,
22102         {
22103             isTarget: true,
22104             listeners : listeners || {} 
22105            
22106         
22107         }
22108     );
22109
22110 };
22111
22112 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
22113     /**
22114      * @cfg {String} overClass
22115      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
22116      */
22117      /**
22118      * @cfg {String} ddGroup
22119      * The drag drop group to handle drop events for
22120      */
22121      
22122     /**
22123      * @cfg {String} dropAllowed
22124      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
22125      */
22126     dropAllowed : "x-dd-drop-ok",
22127     /**
22128      * @cfg {String} dropNotAllowed
22129      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
22130      */
22131     dropNotAllowed : "x-dd-drop-nodrop",
22132     /**
22133      * @cfg {boolean} success
22134      * set this after drop listener.. 
22135      */
22136     success : false,
22137     /**
22138      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
22139      * if the drop point is valid for over/enter..
22140      */
22141     valid : false,
22142     // private
22143     isTarget : true,
22144
22145     // private
22146     isNotifyTarget : true,
22147     
22148     /**
22149      * @hide
22150      */
22151     notifyEnter : function(dd, e, data)
22152     {
22153         this.valid = true;
22154         this.fireEvent('enter', dd, e, data);
22155         if(this.overClass){
22156             this.el.addClass(this.overClass);
22157         }
22158         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22159             this.valid ? this.dropAllowed : this.dropNotAllowed
22160         );
22161     },
22162
22163     /**
22164      * @hide
22165      */
22166     notifyOver : function(dd, e, data)
22167     {
22168         this.valid = true;
22169         this.fireEvent('over', dd, e, data);
22170         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22171             this.valid ? this.dropAllowed : this.dropNotAllowed
22172         );
22173     },
22174
22175     /**
22176      * @hide
22177      */
22178     notifyOut : function(dd, e, data)
22179     {
22180         this.fireEvent('out', dd, e, data);
22181         if(this.overClass){
22182             this.el.removeClass(this.overClass);
22183         }
22184     },
22185
22186     /**
22187      * @hide
22188      */
22189     notifyDrop : function(dd, e, data)
22190     {
22191         this.success = false;
22192         this.fireEvent('drop', dd, e, data);
22193         return this.success;
22194     }
22195 });/*
22196  * Based on:
22197  * Ext JS Library 1.1.1
22198  * Copyright(c) 2006-2007, Ext JS, LLC.
22199  *
22200  * Originally Released Under LGPL - original licence link has changed is not relivant.
22201  *
22202  * Fork - LGPL
22203  * <script type="text/javascript">
22204  */
22205
22206
22207 /**
22208  * @class Roo.dd.DragZone
22209  * @extends Roo.dd.DragSource
22210  * This class provides a container DD instance that proxies for multiple child node sources.<br />
22211  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
22212  * @constructor
22213  * @param {String/HTMLElement/Element} el The container element
22214  * @param {Object} config
22215  */
22216 Roo.dd.DragZone = function(el, config){
22217     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
22218     if(this.containerScroll){
22219         Roo.dd.ScrollManager.register(this.el);
22220     }
22221 };
22222
22223 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
22224     /**
22225      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
22226      * for auto scrolling during drag operations.
22227      */
22228     /**
22229      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
22230      * method after a failed drop (defaults to "c3daf9" - light blue)
22231      */
22232
22233     /**
22234      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
22235      * for a valid target to drag based on the mouse down. Override this method
22236      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
22237      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
22238      * @param {EventObject} e The mouse down event
22239      * @return {Object} The dragData
22240      */
22241     getDragData : function(e){
22242         return Roo.dd.Registry.getHandleFromEvent(e);
22243     },
22244     
22245     /**
22246      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
22247      * this.dragData.ddel
22248      * @param {Number} x The x position of the click on the dragged object
22249      * @param {Number} y The y position of the click on the dragged object
22250      * @return {Boolean} true to continue the drag, false to cancel
22251      */
22252     onInitDrag : function(x, y){
22253         this.proxy.update(this.dragData.ddel.cloneNode(true));
22254         this.onStartDrag(x, y);
22255         return true;
22256     },
22257     
22258     /**
22259      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
22260      */
22261     afterRepair : function(){
22262         if(Roo.enableFx){
22263             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
22264         }
22265         this.dragging = false;
22266     },
22267
22268     /**
22269      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
22270      * the XY of this.dragData.ddel
22271      * @param {EventObject} e The mouse up event
22272      * @return {Array} The xy location (e.g. [100, 200])
22273      */
22274     getRepairXY : function(e){
22275         return Roo.Element.fly(this.dragData.ddel).getXY();  
22276     }
22277 });/*
22278  * Based on:
22279  * Ext JS Library 1.1.1
22280  * Copyright(c) 2006-2007, Ext JS, LLC.
22281  *
22282  * Originally Released Under LGPL - original licence link has changed is not relivant.
22283  *
22284  * Fork - LGPL
22285  * <script type="text/javascript">
22286  */
22287 /**
22288  * @class Roo.dd.DropZone
22289  * @extends Roo.dd.DropTarget
22290  * This class provides a container DD instance that proxies for multiple child node targets.<br />
22291  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
22292  * @constructor
22293  * @param {String/HTMLElement/Element} el The container element
22294  * @param {Object} config
22295  */
22296 Roo.dd.DropZone = function(el, config){
22297     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
22298 };
22299
22300 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
22301     /**
22302      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
22303      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
22304      * provide your own custom lookup.
22305      * @param {Event} e The event
22306      * @return {Object} data The custom data
22307      */
22308     getTargetFromEvent : function(e){
22309         return Roo.dd.Registry.getTargetFromEvent(e);
22310     },
22311
22312     /**
22313      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
22314      * that it has registered.  This method has no default implementation and should be overridden to provide
22315      * node-specific processing if necessary.
22316      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
22317      * {@link #getTargetFromEvent} for this node)
22318      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22319      * @param {Event} e The event
22320      * @param {Object} data An object containing arbitrary data supplied by the drag source
22321      */
22322     onNodeEnter : function(n, dd, e, data){
22323         
22324     },
22325
22326     /**
22327      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
22328      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
22329      * overridden to provide the proper feedback.
22330      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22331      * {@link #getTargetFromEvent} for this node)
22332      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22333      * @param {Event} e The event
22334      * @param {Object} data An object containing arbitrary data supplied by the drag source
22335      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22336      * underlying {@link Roo.dd.StatusProxy} can be updated
22337      */
22338     onNodeOver : function(n, dd, e, data){
22339         return this.dropAllowed;
22340     },
22341
22342     /**
22343      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
22344      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
22345      * node-specific processing if necessary.
22346      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22347      * {@link #getTargetFromEvent} for this node)
22348      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22349      * @param {Event} e The event
22350      * @param {Object} data An object containing arbitrary data supplied by the drag source
22351      */
22352     onNodeOut : function(n, dd, e, data){
22353         
22354     },
22355
22356     /**
22357      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
22358      * the drop node.  The default implementation returns false, so it should be overridden to provide the
22359      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
22360      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22361      * {@link #getTargetFromEvent} for this node)
22362      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22363      * @param {Event} e The event
22364      * @param {Object} data An object containing arbitrary data supplied by the drag source
22365      * @return {Boolean} True if the drop was valid, else false
22366      */
22367     onNodeDrop : function(n, dd, e, data){
22368         return false;
22369     },
22370
22371     /**
22372      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
22373      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
22374      * it should be overridden to provide the proper feedback if necessary.
22375      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22376      * @param {Event} e The event
22377      * @param {Object} data An object containing arbitrary data supplied by the drag source
22378      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22379      * underlying {@link Roo.dd.StatusProxy} can be updated
22380      */
22381     onContainerOver : function(dd, e, data){
22382         return this.dropNotAllowed;
22383     },
22384
22385     /**
22386      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
22387      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
22388      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
22389      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
22390      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22391      * @param {Event} e The event
22392      * @param {Object} data An object containing arbitrary data supplied by the drag source
22393      * @return {Boolean} True if the drop was valid, else false
22394      */
22395     onContainerDrop : function(dd, e, data){
22396         return false;
22397     },
22398
22399     /**
22400      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
22401      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
22402      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
22403      * you should override this method and provide a custom implementation.
22404      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22405      * @param {Event} e The event
22406      * @param {Object} data An object containing arbitrary data supplied by the drag source
22407      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22408      * underlying {@link Roo.dd.StatusProxy} can be updated
22409      */
22410     notifyEnter : function(dd, e, data){
22411         return this.dropNotAllowed;
22412     },
22413
22414     /**
22415      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
22416      * This method will be called on every mouse movement while the drag source is over the drop zone.
22417      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
22418      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
22419      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
22420      * registered node, it will call {@link #onContainerOver}.
22421      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22422      * @param {Event} e The event
22423      * @param {Object} data An object containing arbitrary data supplied by the drag source
22424      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22425      * underlying {@link Roo.dd.StatusProxy} can be updated
22426      */
22427     notifyOver : function(dd, e, data){
22428         var n = this.getTargetFromEvent(e);
22429         if(!n){ // not over valid drop target
22430             if(this.lastOverNode){
22431                 this.onNodeOut(this.lastOverNode, dd, e, data);
22432                 this.lastOverNode = null;
22433             }
22434             return this.onContainerOver(dd, e, data);
22435         }
22436         if(this.lastOverNode != n){
22437             if(this.lastOverNode){
22438                 this.onNodeOut(this.lastOverNode, dd, e, data);
22439             }
22440             this.onNodeEnter(n, dd, e, data);
22441             this.lastOverNode = n;
22442         }
22443         return this.onNodeOver(n, dd, e, data);
22444     },
22445
22446     /**
22447      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
22448      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
22449      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
22450      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22451      * @param {Event} e The event
22452      * @param {Object} data An object containing arbitrary data supplied by the drag zone
22453      */
22454     notifyOut : function(dd, e, data){
22455         if(this.lastOverNode){
22456             this.onNodeOut(this.lastOverNode, dd, e, data);
22457             this.lastOverNode = null;
22458         }
22459     },
22460
22461     /**
22462      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
22463      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
22464      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
22465      * otherwise it will call {@link #onContainerDrop}.
22466      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22467      * @param {Event} e The event
22468      * @param {Object} data An object containing arbitrary data supplied by the drag source
22469      * @return {Boolean} True if the drop was valid, else false
22470      */
22471     notifyDrop : function(dd, e, data){
22472         if(this.lastOverNode){
22473             this.onNodeOut(this.lastOverNode, dd, e, data);
22474             this.lastOverNode = null;
22475         }
22476         var n = this.getTargetFromEvent(e);
22477         return n ?
22478             this.onNodeDrop(n, dd, e, data) :
22479             this.onContainerDrop(dd, e, data);
22480     },
22481
22482     // private
22483     triggerCacheRefresh : function(){
22484         Roo.dd.DDM.refreshCache(this.groups);
22485     }  
22486 });/*
22487  * Based on:
22488  * Ext JS Library 1.1.1
22489  * Copyright(c) 2006-2007, Ext JS, LLC.
22490  *
22491  * Originally Released Under LGPL - original licence link has changed is not relivant.
22492  *
22493  * Fork - LGPL
22494  * <script type="text/javascript">
22495  */
22496
22497
22498 /**
22499  * @class Roo.data.SortTypes
22500  * @singleton
22501  * Defines the default sorting (casting?) comparison functions used when sorting data.
22502  */
22503 Roo.data.SortTypes = {
22504     /**
22505      * Default sort that does nothing
22506      * @param {Mixed} s The value being converted
22507      * @return {Mixed} The comparison value
22508      */
22509     none : function(s){
22510         return s;
22511     },
22512     
22513     /**
22514      * The regular expression used to strip tags
22515      * @type {RegExp}
22516      * @property
22517      */
22518     stripTagsRE : /<\/?[^>]+>/gi,
22519     
22520     /**
22521      * Strips all HTML tags to sort on text only
22522      * @param {Mixed} s The value being converted
22523      * @return {String} The comparison value
22524      */
22525     asText : function(s){
22526         return String(s).replace(this.stripTagsRE, "");
22527     },
22528     
22529     /**
22530      * Strips all HTML tags to sort on text only - Case insensitive
22531      * @param {Mixed} s The value being converted
22532      * @return {String} The comparison value
22533      */
22534     asUCText : function(s){
22535         return String(s).toUpperCase().replace(this.stripTagsRE, "");
22536     },
22537     
22538     /**
22539      * Case insensitive string
22540      * @param {Mixed} s The value being converted
22541      * @return {String} The comparison value
22542      */
22543     asUCString : function(s) {
22544         return String(s).toUpperCase();
22545     },
22546     
22547     /**
22548      * Date sorting
22549      * @param {Mixed} s The value being converted
22550      * @return {Number} The comparison value
22551      */
22552     asDate : function(s) {
22553         if(!s){
22554             return 0;
22555         }
22556         if(s instanceof Date){
22557             return s.getTime();
22558         }
22559         return Date.parse(String(s));
22560     },
22561     
22562     /**
22563      * Float sorting
22564      * @param {Mixed} s The value being converted
22565      * @return {Float} The comparison value
22566      */
22567     asFloat : function(s) {
22568         var val = parseFloat(String(s).replace(/,/g, ""));
22569         if(isNaN(val)) {
22570             val = 0;
22571         }
22572         return val;
22573     },
22574     
22575     /**
22576      * Integer sorting
22577      * @param {Mixed} s The value being converted
22578      * @return {Number} The comparison value
22579      */
22580     asInt : function(s) {
22581         var val = parseInt(String(s).replace(/,/g, ""));
22582         if(isNaN(val)) {
22583             val = 0;
22584         }
22585         return val;
22586     }
22587 };/*
22588  * Based on:
22589  * Ext JS Library 1.1.1
22590  * Copyright(c) 2006-2007, Ext JS, LLC.
22591  *
22592  * Originally Released Under LGPL - original licence link has changed is not relivant.
22593  *
22594  * Fork - LGPL
22595  * <script type="text/javascript">
22596  */
22597
22598 /**
22599 * @class Roo.data.Record
22600  * Instances of this class encapsulate both record <em>definition</em> information, and record
22601  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
22602  * to access Records cached in an {@link Roo.data.Store} object.<br>
22603  * <p>
22604  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
22605  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
22606  * objects.<br>
22607  * <p>
22608  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
22609  * @constructor
22610  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
22611  * {@link #create}. The parameters are the same.
22612  * @param {Array} data An associative Array of data values keyed by the field name.
22613  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
22614  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
22615  * not specified an integer id is generated.
22616  */
22617 Roo.data.Record = function(data, id){
22618     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
22619     this.data = data;
22620 };
22621
22622 /**
22623  * Generate a constructor for a specific record layout.
22624  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
22625  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
22626  * Each field definition object may contain the following properties: <ul>
22627  * <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,
22628  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
22629  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
22630  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
22631  * is being used, then this is a string containing the javascript expression to reference the data relative to 
22632  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
22633  * to the data item relative to the record element. If the mapping expression is the same as the field name,
22634  * this may be omitted.</p></li>
22635  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
22636  * <ul><li>auto (Default, implies no conversion)</li>
22637  * <li>string</li>
22638  * <li>int</li>
22639  * <li>float</li>
22640  * <li>boolean</li>
22641  * <li>date</li></ul></p></li>
22642  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
22643  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
22644  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
22645  * by the Reader into an object that will be stored in the Record. It is passed the
22646  * following parameters:<ul>
22647  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
22648  * </ul></p></li>
22649  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
22650  * </ul>
22651  * <br>usage:<br><pre><code>
22652 var TopicRecord = Roo.data.Record.create(
22653     {name: 'title', mapping: 'topic_title'},
22654     {name: 'author', mapping: 'username'},
22655     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
22656     {name: 'lastPost', mapping: 'post_time', type: 'date'},
22657     {name: 'lastPoster', mapping: 'user2'},
22658     {name: 'excerpt', mapping: 'post_text'}
22659 );
22660
22661 var myNewRecord = new TopicRecord({
22662     title: 'Do my job please',
22663     author: 'noobie',
22664     totalPosts: 1,
22665     lastPost: new Date(),
22666     lastPoster: 'Animal',
22667     excerpt: 'No way dude!'
22668 });
22669 myStore.add(myNewRecord);
22670 </code></pre>
22671  * @method create
22672  * @static
22673  */
22674 Roo.data.Record.create = function(o){
22675     var f = function(){
22676         f.superclass.constructor.apply(this, arguments);
22677     };
22678     Roo.extend(f, Roo.data.Record);
22679     var p = f.prototype;
22680     p.fields = new Roo.util.MixedCollection(false, function(field){
22681         return field.name;
22682     });
22683     for(var i = 0, len = o.length; i < len; i++){
22684         p.fields.add(new Roo.data.Field(o[i]));
22685     }
22686     f.getField = function(name){
22687         return p.fields.get(name);  
22688     };
22689     return f;
22690 };
22691
22692 Roo.data.Record.AUTO_ID = 1000;
22693 Roo.data.Record.EDIT = 'edit';
22694 Roo.data.Record.REJECT = 'reject';
22695 Roo.data.Record.COMMIT = 'commit';
22696
22697 Roo.data.Record.prototype = {
22698     /**
22699      * Readonly flag - true if this record has been modified.
22700      * @type Boolean
22701      */
22702     dirty : false,
22703     editing : false,
22704     error: null,
22705     modified: null,
22706
22707     // private
22708     join : function(store){
22709         this.store = store;
22710     },
22711
22712     /**
22713      * Set the named field to the specified value.
22714      * @param {String} name The name of the field to set.
22715      * @param {Object} value The value to set the field to.
22716      */
22717     set : function(name, value){
22718         if(this.data[name] == value){
22719             return;
22720         }
22721         this.dirty = true;
22722         if(!this.modified){
22723             this.modified = {};
22724         }
22725         if(typeof this.modified[name] == 'undefined'){
22726             this.modified[name] = this.data[name];
22727         }
22728         this.data[name] = value;
22729         if(!this.editing && this.store){
22730             this.store.afterEdit(this);
22731         }       
22732     },
22733
22734     /**
22735      * Get the value of the named field.
22736      * @param {String} name The name of the field to get the value of.
22737      * @return {Object} The value of the field.
22738      */
22739     get : function(name){
22740         return this.data[name]; 
22741     },
22742
22743     // private
22744     beginEdit : function(){
22745         this.editing = true;
22746         this.modified = {}; 
22747     },
22748
22749     // private
22750     cancelEdit : function(){
22751         this.editing = false;
22752         delete this.modified;
22753     },
22754
22755     // private
22756     endEdit : function(){
22757         this.editing = false;
22758         if(this.dirty && this.store){
22759             this.store.afterEdit(this);
22760         }
22761     },
22762
22763     /**
22764      * Usually called by the {@link Roo.data.Store} which owns the Record.
22765      * Rejects all changes made to the Record since either creation, or the last commit operation.
22766      * Modified fields are reverted to their original values.
22767      * <p>
22768      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
22769      * of reject operations.
22770      */
22771     reject : function(){
22772         var m = this.modified;
22773         for(var n in m){
22774             if(typeof m[n] != "function"){
22775                 this.data[n] = m[n];
22776             }
22777         }
22778         this.dirty = false;
22779         delete this.modified;
22780         this.editing = false;
22781         if(this.store){
22782             this.store.afterReject(this);
22783         }
22784     },
22785
22786     /**
22787      * Usually called by the {@link Roo.data.Store} which owns the Record.
22788      * Commits all changes made to the Record since either creation, or the last commit operation.
22789      * <p>
22790      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
22791      * of commit operations.
22792      */
22793     commit : function(){
22794         this.dirty = false;
22795         delete this.modified;
22796         this.editing = false;
22797         if(this.store){
22798             this.store.afterCommit(this);
22799         }
22800     },
22801
22802     // private
22803     hasError : function(){
22804         return this.error != null;
22805     },
22806
22807     // private
22808     clearError : function(){
22809         this.error = null;
22810     },
22811
22812     /**
22813      * Creates a copy of this record.
22814      * @param {String} id (optional) A new record id if you don't want to use this record's id
22815      * @return {Record}
22816      */
22817     copy : function(newId) {
22818         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
22819     }
22820 };/*
22821  * Based on:
22822  * Ext JS Library 1.1.1
22823  * Copyright(c) 2006-2007, Ext JS, LLC.
22824  *
22825  * Originally Released Under LGPL - original licence link has changed is not relivant.
22826  *
22827  * Fork - LGPL
22828  * <script type="text/javascript">
22829  */
22830
22831
22832
22833 /**
22834  * @class Roo.data.Store
22835  * @extends Roo.util.Observable
22836  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
22837  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
22838  * <p>
22839  * 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
22840  * has no knowledge of the format of the data returned by the Proxy.<br>
22841  * <p>
22842  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
22843  * instances from the data object. These records are cached and made available through accessor functions.
22844  * @constructor
22845  * Creates a new Store.
22846  * @param {Object} config A config object containing the objects needed for the Store to access data,
22847  * and read the data into Records.
22848  */
22849 Roo.data.Store = function(config){
22850     this.data = new Roo.util.MixedCollection(false);
22851     this.data.getKey = function(o){
22852         return o.id;
22853     };
22854     this.baseParams = {};
22855     // private
22856     this.paramNames = {
22857         "start" : "start",
22858         "limit" : "limit",
22859         "sort" : "sort",
22860         "dir" : "dir",
22861         "multisort" : "_multisort"
22862     };
22863
22864     if(config && config.data){
22865         this.inlineData = config.data;
22866         delete config.data;
22867     }
22868
22869     Roo.apply(this, config);
22870     
22871     if(this.reader){ // reader passed
22872         this.reader = Roo.factory(this.reader, Roo.data);
22873         this.reader.xmodule = this.xmodule || false;
22874         if(!this.recordType){
22875             this.recordType = this.reader.recordType;
22876         }
22877         if(this.reader.onMetaChange){
22878             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
22879         }
22880     }
22881
22882     if(this.recordType){
22883         this.fields = this.recordType.prototype.fields;
22884     }
22885     this.modified = [];
22886
22887     this.addEvents({
22888         /**
22889          * @event datachanged
22890          * Fires when the data cache has changed, and a widget which is using this Store
22891          * as a Record cache should refresh its view.
22892          * @param {Store} this
22893          */
22894         datachanged : true,
22895         /**
22896          * @event metachange
22897          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
22898          * @param {Store} this
22899          * @param {Object} meta The JSON metadata
22900          */
22901         metachange : true,
22902         /**
22903          * @event add
22904          * Fires when Records have been added to the Store
22905          * @param {Store} this
22906          * @param {Roo.data.Record[]} records The array of Records added
22907          * @param {Number} index The index at which the record(s) were added
22908          */
22909         add : true,
22910         /**
22911          * @event remove
22912          * Fires when a Record has been removed from the Store
22913          * @param {Store} this
22914          * @param {Roo.data.Record} record The Record that was removed
22915          * @param {Number} index The index at which the record was removed
22916          */
22917         remove : true,
22918         /**
22919          * @event update
22920          * Fires when a Record has been updated
22921          * @param {Store} this
22922          * @param {Roo.data.Record} record The Record that was updated
22923          * @param {String} operation The update operation being performed.  Value may be one of:
22924          * <pre><code>
22925  Roo.data.Record.EDIT
22926  Roo.data.Record.REJECT
22927  Roo.data.Record.COMMIT
22928          * </code></pre>
22929          */
22930         update : true,
22931         /**
22932          * @event clear
22933          * Fires when the data cache has been cleared.
22934          * @param {Store} this
22935          */
22936         clear : true,
22937         /**
22938          * @event beforeload
22939          * Fires before a request is made for a new data object.  If the beforeload handler returns false
22940          * the load action will be canceled.
22941          * @param {Store} this
22942          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22943          */
22944         beforeload : true,
22945         /**
22946          * @event beforeloadadd
22947          * Fires after a new set of Records has been loaded.
22948          * @param {Store} this
22949          * @param {Roo.data.Record[]} records The Records that were loaded
22950          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22951          */
22952         beforeloadadd : true,
22953         /**
22954          * @event load
22955          * Fires after a new set of Records has been loaded, before they are added to the store.
22956          * @param {Store} this
22957          * @param {Roo.data.Record[]} records The Records that were loaded
22958          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22959          * @params {Object} return from reader
22960          */
22961         load : true,
22962         /**
22963          * @event loadexception
22964          * Fires if an exception occurs in the Proxy during loading.
22965          * Called with the signature of the Proxy's "loadexception" event.
22966          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
22967          * 
22968          * @param {Proxy} 
22969          * @param {Object} return from JsonData.reader() - success, totalRecords, records
22970          * @param {Object} load options 
22971          * @param {Object} jsonData from your request (normally this contains the Exception)
22972          */
22973         loadexception : true
22974     });
22975     
22976     if(this.proxy){
22977         this.proxy = Roo.factory(this.proxy, Roo.data);
22978         this.proxy.xmodule = this.xmodule || false;
22979         this.relayEvents(this.proxy,  ["loadexception"]);
22980     }
22981     this.sortToggle = {};
22982     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
22983
22984     Roo.data.Store.superclass.constructor.call(this);
22985
22986     if(this.inlineData){
22987         this.loadData(this.inlineData);
22988         delete this.inlineData;
22989     }
22990 };
22991
22992 Roo.extend(Roo.data.Store, Roo.util.Observable, {
22993      /**
22994     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
22995     * without a remote query - used by combo/forms at present.
22996     */
22997     
22998     /**
22999     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
23000     */
23001     /**
23002     * @cfg {Array} data Inline data to be loaded when the store is initialized.
23003     */
23004     /**
23005     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
23006     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
23007     */
23008     /**
23009     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
23010     * on any HTTP request
23011     */
23012     /**
23013     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
23014     */
23015     /**
23016     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
23017     */
23018     multiSort: false,
23019     /**
23020     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
23021     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
23022     */
23023     remoteSort : false,
23024
23025     /**
23026     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
23027      * loaded or when a record is removed. (defaults to false).
23028     */
23029     pruneModifiedRecords : false,
23030
23031     // private
23032     lastOptions : null,
23033
23034     /**
23035      * Add Records to the Store and fires the add event.
23036      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
23037      */
23038     add : function(records){
23039         records = [].concat(records);
23040         for(var i = 0, len = records.length; i < len; i++){
23041             records[i].join(this);
23042         }
23043         var index = this.data.length;
23044         this.data.addAll(records);
23045         this.fireEvent("add", this, records, index);
23046     },
23047
23048     /**
23049      * Remove a Record from the Store and fires the remove event.
23050      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
23051      */
23052     remove : function(record){
23053         var index = this.data.indexOf(record);
23054         this.data.removeAt(index);
23055  
23056         if(this.pruneModifiedRecords){
23057             this.modified.remove(record);
23058         }
23059         this.fireEvent("remove", this, record, index);
23060     },
23061
23062     /**
23063      * Remove all Records from the Store and fires the clear event.
23064      */
23065     removeAll : function(){
23066         this.data.clear();
23067         if(this.pruneModifiedRecords){
23068             this.modified = [];
23069         }
23070         this.fireEvent("clear", this);
23071     },
23072
23073     /**
23074      * Inserts Records to the Store at the given index and fires the add event.
23075      * @param {Number} index The start index at which to insert the passed Records.
23076      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
23077      */
23078     insert : function(index, records){
23079         records = [].concat(records);
23080         for(var i = 0, len = records.length; i < len; i++){
23081             this.data.insert(index, records[i]);
23082             records[i].join(this);
23083         }
23084         this.fireEvent("add", this, records, index);
23085     },
23086
23087     /**
23088      * Get the index within the cache of the passed Record.
23089      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
23090      * @return {Number} The index of the passed Record. Returns -1 if not found.
23091      */
23092     indexOf : function(record){
23093         return this.data.indexOf(record);
23094     },
23095
23096     /**
23097      * Get the index within the cache of the Record with the passed id.
23098      * @param {String} id The id of the Record to find.
23099      * @return {Number} The index of the Record. Returns -1 if not found.
23100      */
23101     indexOfId : function(id){
23102         return this.data.indexOfKey(id);
23103     },
23104
23105     /**
23106      * Get the Record with the specified id.
23107      * @param {String} id The id of the Record to find.
23108      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
23109      */
23110     getById : function(id){
23111         return this.data.key(id);
23112     },
23113
23114     /**
23115      * Get the Record at the specified index.
23116      * @param {Number} index The index of the Record to find.
23117      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
23118      */
23119     getAt : function(index){
23120         return this.data.itemAt(index);
23121     },
23122
23123     /**
23124      * Returns a range of Records between specified indices.
23125      * @param {Number} startIndex (optional) The starting index (defaults to 0)
23126      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
23127      * @return {Roo.data.Record[]} An array of Records
23128      */
23129     getRange : function(start, end){
23130         return this.data.getRange(start, end);
23131     },
23132
23133     // private
23134     storeOptions : function(o){
23135         o = Roo.apply({}, o);
23136         delete o.callback;
23137         delete o.scope;
23138         this.lastOptions = o;
23139     },
23140
23141     /**
23142      * Loads the Record cache from the configured Proxy using the configured Reader.
23143      * <p>
23144      * If using remote paging, then the first load call must specify the <em>start</em>
23145      * and <em>limit</em> properties in the options.params property to establish the initial
23146      * position within the dataset, and the number of Records to cache on each read from the Proxy.
23147      * <p>
23148      * <strong>It is important to note that for remote data sources, loading is asynchronous,
23149      * and this call will return before the new data has been loaded. Perform any post-processing
23150      * in a callback function, or in a "load" event handler.</strong>
23151      * <p>
23152      * @param {Object} options An object containing properties which control loading options:<ul>
23153      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
23154      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
23155      * passed the following arguments:<ul>
23156      * <li>r : Roo.data.Record[]</li>
23157      * <li>options: Options object from the load call</li>
23158      * <li>success: Boolean success indicator</li></ul></li>
23159      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
23160      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
23161      * </ul>
23162      */
23163     load : function(options){
23164         options = options || {};
23165         if(this.fireEvent("beforeload", this, options) !== false){
23166             this.storeOptions(options);
23167             var p = Roo.apply(options.params || {}, this.baseParams);
23168             // if meta was not loaded from remote source.. try requesting it.
23169             if (!this.reader.metaFromRemote) {
23170                 p._requestMeta = 1;
23171             }
23172             if(this.sortInfo && this.remoteSort){
23173                 var pn = this.paramNames;
23174                 p[pn["sort"]] = this.sortInfo.field;
23175                 p[pn["dir"]] = this.sortInfo.direction;
23176             }
23177             if (this.multiSort) {
23178                 var pn = this.paramNames;
23179                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
23180             }
23181             
23182             this.proxy.load(p, this.reader, this.loadRecords, this, options);
23183         }
23184     },
23185
23186     /**
23187      * Reloads the Record cache from the configured Proxy using the configured Reader and
23188      * the options from the last load operation performed.
23189      * @param {Object} options (optional) An object containing properties which may override the options
23190      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
23191      * the most recently used options are reused).
23192      */
23193     reload : function(options){
23194         this.load(Roo.applyIf(options||{}, this.lastOptions));
23195     },
23196
23197     // private
23198     // Called as a callback by the Reader during a load operation.
23199     loadRecords : function(o, options, success){
23200         if(!o || success === false){
23201             if(success !== false){
23202                 this.fireEvent("load", this, [], options, o);
23203             }
23204             if(options.callback){
23205                 options.callback.call(options.scope || this, [], options, false);
23206             }
23207             return;
23208         }
23209         // if data returned failure - throw an exception.
23210         if (o.success === false) {
23211             // show a message if no listener is registered.
23212             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
23213                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
23214             }
23215             // loadmask wil be hooked into this..
23216             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
23217             return;
23218         }
23219         var r = o.records, t = o.totalRecords || r.length;
23220         
23221         this.fireEvent("beforeloadadd", this, r, options, o);
23222         
23223         if(!options || options.add !== true){
23224             if(this.pruneModifiedRecords){
23225                 this.modified = [];
23226             }
23227             for(var i = 0, len = r.length; i < len; i++){
23228                 r[i].join(this);
23229             }
23230             if(this.snapshot){
23231                 this.data = this.snapshot;
23232                 delete this.snapshot;
23233             }
23234             this.data.clear();
23235             this.data.addAll(r);
23236             this.totalLength = t;
23237             this.applySort();
23238             this.fireEvent("datachanged", this);
23239         }else{
23240             this.totalLength = Math.max(t, this.data.length+r.length);
23241             this.add(r);
23242         }
23243         
23244         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
23245                 
23246             var e = new Roo.data.Record({});
23247
23248             e.set(this.parent.displayField, this.parent.emptyTitle);
23249             e.set(this.parent.valueField, '');
23250
23251             this.insert(0, e);
23252         }
23253             
23254         this.fireEvent("load", this, r, options, o);
23255         if(options.callback){
23256             options.callback.call(options.scope || this, r, options, true);
23257         }
23258     },
23259
23260
23261     /**
23262      * Loads data from a passed data block. A Reader which understands the format of the data
23263      * must have been configured in the constructor.
23264      * @param {Object} data The data block from which to read the Records.  The format of the data expected
23265      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
23266      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
23267      */
23268     loadData : function(o, append){
23269         var r = this.reader.readRecords(o);
23270         this.loadRecords(r, {add: append}, true);
23271     },
23272     
23273      /**
23274      * using 'cn' the nested child reader read the child array into it's child stores.
23275      * @param {Object} rec The record with a 'children array
23276      */
23277     loadDataFromChildren : function(rec)
23278     {
23279         this.loadData(this.reader.toLoadData(rec));
23280     },
23281     
23282
23283     /**
23284      * Gets the number of cached records.
23285      * <p>
23286      * <em>If using paging, this may not be the total size of the dataset. If the data object
23287      * used by the Reader contains the dataset size, then the getTotalCount() function returns
23288      * the data set size</em>
23289      */
23290     getCount : function(){
23291         return this.data.length || 0;
23292     },
23293
23294     /**
23295      * Gets the total number of records in the dataset as returned by the server.
23296      * <p>
23297      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
23298      * the dataset size</em>
23299      */
23300     getTotalCount : function(){
23301         return this.totalLength || 0;
23302     },
23303
23304     /**
23305      * Returns the sort state of the Store as an object with two properties:
23306      * <pre><code>
23307  field {String} The name of the field by which the Records are sorted
23308  direction {String} The sort order, "ASC" or "DESC"
23309      * </code></pre>
23310      */
23311     getSortState : function(){
23312         return this.sortInfo;
23313     },
23314
23315     // private
23316     applySort : function(){
23317         if(this.sortInfo && !this.remoteSort){
23318             var s = this.sortInfo, f = s.field;
23319             var st = this.fields.get(f).sortType;
23320             var fn = function(r1, r2){
23321                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
23322                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
23323             };
23324             this.data.sort(s.direction, fn);
23325             if(this.snapshot && this.snapshot != this.data){
23326                 this.snapshot.sort(s.direction, fn);
23327             }
23328         }
23329     },
23330
23331     /**
23332      * Sets the default sort column and order to be used by the next load operation.
23333      * @param {String} fieldName The name of the field to sort by.
23334      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23335      */
23336     setDefaultSort : function(field, dir){
23337         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
23338     },
23339
23340     /**
23341      * Sort the Records.
23342      * If remote sorting is used, the sort is performed on the server, and the cache is
23343      * reloaded. If local sorting is used, the cache is sorted internally.
23344      * @param {String} fieldName The name of the field to sort by.
23345      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23346      */
23347     sort : function(fieldName, dir){
23348         var f = this.fields.get(fieldName);
23349         if(!dir){
23350             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
23351             
23352             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
23353                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
23354             }else{
23355                 dir = f.sortDir;
23356             }
23357         }
23358         this.sortToggle[f.name] = dir;
23359         this.sortInfo = {field: f.name, direction: dir};
23360         if(!this.remoteSort){
23361             this.applySort();
23362             this.fireEvent("datachanged", this);
23363         }else{
23364             this.load(this.lastOptions);
23365         }
23366     },
23367
23368     /**
23369      * Calls the specified function for each of the Records in the cache.
23370      * @param {Function} fn The function to call. The Record is passed as the first parameter.
23371      * Returning <em>false</em> aborts and exits the iteration.
23372      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
23373      */
23374     each : function(fn, scope){
23375         this.data.each(fn, scope);
23376     },
23377
23378     /**
23379      * Gets all records modified since the last commit.  Modified records are persisted across load operations
23380      * (e.g., during paging).
23381      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
23382      */
23383     getModifiedRecords : function(){
23384         return this.modified;
23385     },
23386
23387     // private
23388     createFilterFn : function(property, value, anyMatch){
23389         if(!value.exec){ // not a regex
23390             value = String(value);
23391             if(value.length == 0){
23392                 return false;
23393             }
23394             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
23395         }
23396         return function(r){
23397             return value.test(r.data[property]);
23398         };
23399     },
23400
23401     /**
23402      * Sums the value of <i>property</i> for each record between start and end and returns the result.
23403      * @param {String} property A field on your records
23404      * @param {Number} start The record index to start at (defaults to 0)
23405      * @param {Number} end The last record index to include (defaults to length - 1)
23406      * @return {Number} The sum
23407      */
23408     sum : function(property, start, end){
23409         var rs = this.data.items, v = 0;
23410         start = start || 0;
23411         end = (end || end === 0) ? end : rs.length-1;
23412
23413         for(var i = start; i <= end; i++){
23414             v += (rs[i].data[property] || 0);
23415         }
23416         return v;
23417     },
23418
23419     /**
23420      * Filter the records by a specified property.
23421      * @param {String} field A field on your records
23422      * @param {String/RegExp} value Either a string that the field
23423      * should start with or a RegExp to test against the field
23424      * @param {Boolean} anyMatch True to match any part not just the beginning
23425      */
23426     filter : function(property, value, anyMatch){
23427         var fn = this.createFilterFn(property, value, anyMatch);
23428         return fn ? this.filterBy(fn) : this.clearFilter();
23429     },
23430
23431     /**
23432      * Filter by a function. The specified function will be called with each
23433      * record in this data source. If the function returns true the record is included,
23434      * otherwise it is filtered.
23435      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23436      * @param {Object} scope (optional) The scope of the function (defaults to this)
23437      */
23438     filterBy : function(fn, scope){
23439         this.snapshot = this.snapshot || this.data;
23440         this.data = this.queryBy(fn, scope||this);
23441         this.fireEvent("datachanged", this);
23442     },
23443
23444     /**
23445      * Query the records by a specified property.
23446      * @param {String} field A field on your records
23447      * @param {String/RegExp} value Either a string that the field
23448      * should start with or a RegExp to test against the field
23449      * @param {Boolean} anyMatch True to match any part not just the beginning
23450      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23451      */
23452     query : function(property, value, anyMatch){
23453         var fn = this.createFilterFn(property, value, anyMatch);
23454         return fn ? this.queryBy(fn) : this.data.clone();
23455     },
23456
23457     /**
23458      * Query by a function. The specified function will be called with each
23459      * record in this data source. If the function returns true the record is included
23460      * in the results.
23461      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23462      * @param {Object} scope (optional) The scope of the function (defaults to this)
23463       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23464      **/
23465     queryBy : function(fn, scope){
23466         var data = this.snapshot || this.data;
23467         return data.filterBy(fn, scope||this);
23468     },
23469
23470     /**
23471      * Collects unique values for a particular dataIndex from this store.
23472      * @param {String} dataIndex The property to collect
23473      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
23474      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
23475      * @return {Array} An array of the unique values
23476      **/
23477     collect : function(dataIndex, allowNull, bypassFilter){
23478         var d = (bypassFilter === true && this.snapshot) ?
23479                 this.snapshot.items : this.data.items;
23480         var v, sv, r = [], l = {};
23481         for(var i = 0, len = d.length; i < len; i++){
23482             v = d[i].data[dataIndex];
23483             sv = String(v);
23484             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
23485                 l[sv] = true;
23486                 r[r.length] = v;
23487             }
23488         }
23489         return r;
23490     },
23491
23492     /**
23493      * Revert to a view of the Record cache with no filtering applied.
23494      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
23495      */
23496     clearFilter : function(suppressEvent){
23497         if(this.snapshot && this.snapshot != this.data){
23498             this.data = this.snapshot;
23499             delete this.snapshot;
23500             if(suppressEvent !== true){
23501                 this.fireEvent("datachanged", this);
23502             }
23503         }
23504     },
23505
23506     // private
23507     afterEdit : function(record){
23508         if(this.modified.indexOf(record) == -1){
23509             this.modified.push(record);
23510         }
23511         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
23512     },
23513     
23514     // private
23515     afterReject : function(record){
23516         this.modified.remove(record);
23517         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
23518     },
23519
23520     // private
23521     afterCommit : function(record){
23522         this.modified.remove(record);
23523         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
23524     },
23525
23526     /**
23527      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
23528      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
23529      */
23530     commitChanges : function(){
23531         var m = this.modified.slice(0);
23532         this.modified = [];
23533         for(var i = 0, len = m.length; i < len; i++){
23534             m[i].commit();
23535         }
23536     },
23537
23538     /**
23539      * Cancel outstanding changes on all changed records.
23540      */
23541     rejectChanges : function(){
23542         var m = this.modified.slice(0);
23543         this.modified = [];
23544         for(var i = 0, len = m.length; i < len; i++){
23545             m[i].reject();
23546         }
23547     },
23548
23549     onMetaChange : function(meta, rtype, o){
23550         this.recordType = rtype;
23551         this.fields = rtype.prototype.fields;
23552         delete this.snapshot;
23553         this.sortInfo = meta.sortInfo || this.sortInfo;
23554         this.modified = [];
23555         this.fireEvent('metachange', this, this.reader.meta);
23556     },
23557     
23558     moveIndex : function(data, type)
23559     {
23560         var index = this.indexOf(data);
23561         
23562         var newIndex = index + type;
23563         
23564         this.remove(data);
23565         
23566         this.insert(newIndex, data);
23567         
23568     }
23569 });/*
23570  * Based on:
23571  * Ext JS Library 1.1.1
23572  * Copyright(c) 2006-2007, Ext JS, LLC.
23573  *
23574  * Originally Released Under LGPL - original licence link has changed is not relivant.
23575  *
23576  * Fork - LGPL
23577  * <script type="text/javascript">
23578  */
23579
23580 /**
23581  * @class Roo.data.SimpleStore
23582  * @extends Roo.data.Store
23583  * Small helper class to make creating Stores from Array data easier.
23584  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
23585  * @cfg {Array} fields An array of field definition objects, or field name strings.
23586  * @cfg {Object} an existing reader (eg. copied from another store)
23587  * @cfg {Array} data The multi-dimensional array of data
23588  * @constructor
23589  * @param {Object} config
23590  */
23591 Roo.data.SimpleStore = function(config)
23592 {
23593     Roo.data.SimpleStore.superclass.constructor.call(this, {
23594         isLocal : true,
23595         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
23596                 id: config.id
23597             },
23598             Roo.data.Record.create(config.fields)
23599         ),
23600         proxy : new Roo.data.MemoryProxy(config.data)
23601     });
23602     this.load();
23603 };
23604 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
23605  * Based on:
23606  * Ext JS Library 1.1.1
23607  * Copyright(c) 2006-2007, Ext JS, LLC.
23608  *
23609  * Originally Released Under LGPL - original licence link has changed is not relivant.
23610  *
23611  * Fork - LGPL
23612  * <script type="text/javascript">
23613  */
23614
23615 /**
23616 /**
23617  * @extends Roo.data.Store
23618  * @class Roo.data.JsonStore
23619  * Small helper class to make creating Stores for JSON data easier. <br/>
23620 <pre><code>
23621 var store = new Roo.data.JsonStore({
23622     url: 'get-images.php',
23623     root: 'images',
23624     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
23625 });
23626 </code></pre>
23627  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
23628  * JsonReader and HttpProxy (unless inline data is provided).</b>
23629  * @cfg {Array} fields An array of field definition objects, or field name strings.
23630  * @constructor
23631  * @param {Object} config
23632  */
23633 Roo.data.JsonStore = function(c){
23634     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
23635         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
23636         reader: new Roo.data.JsonReader(c, c.fields)
23637     }));
23638 };
23639 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
23640  * Based on:
23641  * Ext JS Library 1.1.1
23642  * Copyright(c) 2006-2007, Ext JS, LLC.
23643  *
23644  * Originally Released Under LGPL - original licence link has changed is not relivant.
23645  *
23646  * Fork - LGPL
23647  * <script type="text/javascript">
23648  */
23649
23650  
23651 Roo.data.Field = function(config){
23652     if(typeof config == "string"){
23653         config = {name: config};
23654     }
23655     Roo.apply(this, config);
23656     
23657     if(!this.type){
23658         this.type = "auto";
23659     }
23660     
23661     var st = Roo.data.SortTypes;
23662     // named sortTypes are supported, here we look them up
23663     if(typeof this.sortType == "string"){
23664         this.sortType = st[this.sortType];
23665     }
23666     
23667     // set default sortType for strings and dates
23668     if(!this.sortType){
23669         switch(this.type){
23670             case "string":
23671                 this.sortType = st.asUCString;
23672                 break;
23673             case "date":
23674                 this.sortType = st.asDate;
23675                 break;
23676             default:
23677                 this.sortType = st.none;
23678         }
23679     }
23680
23681     // define once
23682     var stripRe = /[\$,%]/g;
23683
23684     // prebuilt conversion function for this field, instead of
23685     // switching every time we're reading a value
23686     if(!this.convert){
23687         var cv, dateFormat = this.dateFormat;
23688         switch(this.type){
23689             case "":
23690             case "auto":
23691             case undefined:
23692                 cv = function(v){ return v; };
23693                 break;
23694             case "string":
23695                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
23696                 break;
23697             case "int":
23698                 cv = function(v){
23699                     return v !== undefined && v !== null && v !== '' ?
23700                            parseInt(String(v).replace(stripRe, ""), 10) : '';
23701                     };
23702                 break;
23703             case "float":
23704                 cv = function(v){
23705                     return v !== undefined && v !== null && v !== '' ?
23706                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
23707                     };
23708                 break;
23709             case "bool":
23710             case "boolean":
23711                 cv = function(v){ return v === true || v === "true" || v == 1; };
23712                 break;
23713             case "date":
23714                 cv = function(v){
23715                     if(!v){
23716                         return '';
23717                     }
23718                     if(v instanceof Date){
23719                         return v;
23720                     }
23721                     if(dateFormat){
23722                         if(dateFormat == "timestamp"){
23723                             return new Date(v*1000);
23724                         }
23725                         return Date.parseDate(v, dateFormat);
23726                     }
23727                     var parsed = Date.parse(v);
23728                     return parsed ? new Date(parsed) : null;
23729                 };
23730              break;
23731             
23732         }
23733         this.convert = cv;
23734     }
23735 };
23736
23737 Roo.data.Field.prototype = {
23738     dateFormat: null,
23739     defaultValue: "",
23740     mapping: null,
23741     sortType : null,
23742     sortDir : "ASC"
23743 };/*
23744  * Based on:
23745  * Ext JS Library 1.1.1
23746  * Copyright(c) 2006-2007, Ext JS, LLC.
23747  *
23748  * Originally Released Under LGPL - original licence link has changed is not relivant.
23749  *
23750  * Fork - LGPL
23751  * <script type="text/javascript">
23752  */
23753  
23754 // Base class for reading structured data from a data source.  This class is intended to be
23755 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
23756
23757 /**
23758  * @class Roo.data.DataReader
23759  * Base class for reading structured data from a data source.  This class is intended to be
23760  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
23761  */
23762
23763 Roo.data.DataReader = function(meta, recordType){
23764     
23765     this.meta = meta;
23766     
23767     this.recordType = recordType instanceof Array ? 
23768         Roo.data.Record.create(recordType) : recordType;
23769 };
23770
23771 Roo.data.DataReader.prototype = {
23772     
23773     
23774     readerType : 'Data',
23775      /**
23776      * Create an empty record
23777      * @param {Object} data (optional) - overlay some values
23778      * @return {Roo.data.Record} record created.
23779      */
23780     newRow :  function(d) {
23781         var da =  {};
23782         this.recordType.prototype.fields.each(function(c) {
23783             switch( c.type) {
23784                 case 'int' : da[c.name] = 0; break;
23785                 case 'date' : da[c.name] = new Date(); break;
23786                 case 'float' : da[c.name] = 0.0; break;
23787                 case 'boolean' : da[c.name] = false; break;
23788                 default : da[c.name] = ""; break;
23789             }
23790             
23791         });
23792         return new this.recordType(Roo.apply(da, d));
23793     }
23794     
23795     
23796 };/*
23797  * Based on:
23798  * Ext JS Library 1.1.1
23799  * Copyright(c) 2006-2007, Ext JS, LLC.
23800  *
23801  * Originally Released Under LGPL - original licence link has changed is not relivant.
23802  *
23803  * Fork - LGPL
23804  * <script type="text/javascript">
23805  */
23806
23807 /**
23808  * @class Roo.data.DataProxy
23809  * @extends Roo.data.Observable
23810  * This class is an abstract base class for implementations which provide retrieval of
23811  * unformatted data objects.<br>
23812  * <p>
23813  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
23814  * (of the appropriate type which knows how to parse the data object) to provide a block of
23815  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
23816  * <p>
23817  * Custom implementations must implement the load method as described in
23818  * {@link Roo.data.HttpProxy#load}.
23819  */
23820 Roo.data.DataProxy = function(){
23821     this.addEvents({
23822         /**
23823          * @event beforeload
23824          * Fires before a network request is made to retrieve a data object.
23825          * @param {Object} This DataProxy object.
23826          * @param {Object} params The params parameter to the load function.
23827          */
23828         beforeload : true,
23829         /**
23830          * @event load
23831          * Fires before the load method's callback is called.
23832          * @param {Object} This DataProxy object.
23833          * @param {Object} o The data object.
23834          * @param {Object} arg The callback argument object passed to the load function.
23835          */
23836         load : true,
23837         /**
23838          * @event loadexception
23839          * Fires if an Exception occurs during data retrieval.
23840          * @param {Object} This DataProxy object.
23841          * @param {Object} o The data object.
23842          * @param {Object} arg The callback argument object passed to the load function.
23843          * @param {Object} e The Exception.
23844          */
23845         loadexception : true
23846     });
23847     Roo.data.DataProxy.superclass.constructor.call(this);
23848 };
23849
23850 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
23851
23852     /**
23853      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
23854      */
23855 /*
23856  * Based on:
23857  * Ext JS Library 1.1.1
23858  * Copyright(c) 2006-2007, Ext JS, LLC.
23859  *
23860  * Originally Released Under LGPL - original licence link has changed is not relivant.
23861  *
23862  * Fork - LGPL
23863  * <script type="text/javascript">
23864  */
23865 /**
23866  * @class Roo.data.MemoryProxy
23867  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
23868  * to the Reader when its load method is called.
23869  * @constructor
23870  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
23871  */
23872 Roo.data.MemoryProxy = function(data){
23873     if (data.data) {
23874         data = data.data;
23875     }
23876     Roo.data.MemoryProxy.superclass.constructor.call(this);
23877     this.data = data;
23878 };
23879
23880 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
23881     
23882     /**
23883      * Load data from the requested source (in this case an in-memory
23884      * data object passed to the constructor), read the data object into
23885      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
23886      * process that block using the passed callback.
23887      * @param {Object} params This parameter is not used by the MemoryProxy class.
23888      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23889      * object into a block of Roo.data.Records.
23890      * @param {Function} callback The function into which to pass the block of Roo.data.records.
23891      * The function must be passed <ul>
23892      * <li>The Record block object</li>
23893      * <li>The "arg" argument from the load function</li>
23894      * <li>A boolean success indicator</li>
23895      * </ul>
23896      * @param {Object} scope The scope in which to call the callback
23897      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23898      */
23899     load : function(params, reader, callback, scope, arg){
23900         params = params || {};
23901         var result;
23902         try {
23903             result = reader.readRecords(params.data ? params.data :this.data);
23904         }catch(e){
23905             this.fireEvent("loadexception", this, arg, null, e);
23906             callback.call(scope, null, arg, false);
23907             return;
23908         }
23909         callback.call(scope, result, arg, true);
23910     },
23911     
23912     // private
23913     update : function(params, records){
23914         
23915     }
23916 });/*
23917  * Based on:
23918  * Ext JS Library 1.1.1
23919  * Copyright(c) 2006-2007, Ext JS, LLC.
23920  *
23921  * Originally Released Under LGPL - original licence link has changed is not relivant.
23922  *
23923  * Fork - LGPL
23924  * <script type="text/javascript">
23925  */
23926 /**
23927  * @class Roo.data.HttpProxy
23928  * @extends Roo.data.DataProxy
23929  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
23930  * configured to reference a certain URL.<br><br>
23931  * <p>
23932  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
23933  * from which the running page was served.<br><br>
23934  * <p>
23935  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
23936  * <p>
23937  * Be aware that to enable the browser to parse an XML document, the server must set
23938  * the Content-Type header in the HTTP response to "text/xml".
23939  * @constructor
23940  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
23941  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
23942  * will be used to make the request.
23943  */
23944 Roo.data.HttpProxy = function(conn){
23945     Roo.data.HttpProxy.superclass.constructor.call(this);
23946     // is conn a conn config or a real conn?
23947     this.conn = conn;
23948     this.useAjax = !conn || !conn.events;
23949   
23950 };
23951
23952 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
23953     // thse are take from connection...
23954     
23955     /**
23956      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
23957      */
23958     /**
23959      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
23960      * extra parameters to each request made by this object. (defaults to undefined)
23961      */
23962     /**
23963      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
23964      *  to each request made by this object. (defaults to undefined)
23965      */
23966     /**
23967      * @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)
23968      */
23969     /**
23970      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
23971      */
23972      /**
23973      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
23974      * @type Boolean
23975      */
23976   
23977
23978     /**
23979      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
23980      * @type Boolean
23981      */
23982     /**
23983      * Return the {@link Roo.data.Connection} object being used by this Proxy.
23984      * @return {Connection} The Connection object. This object may be used to subscribe to events on
23985      * a finer-grained basis than the DataProxy events.
23986      */
23987     getConnection : function(){
23988         return this.useAjax ? Roo.Ajax : this.conn;
23989     },
23990
23991     /**
23992      * Load data from the configured {@link Roo.data.Connection}, read the data object into
23993      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
23994      * process that block using the passed callback.
23995      * @param {Object} params An object containing properties which are to be used as HTTP parameters
23996      * for the request to the remote server.
23997      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23998      * object into a block of Roo.data.Records.
23999      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
24000      * The function must be passed <ul>
24001      * <li>The Record block object</li>
24002      * <li>The "arg" argument from the load function</li>
24003      * <li>A boolean success indicator</li>
24004      * </ul>
24005      * @param {Object} scope The scope in which to call the callback
24006      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24007      */
24008     load : function(params, reader, callback, scope, arg){
24009         if(this.fireEvent("beforeload", this, params) !== false){
24010             var  o = {
24011                 params : params || {},
24012                 request: {
24013                     callback : callback,
24014                     scope : scope,
24015                     arg : arg
24016                 },
24017                 reader: reader,
24018                 callback : this.loadResponse,
24019                 scope: this
24020             };
24021             if(this.useAjax){
24022                 Roo.applyIf(o, this.conn);
24023                 if(this.activeRequest){
24024                     Roo.Ajax.abort(this.activeRequest);
24025                 }
24026                 this.activeRequest = Roo.Ajax.request(o);
24027             }else{
24028                 this.conn.request(o);
24029             }
24030         }else{
24031             callback.call(scope||this, null, arg, false);
24032         }
24033     },
24034
24035     // private
24036     loadResponse : function(o, success, response){
24037         delete this.activeRequest;
24038         if(!success){
24039             this.fireEvent("loadexception", this, o, response);
24040             o.request.callback.call(o.request.scope, null, o.request.arg, false);
24041             return;
24042         }
24043         var result;
24044         try {
24045             result = o.reader.read(response);
24046         }catch(e){
24047             this.fireEvent("loadexception", this, o, response, e);
24048             o.request.callback.call(o.request.scope, null, o.request.arg, false);
24049             return;
24050         }
24051         
24052         this.fireEvent("load", this, o, o.request.arg);
24053         o.request.callback.call(o.request.scope, result, o.request.arg, true);
24054     },
24055
24056     // private
24057     update : function(dataSet){
24058
24059     },
24060
24061     // private
24062     updateResponse : function(dataSet){
24063
24064     }
24065 });/*
24066  * Based on:
24067  * Ext JS Library 1.1.1
24068  * Copyright(c) 2006-2007, Ext JS, LLC.
24069  *
24070  * Originally Released Under LGPL - original licence link has changed is not relivant.
24071  *
24072  * Fork - LGPL
24073  * <script type="text/javascript">
24074  */
24075
24076 /**
24077  * @class Roo.data.ScriptTagProxy
24078  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
24079  * other than the originating domain of the running page.<br><br>
24080  * <p>
24081  * <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
24082  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
24083  * <p>
24084  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
24085  * source code that is used as the source inside a &lt;script> tag.<br><br>
24086  * <p>
24087  * In order for the browser to process the returned data, the server must wrap the data object
24088  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
24089  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
24090  * depending on whether the callback name was passed:
24091  * <p>
24092  * <pre><code>
24093 boolean scriptTag = false;
24094 String cb = request.getParameter("callback");
24095 if (cb != null) {
24096     scriptTag = true;
24097     response.setContentType("text/javascript");
24098 } else {
24099     response.setContentType("application/x-json");
24100 }
24101 Writer out = response.getWriter();
24102 if (scriptTag) {
24103     out.write(cb + "(");
24104 }
24105 out.print(dataBlock.toJsonString());
24106 if (scriptTag) {
24107     out.write(");");
24108 }
24109 </pre></code>
24110  *
24111  * @constructor
24112  * @param {Object} config A configuration object.
24113  */
24114 Roo.data.ScriptTagProxy = function(config){
24115     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
24116     Roo.apply(this, config);
24117     this.head = document.getElementsByTagName("head")[0];
24118 };
24119
24120 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
24121
24122 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
24123     /**
24124      * @cfg {String} url The URL from which to request the data object.
24125      */
24126     /**
24127      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
24128      */
24129     timeout : 30000,
24130     /**
24131      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
24132      * the server the name of the callback function set up by the load call to process the returned data object.
24133      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
24134      * javascript output which calls this named function passing the data object as its only parameter.
24135      */
24136     callbackParam : "callback",
24137     /**
24138      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
24139      * name to the request.
24140      */
24141     nocache : true,
24142
24143     /**
24144      * Load data from the configured URL, read the data object into
24145      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
24146      * process that block using the passed callback.
24147      * @param {Object} params An object containing properties which are to be used as HTTP parameters
24148      * for the request to the remote server.
24149      * @param {Roo.data.DataReader} reader The Reader object which converts the data
24150      * object into a block of Roo.data.Records.
24151      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
24152      * The function must be passed <ul>
24153      * <li>The Record block object</li>
24154      * <li>The "arg" argument from the load function</li>
24155      * <li>A boolean success indicator</li>
24156      * </ul>
24157      * @param {Object} scope The scope in which to call the callback
24158      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24159      */
24160     load : function(params, reader, callback, scope, arg){
24161         if(this.fireEvent("beforeload", this, params) !== false){
24162
24163             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
24164
24165             var url = this.url;
24166             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
24167             if(this.nocache){
24168                 url += "&_dc=" + (new Date().getTime());
24169             }
24170             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
24171             var trans = {
24172                 id : transId,
24173                 cb : "stcCallback"+transId,
24174                 scriptId : "stcScript"+transId,
24175                 params : params,
24176                 arg : arg,
24177                 url : url,
24178                 callback : callback,
24179                 scope : scope,
24180                 reader : reader
24181             };
24182             var conn = this;
24183
24184             window[trans.cb] = function(o){
24185                 conn.handleResponse(o, trans);
24186             };
24187
24188             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
24189
24190             if(this.autoAbort !== false){
24191                 this.abort();
24192             }
24193
24194             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
24195
24196             var script = document.createElement("script");
24197             script.setAttribute("src", url);
24198             script.setAttribute("type", "text/javascript");
24199             script.setAttribute("id", trans.scriptId);
24200             this.head.appendChild(script);
24201
24202             this.trans = trans;
24203         }else{
24204             callback.call(scope||this, null, arg, false);
24205         }
24206     },
24207
24208     // private
24209     isLoading : function(){
24210         return this.trans ? true : false;
24211     },
24212
24213     /**
24214      * Abort the current server request.
24215      */
24216     abort : function(){
24217         if(this.isLoading()){
24218             this.destroyTrans(this.trans);
24219         }
24220     },
24221
24222     // private
24223     destroyTrans : function(trans, isLoaded){
24224         this.head.removeChild(document.getElementById(trans.scriptId));
24225         clearTimeout(trans.timeoutId);
24226         if(isLoaded){
24227             window[trans.cb] = undefined;
24228             try{
24229                 delete window[trans.cb];
24230             }catch(e){}
24231         }else{
24232             // if hasn't been loaded, wait for load to remove it to prevent script error
24233             window[trans.cb] = function(){
24234                 window[trans.cb] = undefined;
24235                 try{
24236                     delete window[trans.cb];
24237                 }catch(e){}
24238             };
24239         }
24240     },
24241
24242     // private
24243     handleResponse : function(o, trans){
24244         this.trans = false;
24245         this.destroyTrans(trans, true);
24246         var result;
24247         try {
24248             result = trans.reader.readRecords(o);
24249         }catch(e){
24250             this.fireEvent("loadexception", this, o, trans.arg, e);
24251             trans.callback.call(trans.scope||window, null, trans.arg, false);
24252             return;
24253         }
24254         this.fireEvent("load", this, o, trans.arg);
24255         trans.callback.call(trans.scope||window, result, trans.arg, true);
24256     },
24257
24258     // private
24259     handleFailure : function(trans){
24260         this.trans = false;
24261         this.destroyTrans(trans, false);
24262         this.fireEvent("loadexception", this, null, trans.arg);
24263         trans.callback.call(trans.scope||window, null, trans.arg, false);
24264     }
24265 });/*
24266  * Based on:
24267  * Ext JS Library 1.1.1
24268  * Copyright(c) 2006-2007, Ext JS, LLC.
24269  *
24270  * Originally Released Under LGPL - original licence link has changed is not relivant.
24271  *
24272  * Fork - LGPL
24273  * <script type="text/javascript">
24274  */
24275
24276 /**
24277  * @class Roo.data.JsonReader
24278  * @extends Roo.data.DataReader
24279  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
24280  * based on mappings in a provided Roo.data.Record constructor.
24281  * 
24282  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
24283  * in the reply previously. 
24284  * 
24285  * <p>
24286  * Example code:
24287  * <pre><code>
24288 var RecordDef = Roo.data.Record.create([
24289     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24290     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24291 ]);
24292 var myReader = new Roo.data.JsonReader({
24293     totalProperty: "results",    // The property which contains the total dataset size (optional)
24294     root: "rows",                // The property which contains an Array of row objects
24295     id: "id"                     // The property within each row object that provides an ID for the record (optional)
24296 }, RecordDef);
24297 </code></pre>
24298  * <p>
24299  * This would consume a JSON file like this:
24300  * <pre><code>
24301 { 'results': 2, 'rows': [
24302     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
24303     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
24304 }
24305 </code></pre>
24306  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
24307  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24308  * paged from the remote server.
24309  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
24310  * @cfg {String} root name of the property which contains the Array of row objects.
24311  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
24312  * @cfg {Array} fields Array of field definition objects
24313  * @constructor
24314  * Create a new JsonReader
24315  * @param {Object} meta Metadata configuration options
24316  * @param {Object} recordType Either an Array of field definition objects,
24317  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
24318  */
24319 Roo.data.JsonReader = function(meta, recordType){
24320     
24321     meta = meta || {};
24322     // set some defaults:
24323     Roo.applyIf(meta, {
24324         totalProperty: 'total',
24325         successProperty : 'success',
24326         root : 'data',
24327         id : 'id'
24328     });
24329     
24330     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24331 };
24332 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
24333     
24334     readerType : 'Json',
24335     
24336     /**
24337      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
24338      * Used by Store query builder to append _requestMeta to params.
24339      * 
24340      */
24341     metaFromRemote : false,
24342     /**
24343      * This method is only used by a DataProxy which has retrieved data from a remote server.
24344      * @param {Object} response The XHR object which contains the JSON data in its responseText.
24345      * @return {Object} data A data block which is used by an Roo.data.Store object as
24346      * a cache of Roo.data.Records.
24347      */
24348     read : function(response){
24349         var json = response.responseText;
24350        
24351         var o = /* eval:var:o */ eval("("+json+")");
24352         if(!o) {
24353             throw {message: "JsonReader.read: Json object not found"};
24354         }
24355         
24356         if(o.metaData){
24357             
24358             delete this.ef;
24359             this.metaFromRemote = true;
24360             this.meta = o.metaData;
24361             this.recordType = Roo.data.Record.create(o.metaData.fields);
24362             this.onMetaChange(this.meta, this.recordType, o);
24363         }
24364         return this.readRecords(o);
24365     },
24366
24367     // private function a store will implement
24368     onMetaChange : function(meta, recordType, o){
24369
24370     },
24371
24372     /**
24373          * @ignore
24374          */
24375     simpleAccess: function(obj, subsc) {
24376         return obj[subsc];
24377     },
24378
24379         /**
24380          * @ignore
24381          */
24382     getJsonAccessor: function(){
24383         var re = /[\[\.]/;
24384         return function(expr) {
24385             try {
24386                 return(re.test(expr))
24387                     ? new Function("obj", "return obj." + expr)
24388                     : function(obj){
24389                         return obj[expr];
24390                     };
24391             } catch(e){}
24392             return Roo.emptyFn;
24393         };
24394     }(),
24395
24396     /**
24397      * Create a data block containing Roo.data.Records from an XML document.
24398      * @param {Object} o An object which contains an Array of row objects in the property specified
24399      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
24400      * which contains the total size of the dataset.
24401      * @return {Object} data A data block which is used by an Roo.data.Store object as
24402      * a cache of Roo.data.Records.
24403      */
24404     readRecords : function(o){
24405         /**
24406          * After any data loads, the raw JSON data is available for further custom processing.
24407          * @type Object
24408          */
24409         this.o = o;
24410         var s = this.meta, Record = this.recordType,
24411             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
24412
24413 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
24414         if (!this.ef) {
24415             if(s.totalProperty) {
24416                     this.getTotal = this.getJsonAccessor(s.totalProperty);
24417                 }
24418                 if(s.successProperty) {
24419                     this.getSuccess = this.getJsonAccessor(s.successProperty);
24420                 }
24421                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
24422                 if (s.id) {
24423                         var g = this.getJsonAccessor(s.id);
24424                         this.getId = function(rec) {
24425                                 var r = g(rec);  
24426                                 return (r === undefined || r === "") ? null : r;
24427                         };
24428                 } else {
24429                         this.getId = function(){return null;};
24430                 }
24431             this.ef = [];
24432             for(var jj = 0; jj < fl; jj++){
24433                 f = fi[jj];
24434                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
24435                 this.ef[jj] = this.getJsonAccessor(map);
24436             }
24437         }
24438
24439         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
24440         if(s.totalProperty){
24441             var vt = parseInt(this.getTotal(o), 10);
24442             if(!isNaN(vt)){
24443                 totalRecords = vt;
24444             }
24445         }
24446         if(s.successProperty){
24447             var vs = this.getSuccess(o);
24448             if(vs === false || vs === 'false'){
24449                 success = false;
24450             }
24451         }
24452         var records = [];
24453         for(var i = 0; i < c; i++){
24454                 var n = root[i];
24455             var values = {};
24456             var id = this.getId(n);
24457             for(var j = 0; j < fl; j++){
24458                 f = fi[j];
24459             var v = this.ef[j](n);
24460             if (!f.convert) {
24461                 Roo.log('missing convert for ' + f.name);
24462                 Roo.log(f);
24463                 continue;
24464             }
24465             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
24466             }
24467             var record = new Record(values, id);
24468             record.json = n;
24469             records[i] = record;
24470         }
24471         return {
24472             raw : o,
24473             success : success,
24474             records : records,
24475             totalRecords : totalRecords
24476         };
24477     },
24478     // used when loading children.. @see loadDataFromChildren
24479     toLoadData: function(rec)
24480     {
24481         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
24482         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
24483         return { data : data, total : data.length };
24484         
24485     }
24486 });/*
24487  * Based on:
24488  * Ext JS Library 1.1.1
24489  * Copyright(c) 2006-2007, Ext JS, LLC.
24490  *
24491  * Originally Released Under LGPL - original licence link has changed is not relivant.
24492  *
24493  * Fork - LGPL
24494  * <script type="text/javascript">
24495  */
24496
24497 /**
24498  * @class Roo.data.XmlReader
24499  * @extends Roo.data.DataReader
24500  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
24501  * based on mappings in a provided Roo.data.Record constructor.<br><br>
24502  * <p>
24503  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
24504  * header in the HTTP response must be set to "text/xml".</em>
24505  * <p>
24506  * Example code:
24507  * <pre><code>
24508 var RecordDef = Roo.data.Record.create([
24509    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24510    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24511 ]);
24512 var myReader = new Roo.data.XmlReader({
24513    totalRecords: "results", // The element which contains the total dataset size (optional)
24514    record: "row",           // The repeated element which contains row information
24515    id: "id"                 // The element within the row that provides an ID for the record (optional)
24516 }, RecordDef);
24517 </code></pre>
24518  * <p>
24519  * This would consume an XML file like this:
24520  * <pre><code>
24521 &lt;?xml?>
24522 &lt;dataset>
24523  &lt;results>2&lt;/results>
24524  &lt;row>
24525    &lt;id>1&lt;/id>
24526    &lt;name>Bill&lt;/name>
24527    &lt;occupation>Gardener&lt;/occupation>
24528  &lt;/row>
24529  &lt;row>
24530    &lt;id>2&lt;/id>
24531    &lt;name>Ben&lt;/name>
24532    &lt;occupation>Horticulturalist&lt;/occupation>
24533  &lt;/row>
24534 &lt;/dataset>
24535 </code></pre>
24536  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
24537  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24538  * paged from the remote server.
24539  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
24540  * @cfg {String} success The DomQuery path to the success attribute used by forms.
24541  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
24542  * a record identifier value.
24543  * @constructor
24544  * Create a new XmlReader
24545  * @param {Object} meta Metadata configuration options
24546  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
24547  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
24548  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
24549  */
24550 Roo.data.XmlReader = function(meta, recordType){
24551     meta = meta || {};
24552     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24553 };
24554 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
24555     
24556     readerType : 'Xml',
24557     
24558     /**
24559      * This method is only used by a DataProxy which has retrieved data from a remote server.
24560          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
24561          * to contain a method called 'responseXML' that returns an XML document object.
24562      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
24563      * a cache of Roo.data.Records.
24564      */
24565     read : function(response){
24566         var doc = response.responseXML;
24567         if(!doc) {
24568             throw {message: "XmlReader.read: XML Document not available"};
24569         }
24570         return this.readRecords(doc);
24571     },
24572
24573     /**
24574      * Create a data block containing Roo.data.Records from an XML document.
24575          * @param {Object} doc A parsed XML document.
24576      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
24577      * a cache of Roo.data.Records.
24578      */
24579     readRecords : function(doc){
24580         /**
24581          * After any data loads/reads, the raw XML Document is available for further custom processing.
24582          * @type XMLDocument
24583          */
24584         this.xmlData = doc;
24585         var root = doc.documentElement || doc;
24586         var q = Roo.DomQuery;
24587         var recordType = this.recordType, fields = recordType.prototype.fields;
24588         var sid = this.meta.id;
24589         var totalRecords = 0, success = true;
24590         if(this.meta.totalRecords){
24591             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
24592         }
24593         
24594         if(this.meta.success){
24595             var sv = q.selectValue(this.meta.success, root, true);
24596             success = sv !== false && sv !== 'false';
24597         }
24598         var records = [];
24599         var ns = q.select(this.meta.record, root);
24600         for(var i = 0, len = ns.length; i < len; i++) {
24601                 var n = ns[i];
24602                 var values = {};
24603                 var id = sid ? q.selectValue(sid, n) : undefined;
24604                 for(var j = 0, jlen = fields.length; j < jlen; j++){
24605                     var f = fields.items[j];
24606                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
24607                     v = f.convert(v);
24608                     values[f.name] = v;
24609                 }
24610                 var record = new recordType(values, id);
24611                 record.node = n;
24612                 records[records.length] = record;
24613             }
24614
24615             return {
24616                 success : success,
24617                 records : records,
24618                 totalRecords : totalRecords || records.length
24619             };
24620     }
24621 });/*
24622  * Based on:
24623  * Ext JS Library 1.1.1
24624  * Copyright(c) 2006-2007, Ext JS, LLC.
24625  *
24626  * Originally Released Under LGPL - original licence link has changed is not relivant.
24627  *
24628  * Fork - LGPL
24629  * <script type="text/javascript">
24630  */
24631
24632 /**
24633  * @class Roo.data.ArrayReader
24634  * @extends Roo.data.DataReader
24635  * Data reader class to create an Array of Roo.data.Record objects from an Array.
24636  * Each element of that Array represents a row of data fields. The
24637  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
24638  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
24639  * <p>
24640  * Example code:.
24641  * <pre><code>
24642 var RecordDef = Roo.data.Record.create([
24643     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
24644     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
24645 ]);
24646 var myReader = new Roo.data.ArrayReader({
24647     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
24648 }, RecordDef);
24649 </code></pre>
24650  * <p>
24651  * This would consume an Array like this:
24652  * <pre><code>
24653 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
24654   </code></pre>
24655  
24656  * @constructor
24657  * Create a new JsonReader
24658  * @param {Object} meta Metadata configuration options.
24659  * @param {Object|Array} recordType Either an Array of field definition objects
24660  * 
24661  * @cfg {Array} fields Array of field definition objects
24662  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
24663  * as specified to {@link Roo.data.Record#create},
24664  * or an {@link Roo.data.Record} object
24665  *
24666  * 
24667  * created using {@link Roo.data.Record#create}.
24668  */
24669 Roo.data.ArrayReader = function(meta, recordType)
24670 {    
24671     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24672 };
24673
24674 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
24675     
24676       /**
24677      * Create a data block containing Roo.data.Records from an XML document.
24678      * @param {Object} o An Array of row objects which represents the dataset.
24679      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
24680      * a cache of Roo.data.Records.
24681      */
24682     readRecords : function(o)
24683     {
24684         var sid = this.meta ? this.meta.id : null;
24685         var recordType = this.recordType, fields = recordType.prototype.fields;
24686         var records = [];
24687         var root = o;
24688         for(var i = 0; i < root.length; i++){
24689                 var n = root[i];
24690             var values = {};
24691             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
24692             for(var j = 0, jlen = fields.length; j < jlen; j++){
24693                 var f = fields.items[j];
24694                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
24695                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
24696                 v = f.convert(v);
24697                 values[f.name] = v;
24698             }
24699             var record = new recordType(values, id);
24700             record.json = n;
24701             records[records.length] = record;
24702         }
24703         return {
24704             records : records,
24705             totalRecords : records.length
24706         };
24707     },
24708     // used when loading children.. @see loadDataFromChildren
24709     toLoadData: function(rec)
24710     {
24711         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
24712         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
24713         
24714     }
24715     
24716     
24717 });/*
24718  * Based on:
24719  * Ext JS Library 1.1.1
24720  * Copyright(c) 2006-2007, Ext JS, LLC.
24721  *
24722  * Originally Released Under LGPL - original licence link has changed is not relivant.
24723  *
24724  * Fork - LGPL
24725  * <script type="text/javascript">
24726  */
24727
24728
24729 /**
24730  * @class Roo.data.Tree
24731  * @extends Roo.util.Observable
24732  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
24733  * in the tree have most standard DOM functionality.
24734  * @constructor
24735  * @param {Node} root (optional) The root node
24736  */
24737 Roo.data.Tree = function(root){
24738    this.nodeHash = {};
24739    /**
24740     * The root node for this tree
24741     * @type Node
24742     */
24743    this.root = null;
24744    if(root){
24745        this.setRootNode(root);
24746    }
24747    this.addEvents({
24748        /**
24749         * @event append
24750         * Fires when a new child node is appended to a node in this tree.
24751         * @param {Tree} tree The owner tree
24752         * @param {Node} parent The parent node
24753         * @param {Node} node The newly appended node
24754         * @param {Number} index The index of the newly appended node
24755         */
24756        "append" : true,
24757        /**
24758         * @event remove
24759         * Fires when a child node is removed from a node in this tree.
24760         * @param {Tree} tree The owner tree
24761         * @param {Node} parent The parent node
24762         * @param {Node} node The child node removed
24763         */
24764        "remove" : true,
24765        /**
24766         * @event move
24767         * Fires when a node is moved to a new location in the tree
24768         * @param {Tree} tree The owner tree
24769         * @param {Node} node The node moved
24770         * @param {Node} oldParent The old parent of this node
24771         * @param {Node} newParent The new parent of this node
24772         * @param {Number} index The index it was moved to
24773         */
24774        "move" : true,
24775        /**
24776         * @event insert
24777         * Fires when a new child node is inserted in a node in this tree.
24778         * @param {Tree} tree The owner tree
24779         * @param {Node} parent The parent node
24780         * @param {Node} node The child node inserted
24781         * @param {Node} refNode The child node the node was inserted before
24782         */
24783        "insert" : true,
24784        /**
24785         * @event beforeappend
24786         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
24787         * @param {Tree} tree The owner tree
24788         * @param {Node} parent The parent node
24789         * @param {Node} node The child node to be appended
24790         */
24791        "beforeappend" : true,
24792        /**
24793         * @event beforeremove
24794         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
24795         * @param {Tree} tree The owner tree
24796         * @param {Node} parent The parent node
24797         * @param {Node} node The child node to be removed
24798         */
24799        "beforeremove" : true,
24800        /**
24801         * @event beforemove
24802         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
24803         * @param {Tree} tree The owner tree
24804         * @param {Node} node The node being moved
24805         * @param {Node} oldParent The parent of the node
24806         * @param {Node} newParent The new parent the node is moving to
24807         * @param {Number} index The index it is being moved to
24808         */
24809        "beforemove" : true,
24810        /**
24811         * @event beforeinsert
24812         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
24813         * @param {Tree} tree The owner tree
24814         * @param {Node} parent The parent node
24815         * @param {Node} node The child node to be inserted
24816         * @param {Node} refNode The child node the node is being inserted before
24817         */
24818        "beforeinsert" : true
24819    });
24820
24821     Roo.data.Tree.superclass.constructor.call(this);
24822 };
24823
24824 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
24825     pathSeparator: "/",
24826
24827     proxyNodeEvent : function(){
24828         return this.fireEvent.apply(this, arguments);
24829     },
24830
24831     /**
24832      * Returns the root node for this tree.
24833      * @return {Node}
24834      */
24835     getRootNode : function(){
24836         return this.root;
24837     },
24838
24839     /**
24840      * Sets the root node for this tree.
24841      * @param {Node} node
24842      * @return {Node}
24843      */
24844     setRootNode : function(node){
24845         this.root = node;
24846         node.ownerTree = this;
24847         node.isRoot = true;
24848         this.registerNode(node);
24849         return node;
24850     },
24851
24852     /**
24853      * Gets a node in this tree by its id.
24854      * @param {String} id
24855      * @return {Node}
24856      */
24857     getNodeById : function(id){
24858         return this.nodeHash[id];
24859     },
24860
24861     registerNode : function(node){
24862         this.nodeHash[node.id] = node;
24863     },
24864
24865     unregisterNode : function(node){
24866         delete this.nodeHash[node.id];
24867     },
24868
24869     toString : function(){
24870         return "[Tree"+(this.id?" "+this.id:"")+"]";
24871     }
24872 });
24873
24874 /**
24875  * @class Roo.data.Node
24876  * @extends Roo.util.Observable
24877  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
24878  * @cfg {String} id The id for this node. If one is not specified, one is generated.
24879  * @constructor
24880  * @param {Object} attributes The attributes/config for the node
24881  */
24882 Roo.data.Node = function(attributes){
24883     /**
24884      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
24885      * @type {Object}
24886      */
24887     this.attributes = attributes || {};
24888     this.leaf = this.attributes.leaf;
24889     /**
24890      * The node id. @type String
24891      */
24892     this.id = this.attributes.id;
24893     if(!this.id){
24894         this.id = Roo.id(null, "ynode-");
24895         this.attributes.id = this.id;
24896     }
24897      
24898     
24899     /**
24900      * All child nodes of this node. @type Array
24901      */
24902     this.childNodes = [];
24903     if(!this.childNodes.indexOf){ // indexOf is a must
24904         this.childNodes.indexOf = function(o){
24905             for(var i = 0, len = this.length; i < len; i++){
24906                 if(this[i] == o) {
24907                     return i;
24908                 }
24909             }
24910             return -1;
24911         };
24912     }
24913     /**
24914      * The parent node for this node. @type Node
24915      */
24916     this.parentNode = null;
24917     /**
24918      * The first direct child node of this node, or null if this node has no child nodes. @type Node
24919      */
24920     this.firstChild = null;
24921     /**
24922      * The last direct child node of this node, or null if this node has no child nodes. @type Node
24923      */
24924     this.lastChild = null;
24925     /**
24926      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
24927      */
24928     this.previousSibling = null;
24929     /**
24930      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
24931      */
24932     this.nextSibling = null;
24933
24934     this.addEvents({
24935        /**
24936         * @event append
24937         * Fires when a new child node is appended
24938         * @param {Tree} tree The owner tree
24939         * @param {Node} this This node
24940         * @param {Node} node The newly appended node
24941         * @param {Number} index The index of the newly appended node
24942         */
24943        "append" : true,
24944        /**
24945         * @event remove
24946         * Fires when a child node is removed
24947         * @param {Tree} tree The owner tree
24948         * @param {Node} this This node
24949         * @param {Node} node The removed node
24950         */
24951        "remove" : true,
24952        /**
24953         * @event move
24954         * Fires when this node is moved to a new location in the tree
24955         * @param {Tree} tree The owner tree
24956         * @param {Node} this This node
24957         * @param {Node} oldParent The old parent of this node
24958         * @param {Node} newParent The new parent of this node
24959         * @param {Number} index The index it was moved to
24960         */
24961        "move" : true,
24962        /**
24963         * @event insert
24964         * Fires when a new child node is inserted.
24965         * @param {Tree} tree The owner tree
24966         * @param {Node} this This node
24967         * @param {Node} node The child node inserted
24968         * @param {Node} refNode The child node the node was inserted before
24969         */
24970        "insert" : true,
24971        /**
24972         * @event beforeappend
24973         * Fires before a new child is appended, return false to cancel the append.
24974         * @param {Tree} tree The owner tree
24975         * @param {Node} this This node
24976         * @param {Node} node The child node to be appended
24977         */
24978        "beforeappend" : true,
24979        /**
24980         * @event beforeremove
24981         * Fires before a child is removed, return false to cancel the remove.
24982         * @param {Tree} tree The owner tree
24983         * @param {Node} this This node
24984         * @param {Node} node The child node to be removed
24985         */
24986        "beforeremove" : true,
24987        /**
24988         * @event beforemove
24989         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
24990         * @param {Tree} tree The owner tree
24991         * @param {Node} this This node
24992         * @param {Node} oldParent The parent of this node
24993         * @param {Node} newParent The new parent this node is moving to
24994         * @param {Number} index The index it is being moved to
24995         */
24996        "beforemove" : true,
24997        /**
24998         * @event beforeinsert
24999         * Fires before a new child is inserted, return false to cancel the insert.
25000         * @param {Tree} tree The owner tree
25001         * @param {Node} this This node
25002         * @param {Node} node The child node to be inserted
25003         * @param {Node} refNode The child node the node is being inserted before
25004         */
25005        "beforeinsert" : true
25006    });
25007     this.listeners = this.attributes.listeners;
25008     Roo.data.Node.superclass.constructor.call(this);
25009 };
25010
25011 Roo.extend(Roo.data.Node, Roo.util.Observable, {
25012     fireEvent : function(evtName){
25013         // first do standard event for this node
25014         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
25015             return false;
25016         }
25017         // then bubble it up to the tree if the event wasn't cancelled
25018         var ot = this.getOwnerTree();
25019         if(ot){
25020             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
25021                 return false;
25022             }
25023         }
25024         return true;
25025     },
25026
25027     /**
25028      * Returns true if this node is a leaf
25029      * @return {Boolean}
25030      */
25031     isLeaf : function(){
25032         return this.leaf === true;
25033     },
25034
25035     // private
25036     setFirstChild : function(node){
25037         this.firstChild = node;
25038     },
25039
25040     //private
25041     setLastChild : function(node){
25042         this.lastChild = node;
25043     },
25044
25045
25046     /**
25047      * Returns true if this node is the last child of its parent
25048      * @return {Boolean}
25049      */
25050     isLast : function(){
25051        return (!this.parentNode ? true : this.parentNode.lastChild == this);
25052     },
25053
25054     /**
25055      * Returns true if this node is the first child of its parent
25056      * @return {Boolean}
25057      */
25058     isFirst : function(){
25059        return (!this.parentNode ? true : this.parentNode.firstChild == this);
25060     },
25061
25062     hasChildNodes : function(){
25063         return !this.isLeaf() && this.childNodes.length > 0;
25064     },
25065
25066     /**
25067      * Insert node(s) as the last child node of this node.
25068      * @param {Node/Array} node The node or Array of nodes to append
25069      * @return {Node} The appended node if single append, or null if an array was passed
25070      */
25071     appendChild : function(node){
25072         var multi = false;
25073         if(node instanceof Array){
25074             multi = node;
25075         }else if(arguments.length > 1){
25076             multi = arguments;
25077         }
25078         
25079         // if passed an array or multiple args do them one by one
25080         if(multi){
25081             for(var i = 0, len = multi.length; i < len; i++) {
25082                 this.appendChild(multi[i]);
25083             }
25084         }else{
25085             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
25086                 return false;
25087             }
25088             var index = this.childNodes.length;
25089             var oldParent = node.parentNode;
25090             // it's a move, make sure we move it cleanly
25091             if(oldParent){
25092                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
25093                     return false;
25094                 }
25095                 oldParent.removeChild(node);
25096             }
25097             
25098             index = this.childNodes.length;
25099             if(index == 0){
25100                 this.setFirstChild(node);
25101             }
25102             this.childNodes.push(node);
25103             node.parentNode = this;
25104             var ps = this.childNodes[index-1];
25105             if(ps){
25106                 node.previousSibling = ps;
25107                 ps.nextSibling = node;
25108             }else{
25109                 node.previousSibling = null;
25110             }
25111             node.nextSibling = null;
25112             this.setLastChild(node);
25113             node.setOwnerTree(this.getOwnerTree());
25114             this.fireEvent("append", this.ownerTree, this, node, index);
25115             if(this.ownerTree) {
25116                 this.ownerTree.fireEvent("appendnode", this, node, index);
25117             }
25118             if(oldParent){
25119                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
25120             }
25121             return node;
25122         }
25123     },
25124
25125     /**
25126      * Removes a child node from this node.
25127      * @param {Node} node The node to remove
25128      * @return {Node} The removed node
25129      */
25130     removeChild : function(node){
25131         var index = this.childNodes.indexOf(node);
25132         if(index == -1){
25133             return false;
25134         }
25135         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
25136             return false;
25137         }
25138
25139         // remove it from childNodes collection
25140         this.childNodes.splice(index, 1);
25141
25142         // update siblings
25143         if(node.previousSibling){
25144             node.previousSibling.nextSibling = node.nextSibling;
25145         }
25146         if(node.nextSibling){
25147             node.nextSibling.previousSibling = node.previousSibling;
25148         }
25149
25150         // update child refs
25151         if(this.firstChild == node){
25152             this.setFirstChild(node.nextSibling);
25153         }
25154         if(this.lastChild == node){
25155             this.setLastChild(node.previousSibling);
25156         }
25157
25158         node.setOwnerTree(null);
25159         // clear any references from the node
25160         node.parentNode = null;
25161         node.previousSibling = null;
25162         node.nextSibling = null;
25163         this.fireEvent("remove", this.ownerTree, this, node);
25164         return node;
25165     },
25166
25167     /**
25168      * Inserts the first node before the second node in this nodes childNodes collection.
25169      * @param {Node} node The node to insert
25170      * @param {Node} refNode The node to insert before (if null the node is appended)
25171      * @return {Node} The inserted node
25172      */
25173     insertBefore : function(node, refNode){
25174         if(!refNode){ // like standard Dom, refNode can be null for append
25175             return this.appendChild(node);
25176         }
25177         // nothing to do
25178         if(node == refNode){
25179             return false;
25180         }
25181
25182         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
25183             return false;
25184         }
25185         var index = this.childNodes.indexOf(refNode);
25186         var oldParent = node.parentNode;
25187         var refIndex = index;
25188
25189         // when moving internally, indexes will change after remove
25190         if(oldParent == this && this.childNodes.indexOf(node) < index){
25191             refIndex--;
25192         }
25193
25194         // it's a move, make sure we move it cleanly
25195         if(oldParent){
25196             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
25197                 return false;
25198             }
25199             oldParent.removeChild(node);
25200         }
25201         if(refIndex == 0){
25202             this.setFirstChild(node);
25203         }
25204         this.childNodes.splice(refIndex, 0, node);
25205         node.parentNode = this;
25206         var ps = this.childNodes[refIndex-1];
25207         if(ps){
25208             node.previousSibling = ps;
25209             ps.nextSibling = node;
25210         }else{
25211             node.previousSibling = null;
25212         }
25213         node.nextSibling = refNode;
25214         refNode.previousSibling = node;
25215         node.setOwnerTree(this.getOwnerTree());
25216         this.fireEvent("insert", this.ownerTree, this, node, refNode);
25217         if(oldParent){
25218             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
25219         }
25220         return node;
25221     },
25222
25223     /**
25224      * Returns the child node at the specified index.
25225      * @param {Number} index
25226      * @return {Node}
25227      */
25228     item : function(index){
25229         return this.childNodes[index];
25230     },
25231
25232     /**
25233      * Replaces one child node in this node with another.
25234      * @param {Node} newChild The replacement node
25235      * @param {Node} oldChild The node to replace
25236      * @return {Node} The replaced node
25237      */
25238     replaceChild : function(newChild, oldChild){
25239         this.insertBefore(newChild, oldChild);
25240         this.removeChild(oldChild);
25241         return oldChild;
25242     },
25243
25244     /**
25245      * Returns the index of a child node
25246      * @param {Node} node
25247      * @return {Number} The index of the node or -1 if it was not found
25248      */
25249     indexOf : function(child){
25250         return this.childNodes.indexOf(child);
25251     },
25252
25253     /**
25254      * Returns the tree this node is in.
25255      * @return {Tree}
25256      */
25257     getOwnerTree : function(){
25258         // if it doesn't have one, look for one
25259         if(!this.ownerTree){
25260             var p = this;
25261             while(p){
25262                 if(p.ownerTree){
25263                     this.ownerTree = p.ownerTree;
25264                     break;
25265                 }
25266                 p = p.parentNode;
25267             }
25268         }
25269         return this.ownerTree;
25270     },
25271
25272     /**
25273      * Returns depth of this node (the root node has a depth of 0)
25274      * @return {Number}
25275      */
25276     getDepth : function(){
25277         var depth = 0;
25278         var p = this;
25279         while(p.parentNode){
25280             ++depth;
25281             p = p.parentNode;
25282         }
25283         return depth;
25284     },
25285
25286     // private
25287     setOwnerTree : function(tree){
25288         // if it's move, we need to update everyone
25289         if(tree != this.ownerTree){
25290             if(this.ownerTree){
25291                 this.ownerTree.unregisterNode(this);
25292             }
25293             this.ownerTree = tree;
25294             var cs = this.childNodes;
25295             for(var i = 0, len = cs.length; i < len; i++) {
25296                 cs[i].setOwnerTree(tree);
25297             }
25298             if(tree){
25299                 tree.registerNode(this);
25300             }
25301         }
25302     },
25303
25304     /**
25305      * Returns the path for this node. The path can be used to expand or select this node programmatically.
25306      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
25307      * @return {String} The path
25308      */
25309     getPath : function(attr){
25310         attr = attr || "id";
25311         var p = this.parentNode;
25312         var b = [this.attributes[attr]];
25313         while(p){
25314             b.unshift(p.attributes[attr]);
25315             p = p.parentNode;
25316         }
25317         var sep = this.getOwnerTree().pathSeparator;
25318         return sep + b.join(sep);
25319     },
25320
25321     /**
25322      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25323      * function call will be the scope provided or the current node. The arguments to the function
25324      * will be the args provided or the current node. If the function returns false at any point,
25325      * the bubble is stopped.
25326      * @param {Function} fn The function to call
25327      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25328      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25329      */
25330     bubble : function(fn, scope, args){
25331         var p = this;
25332         while(p){
25333             if(fn.call(scope || p, args || p) === false){
25334                 break;
25335             }
25336             p = p.parentNode;
25337         }
25338     },
25339
25340     /**
25341      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25342      * function call will be the scope provided or the current node. The arguments to the function
25343      * will be the args provided or the current node. If the function returns false at any point,
25344      * the cascade is stopped on that branch.
25345      * @param {Function} fn The function to call
25346      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25347      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25348      */
25349     cascade : function(fn, scope, args){
25350         if(fn.call(scope || this, args || this) !== false){
25351             var cs = this.childNodes;
25352             for(var i = 0, len = cs.length; i < len; i++) {
25353                 cs[i].cascade(fn, scope, args);
25354             }
25355         }
25356     },
25357
25358     /**
25359      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
25360      * function call will be the scope provided or the current node. The arguments to the function
25361      * will be the args provided or the current node. If the function returns false at any point,
25362      * the iteration stops.
25363      * @param {Function} fn The function to call
25364      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25365      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25366      */
25367     eachChild : function(fn, scope, args){
25368         var cs = this.childNodes;
25369         for(var i = 0, len = cs.length; i < len; i++) {
25370                 if(fn.call(scope || this, args || cs[i]) === false){
25371                     break;
25372                 }
25373         }
25374     },
25375
25376     /**
25377      * Finds the first child that has the attribute with the specified value.
25378      * @param {String} attribute The attribute name
25379      * @param {Mixed} value The value to search for
25380      * @return {Node} The found child or null if none was found
25381      */
25382     findChild : function(attribute, value){
25383         var cs = this.childNodes;
25384         for(var i = 0, len = cs.length; i < len; i++) {
25385                 if(cs[i].attributes[attribute] == value){
25386                     return cs[i];
25387                 }
25388         }
25389         return null;
25390     },
25391
25392     /**
25393      * Finds the first child by a custom function. The child matches if the function passed
25394      * returns true.
25395      * @param {Function} fn
25396      * @param {Object} scope (optional)
25397      * @return {Node} The found child or null if none was found
25398      */
25399     findChildBy : function(fn, scope){
25400         var cs = this.childNodes;
25401         for(var i = 0, len = cs.length; i < len; i++) {
25402                 if(fn.call(scope||cs[i], cs[i]) === true){
25403                     return cs[i];
25404                 }
25405         }
25406         return null;
25407     },
25408
25409     /**
25410      * Sorts this nodes children using the supplied sort function
25411      * @param {Function} fn
25412      * @param {Object} scope (optional)
25413      */
25414     sort : function(fn, scope){
25415         var cs = this.childNodes;
25416         var len = cs.length;
25417         if(len > 0){
25418             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
25419             cs.sort(sortFn);
25420             for(var i = 0; i < len; i++){
25421                 var n = cs[i];
25422                 n.previousSibling = cs[i-1];
25423                 n.nextSibling = cs[i+1];
25424                 if(i == 0){
25425                     this.setFirstChild(n);
25426                 }
25427                 if(i == len-1){
25428                     this.setLastChild(n);
25429                 }
25430             }
25431         }
25432     },
25433
25434     /**
25435      * Returns true if this node is an ancestor (at any point) of the passed node.
25436      * @param {Node} node
25437      * @return {Boolean}
25438      */
25439     contains : function(node){
25440         return node.isAncestor(this);
25441     },
25442
25443     /**
25444      * Returns true if the passed node is an ancestor (at any point) of this node.
25445      * @param {Node} node
25446      * @return {Boolean}
25447      */
25448     isAncestor : function(node){
25449         var p = this.parentNode;
25450         while(p){
25451             if(p == node){
25452                 return true;
25453             }
25454             p = p.parentNode;
25455         }
25456         return false;
25457     },
25458
25459     toString : function(){
25460         return "[Node"+(this.id?" "+this.id:"")+"]";
25461     }
25462 });/*
25463  * Based on:
25464  * Ext JS Library 1.1.1
25465  * Copyright(c) 2006-2007, Ext JS, LLC.
25466  *
25467  * Originally Released Under LGPL - original licence link has changed is not relivant.
25468  *
25469  * Fork - LGPL
25470  * <script type="text/javascript">
25471  */
25472
25473
25474 /**
25475  * @class Roo.Shadow
25476  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
25477  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
25478  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
25479  * @constructor
25480  * Create a new Shadow
25481  * @param {Object} config The config object
25482  */
25483 Roo.Shadow = function(config){
25484     Roo.apply(this, config);
25485     if(typeof this.mode != "string"){
25486         this.mode = this.defaultMode;
25487     }
25488     var o = this.offset, a = {h: 0};
25489     var rad = Math.floor(this.offset/2);
25490     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
25491         case "drop":
25492             a.w = 0;
25493             a.l = a.t = o;
25494             a.t -= 1;
25495             if(Roo.isIE){
25496                 a.l -= this.offset + rad;
25497                 a.t -= this.offset + rad;
25498                 a.w -= rad;
25499                 a.h -= rad;
25500                 a.t += 1;
25501             }
25502         break;
25503         case "sides":
25504             a.w = (o*2);
25505             a.l = -o;
25506             a.t = o-1;
25507             if(Roo.isIE){
25508                 a.l -= (this.offset - rad);
25509                 a.t -= this.offset + rad;
25510                 a.l += 1;
25511                 a.w -= (this.offset - rad)*2;
25512                 a.w -= rad + 1;
25513                 a.h -= 1;
25514             }
25515         break;
25516         case "frame":
25517             a.w = a.h = (o*2);
25518             a.l = a.t = -o;
25519             a.t += 1;
25520             a.h -= 2;
25521             if(Roo.isIE){
25522                 a.l -= (this.offset - rad);
25523                 a.t -= (this.offset - rad);
25524                 a.l += 1;
25525                 a.w -= (this.offset + rad + 1);
25526                 a.h -= (this.offset + rad);
25527                 a.h += 1;
25528             }
25529         break;
25530     };
25531
25532     this.adjusts = a;
25533 };
25534
25535 Roo.Shadow.prototype = {
25536     /**
25537      * @cfg {String} mode
25538      * The shadow display mode.  Supports the following options:<br />
25539      * sides: Shadow displays on both sides and bottom only<br />
25540      * frame: Shadow displays equally on all four sides<br />
25541      * drop: Traditional bottom-right drop shadow (default)
25542      */
25543     /**
25544      * @cfg {String} offset
25545      * The number of pixels to offset the shadow from the element (defaults to 4)
25546      */
25547     offset: 4,
25548
25549     // private
25550     defaultMode: "drop",
25551
25552     /**
25553      * Displays the shadow under the target element
25554      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
25555      */
25556     show : function(target){
25557         target = Roo.get(target);
25558         if(!this.el){
25559             this.el = Roo.Shadow.Pool.pull();
25560             if(this.el.dom.nextSibling != target.dom){
25561                 this.el.insertBefore(target);
25562             }
25563         }
25564         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
25565         if(Roo.isIE){
25566             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
25567         }
25568         this.realign(
25569             target.getLeft(true),
25570             target.getTop(true),
25571             target.getWidth(),
25572             target.getHeight()
25573         );
25574         this.el.dom.style.display = "block";
25575     },
25576
25577     /**
25578      * Returns true if the shadow is visible, else false
25579      */
25580     isVisible : function(){
25581         return this.el ? true : false;  
25582     },
25583
25584     /**
25585      * Direct alignment when values are already available. Show must be called at least once before
25586      * calling this method to ensure it is initialized.
25587      * @param {Number} left The target element left position
25588      * @param {Number} top The target element top position
25589      * @param {Number} width The target element width
25590      * @param {Number} height The target element height
25591      */
25592     realign : function(l, t, w, h){
25593         if(!this.el){
25594             return;
25595         }
25596         var a = this.adjusts, d = this.el.dom, s = d.style;
25597         var iea = 0;
25598         s.left = (l+a.l)+"px";
25599         s.top = (t+a.t)+"px";
25600         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
25601  
25602         if(s.width != sws || s.height != shs){
25603             s.width = sws;
25604             s.height = shs;
25605             if(!Roo.isIE){
25606                 var cn = d.childNodes;
25607                 var sww = Math.max(0, (sw-12))+"px";
25608                 cn[0].childNodes[1].style.width = sww;
25609                 cn[1].childNodes[1].style.width = sww;
25610                 cn[2].childNodes[1].style.width = sww;
25611                 cn[1].style.height = Math.max(0, (sh-12))+"px";
25612             }
25613         }
25614     },
25615
25616     /**
25617      * Hides this shadow
25618      */
25619     hide : function(){
25620         if(this.el){
25621             this.el.dom.style.display = "none";
25622             Roo.Shadow.Pool.push(this.el);
25623             delete this.el;
25624         }
25625     },
25626
25627     /**
25628      * Adjust the z-index of this shadow
25629      * @param {Number} zindex The new z-index
25630      */
25631     setZIndex : function(z){
25632         this.zIndex = z;
25633         if(this.el){
25634             this.el.setStyle("z-index", z);
25635         }
25636     }
25637 };
25638
25639 // Private utility class that manages the internal Shadow cache
25640 Roo.Shadow.Pool = function(){
25641     var p = [];
25642     var markup = Roo.isIE ?
25643                  '<div class="x-ie-shadow"></div>' :
25644                  '<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>';
25645     return {
25646         pull : function(){
25647             var sh = p.shift();
25648             if(!sh){
25649                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
25650                 sh.autoBoxAdjust = false;
25651             }
25652             return sh;
25653         },
25654
25655         push : function(sh){
25656             p.push(sh);
25657         }
25658     };
25659 }();/*
25660  * Based on:
25661  * Ext JS Library 1.1.1
25662  * Copyright(c) 2006-2007, Ext JS, LLC.
25663  *
25664  * Originally Released Under LGPL - original licence link has changed is not relivant.
25665  *
25666  * Fork - LGPL
25667  * <script type="text/javascript">
25668  */
25669
25670
25671 /**
25672  * @class Roo.SplitBar
25673  * @extends Roo.util.Observable
25674  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
25675  * <br><br>
25676  * Usage:
25677  * <pre><code>
25678 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
25679                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
25680 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
25681 split.minSize = 100;
25682 split.maxSize = 600;
25683 split.animate = true;
25684 split.on('moved', splitterMoved);
25685 </code></pre>
25686  * @constructor
25687  * Create a new SplitBar
25688  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
25689  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
25690  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
25691  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
25692                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
25693                         position of the SplitBar).
25694  */
25695 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
25696     
25697     /** @private */
25698     this.el = Roo.get(dragElement, true);
25699     this.el.dom.unselectable = "on";
25700     /** @private */
25701     this.resizingEl = Roo.get(resizingElement, true);
25702
25703     /**
25704      * @private
25705      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
25706      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
25707      * @type Number
25708      */
25709     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
25710     
25711     /**
25712      * The minimum size of the resizing element. (Defaults to 0)
25713      * @type Number
25714      */
25715     this.minSize = 0;
25716     
25717     /**
25718      * The maximum size of the resizing element. (Defaults to 2000)
25719      * @type Number
25720      */
25721     this.maxSize = 2000;
25722     
25723     /**
25724      * Whether to animate the transition to the new size
25725      * @type Boolean
25726      */
25727     this.animate = false;
25728     
25729     /**
25730      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
25731      * @type Boolean
25732      */
25733     this.useShim = false;
25734     
25735     /** @private */
25736     this.shim = null;
25737     
25738     if(!existingProxy){
25739         /** @private */
25740         this.proxy = Roo.SplitBar.createProxy(this.orientation);
25741     }else{
25742         this.proxy = Roo.get(existingProxy).dom;
25743     }
25744     /** @private */
25745     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
25746     
25747     /** @private */
25748     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
25749     
25750     /** @private */
25751     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
25752     
25753     /** @private */
25754     this.dragSpecs = {};
25755     
25756     /**
25757      * @private The adapter to use to positon and resize elements
25758      */
25759     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
25760     this.adapter.init(this);
25761     
25762     if(this.orientation == Roo.SplitBar.HORIZONTAL){
25763         /** @private */
25764         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
25765         this.el.addClass("x-splitbar-h");
25766     }else{
25767         /** @private */
25768         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
25769         this.el.addClass("x-splitbar-v");
25770     }
25771     
25772     this.addEvents({
25773         /**
25774          * @event resize
25775          * Fires when the splitter is moved (alias for {@link #event-moved})
25776          * @param {Roo.SplitBar} this
25777          * @param {Number} newSize the new width or height
25778          */
25779         "resize" : true,
25780         /**
25781          * @event moved
25782          * Fires when the splitter is moved
25783          * @param {Roo.SplitBar} this
25784          * @param {Number} newSize the new width or height
25785          */
25786         "moved" : true,
25787         /**
25788          * @event beforeresize
25789          * Fires before the splitter is dragged
25790          * @param {Roo.SplitBar} this
25791          */
25792         "beforeresize" : true,
25793
25794         "beforeapply" : true
25795     });
25796
25797     Roo.util.Observable.call(this);
25798 };
25799
25800 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
25801     onStartProxyDrag : function(x, y){
25802         this.fireEvent("beforeresize", this);
25803         if(!this.overlay){
25804             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
25805             o.unselectable();
25806             o.enableDisplayMode("block");
25807             // all splitbars share the same overlay
25808             Roo.SplitBar.prototype.overlay = o;
25809         }
25810         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
25811         this.overlay.show();
25812         Roo.get(this.proxy).setDisplayed("block");
25813         var size = this.adapter.getElementSize(this);
25814         this.activeMinSize = this.getMinimumSize();;
25815         this.activeMaxSize = this.getMaximumSize();;
25816         var c1 = size - this.activeMinSize;
25817         var c2 = Math.max(this.activeMaxSize - size, 0);
25818         if(this.orientation == Roo.SplitBar.HORIZONTAL){
25819             this.dd.resetConstraints();
25820             this.dd.setXConstraint(
25821                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
25822                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
25823             );
25824             this.dd.setYConstraint(0, 0);
25825         }else{
25826             this.dd.resetConstraints();
25827             this.dd.setXConstraint(0, 0);
25828             this.dd.setYConstraint(
25829                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
25830                 this.placement == Roo.SplitBar.TOP ? c2 : c1
25831             );
25832          }
25833         this.dragSpecs.startSize = size;
25834         this.dragSpecs.startPoint = [x, y];
25835         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
25836     },
25837     
25838     /** 
25839      * @private Called after the drag operation by the DDProxy
25840      */
25841     onEndProxyDrag : function(e){
25842         Roo.get(this.proxy).setDisplayed(false);
25843         var endPoint = Roo.lib.Event.getXY(e);
25844         if(this.overlay){
25845             this.overlay.hide();
25846         }
25847         var newSize;
25848         if(this.orientation == Roo.SplitBar.HORIZONTAL){
25849             newSize = this.dragSpecs.startSize + 
25850                 (this.placement == Roo.SplitBar.LEFT ?
25851                     endPoint[0] - this.dragSpecs.startPoint[0] :
25852                     this.dragSpecs.startPoint[0] - endPoint[0]
25853                 );
25854         }else{
25855             newSize = this.dragSpecs.startSize + 
25856                 (this.placement == Roo.SplitBar.TOP ?
25857                     endPoint[1] - this.dragSpecs.startPoint[1] :
25858                     this.dragSpecs.startPoint[1] - endPoint[1]
25859                 );
25860         }
25861         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
25862         if(newSize != this.dragSpecs.startSize){
25863             if(this.fireEvent('beforeapply', this, newSize) !== false){
25864                 this.adapter.setElementSize(this, newSize);
25865                 this.fireEvent("moved", this, newSize);
25866                 this.fireEvent("resize", this, newSize);
25867             }
25868         }
25869     },
25870     
25871     /**
25872      * Get the adapter this SplitBar uses
25873      * @return The adapter object
25874      */
25875     getAdapter : function(){
25876         return this.adapter;
25877     },
25878     
25879     /**
25880      * Set the adapter this SplitBar uses
25881      * @param {Object} adapter A SplitBar adapter object
25882      */
25883     setAdapter : function(adapter){
25884         this.adapter = adapter;
25885         this.adapter.init(this);
25886     },
25887     
25888     /**
25889      * Gets the minimum size for the resizing element
25890      * @return {Number} The minimum size
25891      */
25892     getMinimumSize : function(){
25893         return this.minSize;
25894     },
25895     
25896     /**
25897      * Sets the minimum size for the resizing element
25898      * @param {Number} minSize The minimum size
25899      */
25900     setMinimumSize : function(minSize){
25901         this.minSize = minSize;
25902     },
25903     
25904     /**
25905      * Gets the maximum size for the resizing element
25906      * @return {Number} The maximum size
25907      */
25908     getMaximumSize : function(){
25909         return this.maxSize;
25910     },
25911     
25912     /**
25913      * Sets the maximum size for the resizing element
25914      * @param {Number} maxSize The maximum size
25915      */
25916     setMaximumSize : function(maxSize){
25917         this.maxSize = maxSize;
25918     },
25919     
25920     /**
25921      * Sets the initialize size for the resizing element
25922      * @param {Number} size The initial size
25923      */
25924     setCurrentSize : function(size){
25925         var oldAnimate = this.animate;
25926         this.animate = false;
25927         this.adapter.setElementSize(this, size);
25928         this.animate = oldAnimate;
25929     },
25930     
25931     /**
25932      * Destroy this splitbar. 
25933      * @param {Boolean} removeEl True to remove the element
25934      */
25935     destroy : function(removeEl){
25936         if(this.shim){
25937             this.shim.remove();
25938         }
25939         this.dd.unreg();
25940         this.proxy.parentNode.removeChild(this.proxy);
25941         if(removeEl){
25942             this.el.remove();
25943         }
25944     }
25945 });
25946
25947 /**
25948  * @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.
25949  */
25950 Roo.SplitBar.createProxy = function(dir){
25951     var proxy = new Roo.Element(document.createElement("div"));
25952     proxy.unselectable();
25953     var cls = 'x-splitbar-proxy';
25954     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
25955     document.body.appendChild(proxy.dom);
25956     return proxy.dom;
25957 };
25958
25959 /** 
25960  * @class Roo.SplitBar.BasicLayoutAdapter
25961  * Default Adapter. It assumes the splitter and resizing element are not positioned
25962  * elements and only gets/sets the width of the element. Generally used for table based layouts.
25963  */
25964 Roo.SplitBar.BasicLayoutAdapter = function(){
25965 };
25966
25967 Roo.SplitBar.BasicLayoutAdapter.prototype = {
25968     // do nothing for now
25969     init : function(s){
25970     
25971     },
25972     /**
25973      * Called before drag operations to get the current size of the resizing element. 
25974      * @param {Roo.SplitBar} s The SplitBar using this adapter
25975      */
25976      getElementSize : function(s){
25977         if(s.orientation == Roo.SplitBar.HORIZONTAL){
25978             return s.resizingEl.getWidth();
25979         }else{
25980             return s.resizingEl.getHeight();
25981         }
25982     },
25983     
25984     /**
25985      * Called after drag operations to set the size of the resizing element.
25986      * @param {Roo.SplitBar} s The SplitBar using this adapter
25987      * @param {Number} newSize The new size to set
25988      * @param {Function} onComplete A function to be invoked when resizing is complete
25989      */
25990     setElementSize : function(s, newSize, onComplete){
25991         if(s.orientation == Roo.SplitBar.HORIZONTAL){
25992             if(!s.animate){
25993                 s.resizingEl.setWidth(newSize);
25994                 if(onComplete){
25995                     onComplete(s, newSize);
25996                 }
25997             }else{
25998                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
25999             }
26000         }else{
26001             
26002             if(!s.animate){
26003                 s.resizingEl.setHeight(newSize);
26004                 if(onComplete){
26005                     onComplete(s, newSize);
26006                 }
26007             }else{
26008                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
26009             }
26010         }
26011     }
26012 };
26013
26014 /** 
26015  *@class Roo.SplitBar.AbsoluteLayoutAdapter
26016  * @extends Roo.SplitBar.BasicLayoutAdapter
26017  * Adapter that  moves the splitter element to align with the resized sizing element. 
26018  * Used with an absolute positioned SplitBar.
26019  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
26020  * document.body, make sure you assign an id to the body element.
26021  */
26022 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
26023     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
26024     this.container = Roo.get(container);
26025 };
26026
26027 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
26028     init : function(s){
26029         this.basic.init(s);
26030     },
26031     
26032     getElementSize : function(s){
26033         return this.basic.getElementSize(s);
26034     },
26035     
26036     setElementSize : function(s, newSize, onComplete){
26037         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
26038     },
26039     
26040     moveSplitter : function(s){
26041         var yes = Roo.SplitBar;
26042         switch(s.placement){
26043             case yes.LEFT:
26044                 s.el.setX(s.resizingEl.getRight());
26045                 break;
26046             case yes.RIGHT:
26047                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
26048                 break;
26049             case yes.TOP:
26050                 s.el.setY(s.resizingEl.getBottom());
26051                 break;
26052             case yes.BOTTOM:
26053                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
26054                 break;
26055         }
26056     }
26057 };
26058
26059 /**
26060  * Orientation constant - Create a vertical SplitBar
26061  * @static
26062  * @type Number
26063  */
26064 Roo.SplitBar.VERTICAL = 1;
26065
26066 /**
26067  * Orientation constant - Create a horizontal SplitBar
26068  * @static
26069  * @type Number
26070  */
26071 Roo.SplitBar.HORIZONTAL = 2;
26072
26073 /**
26074  * Placement constant - The resizing element is to the left of the splitter element
26075  * @static
26076  * @type Number
26077  */
26078 Roo.SplitBar.LEFT = 1;
26079
26080 /**
26081  * Placement constant - The resizing element is to the right of the splitter element
26082  * @static
26083  * @type Number
26084  */
26085 Roo.SplitBar.RIGHT = 2;
26086
26087 /**
26088  * Placement constant - The resizing element is positioned above the splitter element
26089  * @static
26090  * @type Number
26091  */
26092 Roo.SplitBar.TOP = 3;
26093
26094 /**
26095  * Placement constant - The resizing element is positioned under splitter element
26096  * @static
26097  * @type Number
26098  */
26099 Roo.SplitBar.BOTTOM = 4;
26100 /*
26101  * Based on:
26102  * Ext JS Library 1.1.1
26103  * Copyright(c) 2006-2007, Ext JS, LLC.
26104  *
26105  * Originally Released Under LGPL - original licence link has changed is not relivant.
26106  *
26107  * Fork - LGPL
26108  * <script type="text/javascript">
26109  */
26110
26111 /**
26112  * @class Roo.View
26113  * @extends Roo.util.Observable
26114  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
26115  * This class also supports single and multi selection modes. <br>
26116  * Create a data model bound view:
26117  <pre><code>
26118  var store = new Roo.data.Store(...);
26119
26120  var view = new Roo.View({
26121     el : "my-element",
26122     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
26123  
26124     singleSelect: true,
26125     selectedClass: "ydataview-selected",
26126     store: store
26127  });
26128
26129  // listen for node click?
26130  view.on("click", function(vw, index, node, e){
26131  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
26132  });
26133
26134  // load XML data
26135  dataModel.load("foobar.xml");
26136  </code></pre>
26137  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
26138  * <br><br>
26139  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
26140  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
26141  * 
26142  * Note: old style constructor is still suported (container, template, config)
26143  * 
26144  * @constructor
26145  * Create a new View
26146  * @param {Object} config The config object
26147  * 
26148  */
26149 Roo.View = function(config, depreciated_tpl, depreciated_config){
26150     
26151     this.parent = false;
26152     
26153     if (typeof(depreciated_tpl) == 'undefined') {
26154         // new way.. - universal constructor.
26155         Roo.apply(this, config);
26156         this.el  = Roo.get(this.el);
26157     } else {
26158         // old format..
26159         this.el  = Roo.get(config);
26160         this.tpl = depreciated_tpl;
26161         Roo.apply(this, depreciated_config);
26162     }
26163     this.wrapEl  = this.el.wrap().wrap();
26164     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
26165     
26166     
26167     if(typeof(this.tpl) == "string"){
26168         this.tpl = new Roo.Template(this.tpl);
26169     } else {
26170         // support xtype ctors..
26171         this.tpl = new Roo.factory(this.tpl, Roo);
26172     }
26173     
26174     
26175     this.tpl.compile();
26176     
26177     /** @private */
26178     this.addEvents({
26179         /**
26180          * @event beforeclick
26181          * Fires before a click is processed. Returns false to cancel the default action.
26182          * @param {Roo.View} this
26183          * @param {Number} index The index of the target node
26184          * @param {HTMLElement} node The target node
26185          * @param {Roo.EventObject} e The raw event object
26186          */
26187             "beforeclick" : true,
26188         /**
26189          * @event click
26190          * Fires when a template node is clicked.
26191          * @param {Roo.View} this
26192          * @param {Number} index The index of the target node
26193          * @param {HTMLElement} node The target node
26194          * @param {Roo.EventObject} e The raw event object
26195          */
26196             "click" : true,
26197         /**
26198          * @event dblclick
26199          * Fires when a template node is double clicked.
26200          * @param {Roo.View} this
26201          * @param {Number} index The index of the target node
26202          * @param {HTMLElement} node The target node
26203          * @param {Roo.EventObject} e The raw event object
26204          */
26205             "dblclick" : true,
26206         /**
26207          * @event contextmenu
26208          * Fires when a template node is right clicked.
26209          * @param {Roo.View} this
26210          * @param {Number} index The index of the target node
26211          * @param {HTMLElement} node The target node
26212          * @param {Roo.EventObject} e The raw event object
26213          */
26214             "contextmenu" : true,
26215         /**
26216          * @event selectionchange
26217          * Fires when the selected nodes change.
26218          * @param {Roo.View} this
26219          * @param {Array} selections Array of the selected nodes
26220          */
26221             "selectionchange" : true,
26222     
26223         /**
26224          * @event beforeselect
26225          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
26226          * @param {Roo.View} this
26227          * @param {HTMLElement} node The node to be selected
26228          * @param {Array} selections Array of currently selected nodes
26229          */
26230             "beforeselect" : true,
26231         /**
26232          * @event preparedata
26233          * Fires on every row to render, to allow you to change the data.
26234          * @param {Roo.View} this
26235          * @param {Object} data to be rendered (change this)
26236          */
26237           "preparedata" : true
26238           
26239           
26240         });
26241
26242
26243
26244     this.el.on({
26245         "click": this.onClick,
26246         "dblclick": this.onDblClick,
26247         "contextmenu": this.onContextMenu,
26248         scope:this
26249     });
26250
26251     this.selections = [];
26252     this.nodes = [];
26253     this.cmp = new Roo.CompositeElementLite([]);
26254     if(this.store){
26255         this.store = Roo.factory(this.store, Roo.data);
26256         this.setStore(this.store, true);
26257     }
26258     
26259     if ( this.footer && this.footer.xtype) {
26260            
26261          var fctr = this.wrapEl.appendChild(document.createElement("div"));
26262         
26263         this.footer.dataSource = this.store;
26264         this.footer.container = fctr;
26265         this.footer = Roo.factory(this.footer, Roo);
26266         fctr.insertFirst(this.el);
26267         
26268         // this is a bit insane - as the paging toolbar seems to detach the el..
26269 //        dom.parentNode.parentNode.parentNode
26270          // they get detached?
26271     }
26272     
26273     
26274     Roo.View.superclass.constructor.call(this);
26275     
26276     
26277 };
26278
26279 Roo.extend(Roo.View, Roo.util.Observable, {
26280     
26281      /**
26282      * @cfg {Roo.data.Store} store Data store to load data from.
26283      */
26284     store : false,
26285     
26286     /**
26287      * @cfg {String|Roo.Element} el The container element.
26288      */
26289     el : '',
26290     
26291     /**
26292      * @cfg {String|Roo.Template} tpl The template used by this View 
26293      */
26294     tpl : false,
26295     /**
26296      * @cfg {String} dataName the named area of the template to use as the data area
26297      *                          Works with domtemplates roo-name="name"
26298      */
26299     dataName: false,
26300     /**
26301      * @cfg {String} selectedClass The css class to add to selected nodes
26302      */
26303     selectedClass : "x-view-selected",
26304      /**
26305      * @cfg {String} emptyText The empty text to show when nothing is loaded.
26306      */
26307     emptyText : "",
26308     
26309     /**
26310      * @cfg {String} text to display on mask (default Loading)
26311      */
26312     mask : false,
26313     /**
26314      * @cfg {Boolean} multiSelect Allow multiple selection
26315      */
26316     multiSelect : false,
26317     /**
26318      * @cfg {Boolean} singleSelect Allow single selection
26319      */
26320     singleSelect:  false,
26321     
26322     /**
26323      * @cfg {Boolean} toggleSelect - selecting 
26324      */
26325     toggleSelect : false,
26326     
26327     /**
26328      * @cfg {Boolean} tickable - selecting 
26329      */
26330     tickable : false,
26331     
26332     /**
26333      * Returns the element this view is bound to.
26334      * @return {Roo.Element}
26335      */
26336     getEl : function(){
26337         return this.wrapEl;
26338     },
26339     
26340     
26341
26342     /**
26343      * Refreshes the view. - called by datachanged on the store. - do not call directly.
26344      */
26345     refresh : function(){
26346         //Roo.log('refresh');
26347         var t = this.tpl;
26348         
26349         // if we are using something like 'domtemplate', then
26350         // the what gets used is:
26351         // t.applySubtemplate(NAME, data, wrapping data..)
26352         // the outer template then get' applied with
26353         //     the store 'extra data'
26354         // and the body get's added to the
26355         //      roo-name="data" node?
26356         //      <span class='roo-tpl-{name}'></span> ?????
26357         
26358         
26359         
26360         this.clearSelections();
26361         this.el.update("");
26362         var html = [];
26363         var records = this.store.getRange();
26364         if(records.length < 1) {
26365             
26366             // is this valid??  = should it render a template??
26367             
26368             this.el.update(this.emptyText);
26369             return;
26370         }
26371         var el = this.el;
26372         if (this.dataName) {
26373             this.el.update(t.apply(this.store.meta)); //????
26374             el = this.el.child('.roo-tpl-' + this.dataName);
26375         }
26376         
26377         for(var i = 0, len = records.length; i < len; i++){
26378             var data = this.prepareData(records[i].data, i, records[i]);
26379             this.fireEvent("preparedata", this, data, i, records[i]);
26380             
26381             var d = Roo.apply({}, data);
26382             
26383             if(this.tickable){
26384                 Roo.apply(d, {'roo-id' : Roo.id()});
26385                 
26386                 var _this = this;
26387             
26388                 Roo.each(this.parent.item, function(item){
26389                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
26390                         return;
26391                     }
26392                     Roo.apply(d, {'roo-data-checked' : 'checked'});
26393                 });
26394             }
26395             
26396             html[html.length] = Roo.util.Format.trim(
26397                 this.dataName ?
26398                     t.applySubtemplate(this.dataName, d, this.store.meta) :
26399                     t.apply(d)
26400             );
26401         }
26402         
26403         
26404         
26405         el.update(html.join(""));
26406         this.nodes = el.dom.childNodes;
26407         this.updateIndexes(0);
26408     },
26409     
26410
26411     /**
26412      * Function to override to reformat the data that is sent to
26413      * the template for each node.
26414      * DEPRICATED - use the preparedata event handler.
26415      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
26416      * a JSON object for an UpdateManager bound view).
26417      */
26418     prepareData : function(data, index, record)
26419     {
26420         this.fireEvent("preparedata", this, data, index, record);
26421         return data;
26422     },
26423
26424     onUpdate : function(ds, record){
26425         // Roo.log('on update');   
26426         this.clearSelections();
26427         var index = this.store.indexOf(record);
26428         var n = this.nodes[index];
26429         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
26430         n.parentNode.removeChild(n);
26431         this.updateIndexes(index, index);
26432     },
26433
26434     
26435     
26436 // --------- FIXME     
26437     onAdd : function(ds, records, index)
26438     {
26439         //Roo.log(['on Add', ds, records, index] );        
26440         this.clearSelections();
26441         if(this.nodes.length == 0){
26442             this.refresh();
26443             return;
26444         }
26445         var n = this.nodes[index];
26446         for(var i = 0, len = records.length; i < len; i++){
26447             var d = this.prepareData(records[i].data, i, records[i]);
26448             if(n){
26449                 this.tpl.insertBefore(n, d);
26450             }else{
26451                 
26452                 this.tpl.append(this.el, d);
26453             }
26454         }
26455         this.updateIndexes(index);
26456     },
26457
26458     onRemove : function(ds, record, index){
26459        // Roo.log('onRemove');
26460         this.clearSelections();
26461         var el = this.dataName  ?
26462             this.el.child('.roo-tpl-' + this.dataName) :
26463             this.el; 
26464         
26465         el.dom.removeChild(this.nodes[index]);
26466         this.updateIndexes(index);
26467     },
26468
26469     /**
26470      * Refresh an individual node.
26471      * @param {Number} index
26472      */
26473     refreshNode : function(index){
26474         this.onUpdate(this.store, this.store.getAt(index));
26475     },
26476
26477     updateIndexes : function(startIndex, endIndex){
26478         var ns = this.nodes;
26479         startIndex = startIndex || 0;
26480         endIndex = endIndex || ns.length - 1;
26481         for(var i = startIndex; i <= endIndex; i++){
26482             ns[i].nodeIndex = i;
26483         }
26484     },
26485
26486     /**
26487      * Changes the data store this view uses and refresh the view.
26488      * @param {Store} store
26489      */
26490     setStore : function(store, initial){
26491         if(!initial && this.store){
26492             this.store.un("datachanged", this.refresh);
26493             this.store.un("add", this.onAdd);
26494             this.store.un("remove", this.onRemove);
26495             this.store.un("update", this.onUpdate);
26496             this.store.un("clear", this.refresh);
26497             this.store.un("beforeload", this.onBeforeLoad);
26498             this.store.un("load", this.onLoad);
26499             this.store.un("loadexception", this.onLoad);
26500         }
26501         if(store){
26502           
26503             store.on("datachanged", this.refresh, this);
26504             store.on("add", this.onAdd, this);
26505             store.on("remove", this.onRemove, this);
26506             store.on("update", this.onUpdate, this);
26507             store.on("clear", this.refresh, this);
26508             store.on("beforeload", this.onBeforeLoad, this);
26509             store.on("load", this.onLoad, this);
26510             store.on("loadexception", this.onLoad, this);
26511         }
26512         
26513         if(store){
26514             this.refresh();
26515         }
26516     },
26517     /**
26518      * onbeforeLoad - masks the loading area.
26519      *
26520      */
26521     onBeforeLoad : function(store,opts)
26522     {
26523          //Roo.log('onBeforeLoad');   
26524         if (!opts.add) {
26525             this.el.update("");
26526         }
26527         this.el.mask(this.mask ? this.mask : "Loading" ); 
26528     },
26529     onLoad : function ()
26530     {
26531         this.el.unmask();
26532     },
26533     
26534
26535     /**
26536      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
26537      * @param {HTMLElement} node
26538      * @return {HTMLElement} The template node
26539      */
26540     findItemFromChild : function(node){
26541         var el = this.dataName  ?
26542             this.el.child('.roo-tpl-' + this.dataName,true) :
26543             this.el.dom; 
26544         
26545         if(!node || node.parentNode == el){
26546                     return node;
26547             }
26548             var p = node.parentNode;
26549             while(p && p != el){
26550             if(p.parentNode == el){
26551                 return p;
26552             }
26553             p = p.parentNode;
26554         }
26555             return null;
26556     },
26557
26558     /** @ignore */
26559     onClick : function(e){
26560         var item = this.findItemFromChild(e.getTarget());
26561         if(item){
26562             var index = this.indexOf(item);
26563             if(this.onItemClick(item, index, e) !== false){
26564                 this.fireEvent("click", this, index, item, e);
26565             }
26566         }else{
26567             this.clearSelections();
26568         }
26569     },
26570
26571     /** @ignore */
26572     onContextMenu : function(e){
26573         var item = this.findItemFromChild(e.getTarget());
26574         if(item){
26575             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
26576         }
26577     },
26578
26579     /** @ignore */
26580     onDblClick : function(e){
26581         var item = this.findItemFromChild(e.getTarget());
26582         if(item){
26583             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
26584         }
26585     },
26586
26587     onItemClick : function(item, index, e)
26588     {
26589         if(this.fireEvent("beforeclick", this, index, item, e) === false){
26590             return false;
26591         }
26592         if (this.toggleSelect) {
26593             var m = this.isSelected(item) ? 'unselect' : 'select';
26594             //Roo.log(m);
26595             var _t = this;
26596             _t[m](item, true, false);
26597             return true;
26598         }
26599         if(this.multiSelect || this.singleSelect){
26600             if(this.multiSelect && e.shiftKey && this.lastSelection){
26601                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
26602             }else{
26603                 this.select(item, this.multiSelect && e.ctrlKey);
26604                 this.lastSelection = item;
26605             }
26606             
26607             if(!this.tickable){
26608                 e.preventDefault();
26609             }
26610             
26611         }
26612         return true;
26613     },
26614
26615     /**
26616      * Get the number of selected nodes.
26617      * @return {Number}
26618      */
26619     getSelectionCount : function(){
26620         return this.selections.length;
26621     },
26622
26623     /**
26624      * Get the currently selected nodes.
26625      * @return {Array} An array of HTMLElements
26626      */
26627     getSelectedNodes : function(){
26628         return this.selections;
26629     },
26630
26631     /**
26632      * Get the indexes of the selected nodes.
26633      * @return {Array}
26634      */
26635     getSelectedIndexes : function(){
26636         var indexes = [], s = this.selections;
26637         for(var i = 0, len = s.length; i < len; i++){
26638             indexes.push(s[i].nodeIndex);
26639         }
26640         return indexes;
26641     },
26642
26643     /**
26644      * Clear all selections
26645      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
26646      */
26647     clearSelections : function(suppressEvent){
26648         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
26649             this.cmp.elements = this.selections;
26650             this.cmp.removeClass(this.selectedClass);
26651             this.selections = [];
26652             if(!suppressEvent){
26653                 this.fireEvent("selectionchange", this, this.selections);
26654             }
26655         }
26656     },
26657
26658     /**
26659      * Returns true if the passed node is selected
26660      * @param {HTMLElement/Number} node The node or node index
26661      * @return {Boolean}
26662      */
26663     isSelected : function(node){
26664         var s = this.selections;
26665         if(s.length < 1){
26666             return false;
26667         }
26668         node = this.getNode(node);
26669         return s.indexOf(node) !== -1;
26670     },
26671
26672     /**
26673      * Selects nodes.
26674      * @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
26675      * @param {Boolean} keepExisting (optional) true to keep existing selections
26676      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
26677      */
26678     select : function(nodeInfo, keepExisting, suppressEvent){
26679         if(nodeInfo instanceof Array){
26680             if(!keepExisting){
26681                 this.clearSelections(true);
26682             }
26683             for(var i = 0, len = nodeInfo.length; i < len; i++){
26684                 this.select(nodeInfo[i], true, true);
26685             }
26686             return;
26687         } 
26688         var node = this.getNode(nodeInfo);
26689         if(!node || this.isSelected(node)){
26690             return; // already selected.
26691         }
26692         if(!keepExisting){
26693             this.clearSelections(true);
26694         }
26695         
26696         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
26697             Roo.fly(node).addClass(this.selectedClass);
26698             this.selections.push(node);
26699             if(!suppressEvent){
26700                 this.fireEvent("selectionchange", this, this.selections);
26701             }
26702         }
26703         
26704         
26705     },
26706       /**
26707      * Unselects nodes.
26708      * @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
26709      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
26710      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
26711      */
26712     unselect : function(nodeInfo, keepExisting, suppressEvent)
26713     {
26714         if(nodeInfo instanceof Array){
26715             Roo.each(this.selections, function(s) {
26716                 this.unselect(s, nodeInfo);
26717             }, this);
26718             return;
26719         }
26720         var node = this.getNode(nodeInfo);
26721         if(!node || !this.isSelected(node)){
26722             //Roo.log("not selected");
26723             return; // not selected.
26724         }
26725         // fireevent???
26726         var ns = [];
26727         Roo.each(this.selections, function(s) {
26728             if (s == node ) {
26729                 Roo.fly(node).removeClass(this.selectedClass);
26730
26731                 return;
26732             }
26733             ns.push(s);
26734         },this);
26735         
26736         this.selections= ns;
26737         this.fireEvent("selectionchange", this, this.selections);
26738     },
26739
26740     /**
26741      * Gets a template node.
26742      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
26743      * @return {HTMLElement} The node or null if it wasn't found
26744      */
26745     getNode : function(nodeInfo){
26746         if(typeof nodeInfo == "string"){
26747             return document.getElementById(nodeInfo);
26748         }else if(typeof nodeInfo == "number"){
26749             return this.nodes[nodeInfo];
26750         }
26751         return nodeInfo;
26752     },
26753
26754     /**
26755      * Gets a range template nodes.
26756      * @param {Number} startIndex
26757      * @param {Number} endIndex
26758      * @return {Array} An array of nodes
26759      */
26760     getNodes : function(start, end){
26761         var ns = this.nodes;
26762         start = start || 0;
26763         end = typeof end == "undefined" ? ns.length - 1 : end;
26764         var nodes = [];
26765         if(start <= end){
26766             for(var i = start; i <= end; i++){
26767                 nodes.push(ns[i]);
26768             }
26769         } else{
26770             for(var i = start; i >= end; i--){
26771                 nodes.push(ns[i]);
26772             }
26773         }
26774         return nodes;
26775     },
26776
26777     /**
26778      * Finds the index of the passed node
26779      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
26780      * @return {Number} The index of the node or -1
26781      */
26782     indexOf : function(node){
26783         node = this.getNode(node);
26784         if(typeof node.nodeIndex == "number"){
26785             return node.nodeIndex;
26786         }
26787         var ns = this.nodes;
26788         for(var i = 0, len = ns.length; i < len; i++){
26789             if(ns[i] == node){
26790                 return i;
26791             }
26792         }
26793         return -1;
26794     }
26795 });
26796 /*
26797  * Based on:
26798  * Ext JS Library 1.1.1
26799  * Copyright(c) 2006-2007, Ext JS, LLC.
26800  *
26801  * Originally Released Under LGPL - original licence link has changed is not relivant.
26802  *
26803  * Fork - LGPL
26804  * <script type="text/javascript">
26805  */
26806
26807 /**
26808  * @class Roo.JsonView
26809  * @extends Roo.View
26810  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
26811 <pre><code>
26812 var view = new Roo.JsonView({
26813     container: "my-element",
26814     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
26815     multiSelect: true, 
26816     jsonRoot: "data" 
26817 });
26818
26819 // listen for node click?
26820 view.on("click", function(vw, index, node, e){
26821     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
26822 });
26823
26824 // direct load of JSON data
26825 view.load("foobar.php");
26826
26827 // Example from my blog list
26828 var tpl = new Roo.Template(
26829     '&lt;div class="entry"&gt;' +
26830     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
26831     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
26832     "&lt;/div&gt;&lt;hr /&gt;"
26833 );
26834
26835 var moreView = new Roo.JsonView({
26836     container :  "entry-list", 
26837     template : tpl,
26838     jsonRoot: "posts"
26839 });
26840 moreView.on("beforerender", this.sortEntries, this);
26841 moreView.load({
26842     url: "/blog/get-posts.php",
26843     params: "allposts=true",
26844     text: "Loading Blog Entries..."
26845 });
26846 </code></pre>
26847
26848 * Note: old code is supported with arguments : (container, template, config)
26849
26850
26851  * @constructor
26852  * Create a new JsonView
26853  * 
26854  * @param {Object} config The config object
26855  * 
26856  */
26857 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
26858     
26859     
26860     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
26861
26862     var um = this.el.getUpdateManager();
26863     um.setRenderer(this);
26864     um.on("update", this.onLoad, this);
26865     um.on("failure", this.onLoadException, this);
26866
26867     /**
26868      * @event beforerender
26869      * Fires before rendering of the downloaded JSON data.
26870      * @param {Roo.JsonView} this
26871      * @param {Object} data The JSON data loaded
26872      */
26873     /**
26874      * @event load
26875      * Fires when data is loaded.
26876      * @param {Roo.JsonView} this
26877      * @param {Object} data The JSON data loaded
26878      * @param {Object} response The raw Connect response object
26879      */
26880     /**
26881      * @event loadexception
26882      * Fires when loading fails.
26883      * @param {Roo.JsonView} this
26884      * @param {Object} response The raw Connect response object
26885      */
26886     this.addEvents({
26887         'beforerender' : true,
26888         'load' : true,
26889         'loadexception' : true
26890     });
26891 };
26892 Roo.extend(Roo.JsonView, Roo.View, {
26893     /**
26894      * @type {String} The root property in the loaded JSON object that contains the data
26895      */
26896     jsonRoot : "",
26897
26898     /**
26899      * Refreshes the view.
26900      */
26901     refresh : function(){
26902         this.clearSelections();
26903         this.el.update("");
26904         var html = [];
26905         var o = this.jsonData;
26906         if(o && o.length > 0){
26907             for(var i = 0, len = o.length; i < len; i++){
26908                 var data = this.prepareData(o[i], i, o);
26909                 html[html.length] = this.tpl.apply(data);
26910             }
26911         }else{
26912             html.push(this.emptyText);
26913         }
26914         this.el.update(html.join(""));
26915         this.nodes = this.el.dom.childNodes;
26916         this.updateIndexes(0);
26917     },
26918
26919     /**
26920      * 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.
26921      * @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:
26922      <pre><code>
26923      view.load({
26924          url: "your-url.php",
26925          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
26926          callback: yourFunction,
26927          scope: yourObject, //(optional scope)
26928          discardUrl: false,
26929          nocache: false,
26930          text: "Loading...",
26931          timeout: 30,
26932          scripts: false
26933      });
26934      </code></pre>
26935      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
26936      * 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.
26937      * @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}
26938      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
26939      * @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.
26940      */
26941     load : function(){
26942         var um = this.el.getUpdateManager();
26943         um.update.apply(um, arguments);
26944     },
26945
26946     // note - render is a standard framework call...
26947     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
26948     render : function(el, response){
26949         
26950         this.clearSelections();
26951         this.el.update("");
26952         var o;
26953         try{
26954             if (response != '') {
26955                 o = Roo.util.JSON.decode(response.responseText);
26956                 if(this.jsonRoot){
26957                     
26958                     o = o[this.jsonRoot];
26959                 }
26960             }
26961         } catch(e){
26962         }
26963         /**
26964          * The current JSON data or null
26965          */
26966         this.jsonData = o;
26967         this.beforeRender();
26968         this.refresh();
26969     },
26970
26971 /**
26972  * Get the number of records in the current JSON dataset
26973  * @return {Number}
26974  */
26975     getCount : function(){
26976         return this.jsonData ? this.jsonData.length : 0;
26977     },
26978
26979 /**
26980  * Returns the JSON object for the specified node(s)
26981  * @param {HTMLElement/Array} node The node or an array of nodes
26982  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
26983  * you get the JSON object for the node
26984  */
26985     getNodeData : function(node){
26986         if(node instanceof Array){
26987             var data = [];
26988             for(var i = 0, len = node.length; i < len; i++){
26989                 data.push(this.getNodeData(node[i]));
26990             }
26991             return data;
26992         }
26993         return this.jsonData[this.indexOf(node)] || null;
26994     },
26995
26996     beforeRender : function(){
26997         this.snapshot = this.jsonData;
26998         if(this.sortInfo){
26999             this.sort.apply(this, this.sortInfo);
27000         }
27001         this.fireEvent("beforerender", this, this.jsonData);
27002     },
27003
27004     onLoad : function(el, o){
27005         this.fireEvent("load", this, this.jsonData, o);
27006     },
27007
27008     onLoadException : function(el, o){
27009         this.fireEvent("loadexception", this, o);
27010     },
27011
27012 /**
27013  * Filter the data by a specific property.
27014  * @param {String} property A property on your JSON objects
27015  * @param {String/RegExp} value Either string that the property values
27016  * should start with, or a RegExp to test against the property
27017  */
27018     filter : function(property, value){
27019         if(this.jsonData){
27020             var data = [];
27021             var ss = this.snapshot;
27022             if(typeof value == "string"){
27023                 var vlen = value.length;
27024                 if(vlen == 0){
27025                     this.clearFilter();
27026                     return;
27027                 }
27028                 value = value.toLowerCase();
27029                 for(var i = 0, len = ss.length; i < len; i++){
27030                     var o = ss[i];
27031                     if(o[property].substr(0, vlen).toLowerCase() == value){
27032                         data.push(o);
27033                     }
27034                 }
27035             } else if(value.exec){ // regex?
27036                 for(var i = 0, len = ss.length; i < len; i++){
27037                     var o = ss[i];
27038                     if(value.test(o[property])){
27039                         data.push(o);
27040                     }
27041                 }
27042             } else{
27043                 return;
27044             }
27045             this.jsonData = data;
27046             this.refresh();
27047         }
27048     },
27049
27050 /**
27051  * Filter by a function. The passed function will be called with each
27052  * object in the current dataset. If the function returns true the value is kept,
27053  * otherwise it is filtered.
27054  * @param {Function} fn
27055  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
27056  */
27057     filterBy : function(fn, scope){
27058         if(this.jsonData){
27059             var data = [];
27060             var ss = this.snapshot;
27061             for(var i = 0, len = ss.length; i < len; i++){
27062                 var o = ss[i];
27063                 if(fn.call(scope || this, o)){
27064                     data.push(o);
27065                 }
27066             }
27067             this.jsonData = data;
27068             this.refresh();
27069         }
27070     },
27071
27072 /**
27073  * Clears the current filter.
27074  */
27075     clearFilter : function(){
27076         if(this.snapshot && this.jsonData != this.snapshot){
27077             this.jsonData = this.snapshot;
27078             this.refresh();
27079         }
27080     },
27081
27082
27083 /**
27084  * Sorts the data for this view and refreshes it.
27085  * @param {String} property A property on your JSON objects to sort on
27086  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
27087  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
27088  */
27089     sort : function(property, dir, sortType){
27090         this.sortInfo = Array.prototype.slice.call(arguments, 0);
27091         if(this.jsonData){
27092             var p = property;
27093             var dsc = dir && dir.toLowerCase() == "desc";
27094             var f = function(o1, o2){
27095                 var v1 = sortType ? sortType(o1[p]) : o1[p];
27096                 var v2 = sortType ? sortType(o2[p]) : o2[p];
27097                 ;
27098                 if(v1 < v2){
27099                     return dsc ? +1 : -1;
27100                 } else if(v1 > v2){
27101                     return dsc ? -1 : +1;
27102                 } else{
27103                     return 0;
27104                 }
27105             };
27106             this.jsonData.sort(f);
27107             this.refresh();
27108             if(this.jsonData != this.snapshot){
27109                 this.snapshot.sort(f);
27110             }
27111         }
27112     }
27113 });/*
27114  * Based on:
27115  * Ext JS Library 1.1.1
27116  * Copyright(c) 2006-2007, Ext JS, LLC.
27117  *
27118  * Originally Released Under LGPL - original licence link has changed is not relivant.
27119  *
27120  * Fork - LGPL
27121  * <script type="text/javascript">
27122  */
27123  
27124
27125 /**
27126  * @class Roo.ColorPalette
27127  * @extends Roo.Component
27128  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
27129  * Here's an example of typical usage:
27130  * <pre><code>
27131 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
27132 cp.render('my-div');
27133
27134 cp.on('select', function(palette, selColor){
27135     // do something with selColor
27136 });
27137 </code></pre>
27138  * @constructor
27139  * Create a new ColorPalette
27140  * @param {Object} config The config object
27141  */
27142 Roo.ColorPalette = function(config){
27143     Roo.ColorPalette.superclass.constructor.call(this, config);
27144     this.addEvents({
27145         /**
27146              * @event select
27147              * Fires when a color is selected
27148              * @param {ColorPalette} this
27149              * @param {String} color The 6-digit color hex code (without the # symbol)
27150              */
27151         select: true
27152     });
27153
27154     if(this.handler){
27155         this.on("select", this.handler, this.scope, true);
27156     }
27157 };
27158 Roo.extend(Roo.ColorPalette, Roo.Component, {
27159     /**
27160      * @cfg {String} itemCls
27161      * The CSS class to apply to the containing element (defaults to "x-color-palette")
27162      */
27163     itemCls : "x-color-palette",
27164     /**
27165      * @cfg {String} value
27166      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
27167      * the hex codes are case-sensitive.
27168      */
27169     value : null,
27170     clickEvent:'click',
27171     // private
27172     ctype: "Roo.ColorPalette",
27173
27174     /**
27175      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
27176      */
27177     allowReselect : false,
27178
27179     /**
27180      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
27181      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
27182      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
27183      * of colors with the width setting until the box is symmetrical.</p>
27184      * <p>You can override individual colors if needed:</p>
27185      * <pre><code>
27186 var cp = new Roo.ColorPalette();
27187 cp.colors[0] = "FF0000";  // change the first box to red
27188 </code></pre>
27189
27190 Or you can provide a custom array of your own for complete control:
27191 <pre><code>
27192 var cp = new Roo.ColorPalette();
27193 cp.colors = ["000000", "993300", "333300"];
27194 </code></pre>
27195      * @type Array
27196      */
27197     colors : [
27198         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
27199         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
27200         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
27201         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
27202         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
27203     ],
27204
27205     // private
27206     onRender : function(container, position){
27207         var t = new Roo.MasterTemplate(
27208             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
27209         );
27210         var c = this.colors;
27211         for(var i = 0, len = c.length; i < len; i++){
27212             t.add([c[i]]);
27213         }
27214         var el = document.createElement("div");
27215         el.className = this.itemCls;
27216         t.overwrite(el);
27217         container.dom.insertBefore(el, position);
27218         this.el = Roo.get(el);
27219         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
27220         if(this.clickEvent != 'click'){
27221             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
27222         }
27223     },
27224
27225     // private
27226     afterRender : function(){
27227         Roo.ColorPalette.superclass.afterRender.call(this);
27228         if(this.value){
27229             var s = this.value;
27230             this.value = null;
27231             this.select(s);
27232         }
27233     },
27234
27235     // private
27236     handleClick : function(e, t){
27237         e.preventDefault();
27238         if(!this.disabled){
27239             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
27240             this.select(c.toUpperCase());
27241         }
27242     },
27243
27244     /**
27245      * Selects the specified color in the palette (fires the select event)
27246      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
27247      */
27248     select : function(color){
27249         color = color.replace("#", "");
27250         if(color != this.value || this.allowReselect){
27251             var el = this.el;
27252             if(this.value){
27253                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
27254             }
27255             el.child("a.color-"+color).addClass("x-color-palette-sel");
27256             this.value = color;
27257             this.fireEvent("select", this, color);
27258         }
27259     }
27260 });/*
27261  * Based on:
27262  * Ext JS Library 1.1.1
27263  * Copyright(c) 2006-2007, Ext JS, LLC.
27264  *
27265  * Originally Released Under LGPL - original licence link has changed is not relivant.
27266  *
27267  * Fork - LGPL
27268  * <script type="text/javascript">
27269  */
27270  
27271 /**
27272  * @class Roo.DatePicker
27273  * @extends Roo.Component
27274  * Simple date picker class.
27275  * @constructor
27276  * Create a new DatePicker
27277  * @param {Object} config The config object
27278  */
27279 Roo.DatePicker = function(config){
27280     Roo.DatePicker.superclass.constructor.call(this, config);
27281
27282     this.value = config && config.value ?
27283                  config.value.clearTime() : new Date().clearTime();
27284
27285     this.addEvents({
27286         /**
27287              * @event select
27288              * Fires when a date is selected
27289              * @param {DatePicker} this
27290              * @param {Date} date The selected date
27291              */
27292         'select': true,
27293         /**
27294              * @event monthchange
27295              * Fires when the displayed month changes 
27296              * @param {DatePicker} this
27297              * @param {Date} date The selected month
27298              */
27299         'monthchange': true
27300     });
27301
27302     if(this.handler){
27303         this.on("select", this.handler,  this.scope || this);
27304     }
27305     // build the disabledDatesRE
27306     if(!this.disabledDatesRE && this.disabledDates){
27307         var dd = this.disabledDates;
27308         var re = "(?:";
27309         for(var i = 0; i < dd.length; i++){
27310             re += dd[i];
27311             if(i != dd.length-1) {
27312                 re += "|";
27313             }
27314         }
27315         this.disabledDatesRE = new RegExp(re + ")");
27316     }
27317 };
27318
27319 Roo.extend(Roo.DatePicker, Roo.Component, {
27320     /**
27321      * @cfg {String} todayText
27322      * The text to display on the button that selects the current date (defaults to "Today")
27323      */
27324     todayText : "Today",
27325     /**
27326      * @cfg {String} okText
27327      * The text to display on the ok button
27328      */
27329     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
27330     /**
27331      * @cfg {String} cancelText
27332      * The text to display on the cancel button
27333      */
27334     cancelText : "Cancel",
27335     /**
27336      * @cfg {String} todayTip
27337      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
27338      */
27339     todayTip : "{0} (Spacebar)",
27340     /**
27341      * @cfg {Date} minDate
27342      * Minimum allowable date (JavaScript date object, defaults to null)
27343      */
27344     minDate : null,
27345     /**
27346      * @cfg {Date} maxDate
27347      * Maximum allowable date (JavaScript date object, defaults to null)
27348      */
27349     maxDate : null,
27350     /**
27351      * @cfg {String} minText
27352      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
27353      */
27354     minText : "This date is before the minimum date",
27355     /**
27356      * @cfg {String} maxText
27357      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
27358      */
27359     maxText : "This date is after the maximum date",
27360     /**
27361      * @cfg {String} format
27362      * The default date format string which can be overriden for localization support.  The format must be
27363      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
27364      */
27365     format : "m/d/y",
27366     /**
27367      * @cfg {Array} disabledDays
27368      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
27369      */
27370     disabledDays : null,
27371     /**
27372      * @cfg {String} disabledDaysText
27373      * The tooltip to display when the date falls on a disabled day (defaults to "")
27374      */
27375     disabledDaysText : "",
27376     /**
27377      * @cfg {RegExp} disabledDatesRE
27378      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
27379      */
27380     disabledDatesRE : null,
27381     /**
27382      * @cfg {String} disabledDatesText
27383      * The tooltip text to display when the date falls on a disabled date (defaults to "")
27384      */
27385     disabledDatesText : "",
27386     /**
27387      * @cfg {Boolean} constrainToViewport
27388      * True to constrain the date picker to the viewport (defaults to true)
27389      */
27390     constrainToViewport : true,
27391     /**
27392      * @cfg {Array} monthNames
27393      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
27394      */
27395     monthNames : Date.monthNames,
27396     /**
27397      * @cfg {Array} dayNames
27398      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
27399      */
27400     dayNames : Date.dayNames,
27401     /**
27402      * @cfg {String} nextText
27403      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
27404      */
27405     nextText: 'Next Month (Control+Right)',
27406     /**
27407      * @cfg {String} prevText
27408      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
27409      */
27410     prevText: 'Previous Month (Control+Left)',
27411     /**
27412      * @cfg {String} monthYearText
27413      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
27414      */
27415     monthYearText: 'Choose a month (Control+Up/Down to move years)',
27416     /**
27417      * @cfg {Number} startDay
27418      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
27419      */
27420     startDay : 0,
27421     /**
27422      * @cfg {Bool} showClear
27423      * Show a clear button (usefull for date form elements that can be blank.)
27424      */
27425     
27426     showClear: false,
27427     
27428     /**
27429      * Sets the value of the date field
27430      * @param {Date} value The date to set
27431      */
27432     setValue : function(value){
27433         var old = this.value;
27434         
27435         if (typeof(value) == 'string') {
27436          
27437             value = Date.parseDate(value, this.format);
27438         }
27439         if (!value) {
27440             value = new Date();
27441         }
27442         
27443         this.value = value.clearTime(true);
27444         if(this.el){
27445             this.update(this.value);
27446         }
27447     },
27448
27449     /**
27450      * Gets the current selected value of the date field
27451      * @return {Date} The selected date
27452      */
27453     getValue : function(){
27454         return this.value;
27455     },
27456
27457     // private
27458     focus : function(){
27459         if(this.el){
27460             this.update(this.activeDate);
27461         }
27462     },
27463
27464     // privateval
27465     onRender : function(container, position){
27466         
27467         var m = [
27468              '<table cellspacing="0">',
27469                 '<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>',
27470                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
27471         var dn = this.dayNames;
27472         for(var i = 0; i < 7; i++){
27473             var d = this.startDay+i;
27474             if(d > 6){
27475                 d = d-7;
27476             }
27477             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
27478         }
27479         m[m.length] = "</tr></thead><tbody><tr>";
27480         for(var i = 0; i < 42; i++) {
27481             if(i % 7 == 0 && i != 0){
27482                 m[m.length] = "</tr><tr>";
27483             }
27484             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
27485         }
27486         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
27487             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
27488
27489         var el = document.createElement("div");
27490         el.className = "x-date-picker";
27491         el.innerHTML = m.join("");
27492
27493         container.dom.insertBefore(el, position);
27494
27495         this.el = Roo.get(el);
27496         this.eventEl = Roo.get(el.firstChild);
27497
27498         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
27499             handler: this.showPrevMonth,
27500             scope: this,
27501             preventDefault:true,
27502             stopDefault:true
27503         });
27504
27505         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
27506             handler: this.showNextMonth,
27507             scope: this,
27508             preventDefault:true,
27509             stopDefault:true
27510         });
27511
27512         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
27513
27514         this.monthPicker = this.el.down('div.x-date-mp');
27515         this.monthPicker.enableDisplayMode('block');
27516         
27517         var kn = new Roo.KeyNav(this.eventEl, {
27518             "left" : function(e){
27519                 e.ctrlKey ?
27520                     this.showPrevMonth() :
27521                     this.update(this.activeDate.add("d", -1));
27522             },
27523
27524             "right" : function(e){
27525                 e.ctrlKey ?
27526                     this.showNextMonth() :
27527                     this.update(this.activeDate.add("d", 1));
27528             },
27529
27530             "up" : function(e){
27531                 e.ctrlKey ?
27532                     this.showNextYear() :
27533                     this.update(this.activeDate.add("d", -7));
27534             },
27535
27536             "down" : function(e){
27537                 e.ctrlKey ?
27538                     this.showPrevYear() :
27539                     this.update(this.activeDate.add("d", 7));
27540             },
27541
27542             "pageUp" : function(e){
27543                 this.showNextMonth();
27544             },
27545
27546             "pageDown" : function(e){
27547                 this.showPrevMonth();
27548             },
27549
27550             "enter" : function(e){
27551                 e.stopPropagation();
27552                 return true;
27553             },
27554
27555             scope : this
27556         });
27557
27558         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
27559
27560         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
27561
27562         this.el.unselectable();
27563         
27564         this.cells = this.el.select("table.x-date-inner tbody td");
27565         this.textNodes = this.el.query("table.x-date-inner tbody span");
27566
27567         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
27568             text: "&#160;",
27569             tooltip: this.monthYearText
27570         });
27571
27572         this.mbtn.on('click', this.showMonthPicker, this);
27573         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
27574
27575
27576         var today = (new Date()).dateFormat(this.format);
27577         
27578         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
27579         if (this.showClear) {
27580             baseTb.add( new Roo.Toolbar.Fill());
27581         }
27582         baseTb.add({
27583             text: String.format(this.todayText, today),
27584             tooltip: String.format(this.todayTip, today),
27585             handler: this.selectToday,
27586             scope: this
27587         });
27588         
27589         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
27590             
27591         //});
27592         if (this.showClear) {
27593             
27594             baseTb.add( new Roo.Toolbar.Fill());
27595             baseTb.add({
27596                 text: '&#160;',
27597                 cls: 'x-btn-icon x-btn-clear',
27598                 handler: function() {
27599                     //this.value = '';
27600                     this.fireEvent("select", this, '');
27601                 },
27602                 scope: this
27603             });
27604         }
27605         
27606         
27607         if(Roo.isIE){
27608             this.el.repaint();
27609         }
27610         this.update(this.value);
27611     },
27612
27613     createMonthPicker : function(){
27614         if(!this.monthPicker.dom.firstChild){
27615             var buf = ['<table border="0" cellspacing="0">'];
27616             for(var i = 0; i < 6; i++){
27617                 buf.push(
27618                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
27619                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
27620                     i == 0 ?
27621                     '<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>' :
27622                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
27623                 );
27624             }
27625             buf.push(
27626                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
27627                     this.okText,
27628                     '</button><button type="button" class="x-date-mp-cancel">',
27629                     this.cancelText,
27630                     '</button></td></tr>',
27631                 '</table>'
27632             );
27633             this.monthPicker.update(buf.join(''));
27634             this.monthPicker.on('click', this.onMonthClick, this);
27635             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
27636
27637             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
27638             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
27639
27640             this.mpMonths.each(function(m, a, i){
27641                 i += 1;
27642                 if((i%2) == 0){
27643                     m.dom.xmonth = 5 + Math.round(i * .5);
27644                 }else{
27645                     m.dom.xmonth = Math.round((i-1) * .5);
27646                 }
27647             });
27648         }
27649     },
27650
27651     showMonthPicker : function(){
27652         this.createMonthPicker();
27653         var size = this.el.getSize();
27654         this.monthPicker.setSize(size);
27655         this.monthPicker.child('table').setSize(size);
27656
27657         this.mpSelMonth = (this.activeDate || this.value).getMonth();
27658         this.updateMPMonth(this.mpSelMonth);
27659         this.mpSelYear = (this.activeDate || this.value).getFullYear();
27660         this.updateMPYear(this.mpSelYear);
27661
27662         this.monthPicker.slideIn('t', {duration:.2});
27663     },
27664
27665     updateMPYear : function(y){
27666         this.mpyear = y;
27667         var ys = this.mpYears.elements;
27668         for(var i = 1; i <= 10; i++){
27669             var td = ys[i-1], y2;
27670             if((i%2) == 0){
27671                 y2 = y + Math.round(i * .5);
27672                 td.firstChild.innerHTML = y2;
27673                 td.xyear = y2;
27674             }else{
27675                 y2 = y - (5-Math.round(i * .5));
27676                 td.firstChild.innerHTML = y2;
27677                 td.xyear = y2;
27678             }
27679             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
27680         }
27681     },
27682
27683     updateMPMonth : function(sm){
27684         this.mpMonths.each(function(m, a, i){
27685             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
27686         });
27687     },
27688
27689     selectMPMonth: function(m){
27690         
27691     },
27692
27693     onMonthClick : function(e, t){
27694         e.stopEvent();
27695         var el = new Roo.Element(t), pn;
27696         if(el.is('button.x-date-mp-cancel')){
27697             this.hideMonthPicker();
27698         }
27699         else if(el.is('button.x-date-mp-ok')){
27700             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
27701             this.hideMonthPicker();
27702         }
27703         else if(pn = el.up('td.x-date-mp-month', 2)){
27704             this.mpMonths.removeClass('x-date-mp-sel');
27705             pn.addClass('x-date-mp-sel');
27706             this.mpSelMonth = pn.dom.xmonth;
27707         }
27708         else if(pn = el.up('td.x-date-mp-year', 2)){
27709             this.mpYears.removeClass('x-date-mp-sel');
27710             pn.addClass('x-date-mp-sel');
27711             this.mpSelYear = pn.dom.xyear;
27712         }
27713         else if(el.is('a.x-date-mp-prev')){
27714             this.updateMPYear(this.mpyear-10);
27715         }
27716         else if(el.is('a.x-date-mp-next')){
27717             this.updateMPYear(this.mpyear+10);
27718         }
27719     },
27720
27721     onMonthDblClick : function(e, t){
27722         e.stopEvent();
27723         var el = new Roo.Element(t), pn;
27724         if(pn = el.up('td.x-date-mp-month', 2)){
27725             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
27726             this.hideMonthPicker();
27727         }
27728         else if(pn = el.up('td.x-date-mp-year', 2)){
27729             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
27730             this.hideMonthPicker();
27731         }
27732     },
27733
27734     hideMonthPicker : function(disableAnim){
27735         if(this.monthPicker){
27736             if(disableAnim === true){
27737                 this.monthPicker.hide();
27738             }else{
27739                 this.monthPicker.slideOut('t', {duration:.2});
27740             }
27741         }
27742     },
27743
27744     // private
27745     showPrevMonth : function(e){
27746         this.update(this.activeDate.add("mo", -1));
27747     },
27748
27749     // private
27750     showNextMonth : function(e){
27751         this.update(this.activeDate.add("mo", 1));
27752     },
27753
27754     // private
27755     showPrevYear : function(){
27756         this.update(this.activeDate.add("y", -1));
27757     },
27758
27759     // private
27760     showNextYear : function(){
27761         this.update(this.activeDate.add("y", 1));
27762     },
27763
27764     // private
27765     handleMouseWheel : function(e){
27766         var delta = e.getWheelDelta();
27767         if(delta > 0){
27768             this.showPrevMonth();
27769             e.stopEvent();
27770         } else if(delta < 0){
27771             this.showNextMonth();
27772             e.stopEvent();
27773         }
27774     },
27775
27776     // private
27777     handleDateClick : function(e, t){
27778         e.stopEvent();
27779         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
27780             this.setValue(new Date(t.dateValue));
27781             this.fireEvent("select", this, this.value);
27782         }
27783     },
27784
27785     // private
27786     selectToday : function(){
27787         this.setValue(new Date().clearTime());
27788         this.fireEvent("select", this, this.value);
27789     },
27790
27791     // private
27792     update : function(date)
27793     {
27794         var vd = this.activeDate;
27795         this.activeDate = date;
27796         if(vd && this.el){
27797             var t = date.getTime();
27798             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
27799                 this.cells.removeClass("x-date-selected");
27800                 this.cells.each(function(c){
27801                    if(c.dom.firstChild.dateValue == t){
27802                        c.addClass("x-date-selected");
27803                        setTimeout(function(){
27804                             try{c.dom.firstChild.focus();}catch(e){}
27805                        }, 50);
27806                        return false;
27807                    }
27808                 });
27809                 return;
27810             }
27811         }
27812         
27813         var days = date.getDaysInMonth();
27814         var firstOfMonth = date.getFirstDateOfMonth();
27815         var startingPos = firstOfMonth.getDay()-this.startDay;
27816
27817         if(startingPos <= this.startDay){
27818             startingPos += 7;
27819         }
27820
27821         var pm = date.add("mo", -1);
27822         var prevStart = pm.getDaysInMonth()-startingPos;
27823
27824         var cells = this.cells.elements;
27825         var textEls = this.textNodes;
27826         days += startingPos;
27827
27828         // convert everything to numbers so it's fast
27829         var day = 86400000;
27830         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
27831         var today = new Date().clearTime().getTime();
27832         var sel = date.clearTime().getTime();
27833         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
27834         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
27835         var ddMatch = this.disabledDatesRE;
27836         var ddText = this.disabledDatesText;
27837         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
27838         var ddaysText = this.disabledDaysText;
27839         var format = this.format;
27840
27841         var setCellClass = function(cal, cell){
27842             cell.title = "";
27843             var t = d.getTime();
27844             cell.firstChild.dateValue = t;
27845             if(t == today){
27846                 cell.className += " x-date-today";
27847                 cell.title = cal.todayText;
27848             }
27849             if(t == sel){
27850                 cell.className += " x-date-selected";
27851                 setTimeout(function(){
27852                     try{cell.firstChild.focus();}catch(e){}
27853                 }, 50);
27854             }
27855             // disabling
27856             if(t < min) {
27857                 cell.className = " x-date-disabled";
27858                 cell.title = cal.minText;
27859                 return;
27860             }
27861             if(t > max) {
27862                 cell.className = " x-date-disabled";
27863                 cell.title = cal.maxText;
27864                 return;
27865             }
27866             if(ddays){
27867                 if(ddays.indexOf(d.getDay()) != -1){
27868                     cell.title = ddaysText;
27869                     cell.className = " x-date-disabled";
27870                 }
27871             }
27872             if(ddMatch && format){
27873                 var fvalue = d.dateFormat(format);
27874                 if(ddMatch.test(fvalue)){
27875                     cell.title = ddText.replace("%0", fvalue);
27876                     cell.className = " x-date-disabled";
27877                 }
27878             }
27879         };
27880
27881         var i = 0;
27882         for(; i < startingPos; i++) {
27883             textEls[i].innerHTML = (++prevStart);
27884             d.setDate(d.getDate()+1);
27885             cells[i].className = "x-date-prevday";
27886             setCellClass(this, cells[i]);
27887         }
27888         for(; i < days; i++){
27889             intDay = i - startingPos + 1;
27890             textEls[i].innerHTML = (intDay);
27891             d.setDate(d.getDate()+1);
27892             cells[i].className = "x-date-active";
27893             setCellClass(this, cells[i]);
27894         }
27895         var extraDays = 0;
27896         for(; i < 42; i++) {
27897              textEls[i].innerHTML = (++extraDays);
27898              d.setDate(d.getDate()+1);
27899              cells[i].className = "x-date-nextday";
27900              setCellClass(this, cells[i]);
27901         }
27902
27903         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
27904         this.fireEvent('monthchange', this, date);
27905         
27906         if(!this.internalRender){
27907             var main = this.el.dom.firstChild;
27908             var w = main.offsetWidth;
27909             this.el.setWidth(w + this.el.getBorderWidth("lr"));
27910             Roo.fly(main).setWidth(w);
27911             this.internalRender = true;
27912             // opera does not respect the auto grow header center column
27913             // then, after it gets a width opera refuses to recalculate
27914             // without a second pass
27915             if(Roo.isOpera && !this.secondPass){
27916                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
27917                 this.secondPass = true;
27918                 this.update.defer(10, this, [date]);
27919             }
27920         }
27921         
27922         
27923     }
27924 });        /*
27925  * Based on:
27926  * Ext JS Library 1.1.1
27927  * Copyright(c) 2006-2007, Ext JS, LLC.
27928  *
27929  * Originally Released Under LGPL - original licence link has changed is not relivant.
27930  *
27931  * Fork - LGPL
27932  * <script type="text/javascript">
27933  */
27934 /**
27935  * @class Roo.TabPanel
27936  * @extends Roo.util.Observable
27937  * A lightweight tab container.
27938  * <br><br>
27939  * Usage:
27940  * <pre><code>
27941 // basic tabs 1, built from existing content
27942 var tabs = new Roo.TabPanel("tabs1");
27943 tabs.addTab("script", "View Script");
27944 tabs.addTab("markup", "View Markup");
27945 tabs.activate("script");
27946
27947 // more advanced tabs, built from javascript
27948 var jtabs = new Roo.TabPanel("jtabs");
27949 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
27950
27951 // set up the UpdateManager
27952 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
27953 var updater = tab2.getUpdateManager();
27954 updater.setDefaultUrl("ajax1.htm");
27955 tab2.on('activate', updater.refresh, updater, true);
27956
27957 // Use setUrl for Ajax loading
27958 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
27959 tab3.setUrl("ajax2.htm", null, true);
27960
27961 // Disabled tab
27962 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
27963 tab4.disable();
27964
27965 jtabs.activate("jtabs-1");
27966  * </code></pre>
27967  * @constructor
27968  * Create a new TabPanel.
27969  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
27970  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
27971  */
27972 Roo.TabPanel = function(container, config){
27973     /**
27974     * The container element for this TabPanel.
27975     * @type Roo.Element
27976     */
27977     this.el = Roo.get(container, true);
27978     if(config){
27979         if(typeof config == "boolean"){
27980             this.tabPosition = config ? "bottom" : "top";
27981         }else{
27982             Roo.apply(this, config);
27983         }
27984     }
27985     if(this.tabPosition == "bottom"){
27986         this.bodyEl = Roo.get(this.createBody(this.el.dom));
27987         this.el.addClass("x-tabs-bottom");
27988     }
27989     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
27990     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
27991     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
27992     if(Roo.isIE){
27993         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
27994     }
27995     if(this.tabPosition != "bottom"){
27996         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
27997          * @type Roo.Element
27998          */
27999         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28000         this.el.addClass("x-tabs-top");
28001     }
28002     this.items = [];
28003
28004     this.bodyEl.setStyle("position", "relative");
28005
28006     this.active = null;
28007     this.activateDelegate = this.activate.createDelegate(this);
28008
28009     this.addEvents({
28010         /**
28011          * @event tabchange
28012          * Fires when the active tab changes
28013          * @param {Roo.TabPanel} this
28014          * @param {Roo.TabPanelItem} activePanel The new active tab
28015          */
28016         "tabchange": true,
28017         /**
28018          * @event beforetabchange
28019          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
28020          * @param {Roo.TabPanel} this
28021          * @param {Object} e Set cancel to true on this object to cancel the tab change
28022          * @param {Roo.TabPanelItem} tab The tab being changed to
28023          */
28024         "beforetabchange" : true
28025     });
28026
28027     Roo.EventManager.onWindowResize(this.onResize, this);
28028     this.cpad = this.el.getPadding("lr");
28029     this.hiddenCount = 0;
28030
28031
28032     // toolbar on the tabbar support...
28033     if (this.toolbar) {
28034         var tcfg = this.toolbar;
28035         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
28036         this.toolbar = new Roo.Toolbar(tcfg);
28037         if (Roo.isSafari) {
28038             var tbl = tcfg.container.child('table', true);
28039             tbl.setAttribute('width', '100%');
28040         }
28041         
28042     }
28043    
28044
28045
28046     Roo.TabPanel.superclass.constructor.call(this);
28047 };
28048
28049 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
28050     /*
28051      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
28052      */
28053     tabPosition : "top",
28054     /*
28055      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
28056      */
28057     currentTabWidth : 0,
28058     /*
28059      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
28060      */
28061     minTabWidth : 40,
28062     /*
28063      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
28064      */
28065     maxTabWidth : 250,
28066     /*
28067      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
28068      */
28069     preferredTabWidth : 175,
28070     /*
28071      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
28072      */
28073     resizeTabs : false,
28074     /*
28075      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
28076      */
28077     monitorResize : true,
28078     /*
28079      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
28080      */
28081     toolbar : false,
28082
28083     /**
28084      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
28085      * @param {String} id The id of the div to use <b>or create</b>
28086      * @param {String} text The text for the tab
28087      * @param {String} content (optional) Content to put in the TabPanelItem body
28088      * @param {Boolean} closable (optional) True to create a close icon on the tab
28089      * @return {Roo.TabPanelItem} The created TabPanelItem
28090      */
28091     addTab : function(id, text, content, closable){
28092         var item = new Roo.TabPanelItem(this, id, text, closable);
28093         this.addTabItem(item);
28094         if(content){
28095             item.setContent(content);
28096         }
28097         return item;
28098     },
28099
28100     /**
28101      * Returns the {@link Roo.TabPanelItem} with the specified id/index
28102      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
28103      * @return {Roo.TabPanelItem}
28104      */
28105     getTab : function(id){
28106         return this.items[id];
28107     },
28108
28109     /**
28110      * Hides the {@link Roo.TabPanelItem} with the specified id/index
28111      * @param {String/Number} id The id or index of the TabPanelItem to hide.
28112      */
28113     hideTab : function(id){
28114         var t = this.items[id];
28115         if(!t.isHidden()){
28116            t.setHidden(true);
28117            this.hiddenCount++;
28118            this.autoSizeTabs();
28119         }
28120     },
28121
28122     /**
28123      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
28124      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
28125      */
28126     unhideTab : function(id){
28127         var t = this.items[id];
28128         if(t.isHidden()){
28129            t.setHidden(false);
28130            this.hiddenCount--;
28131            this.autoSizeTabs();
28132         }
28133     },
28134
28135     /**
28136      * Adds an existing {@link Roo.TabPanelItem}.
28137      * @param {Roo.TabPanelItem} item The TabPanelItem to add
28138      */
28139     addTabItem : function(item){
28140         this.items[item.id] = item;
28141         this.items.push(item);
28142         if(this.resizeTabs){
28143            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
28144            this.autoSizeTabs();
28145         }else{
28146             item.autoSize();
28147         }
28148     },
28149
28150     /**
28151      * Removes a {@link Roo.TabPanelItem}.
28152      * @param {String/Number} id The id or index of the TabPanelItem to remove.
28153      */
28154     removeTab : function(id){
28155         var items = this.items;
28156         var tab = items[id];
28157         if(!tab) { return; }
28158         var index = items.indexOf(tab);
28159         if(this.active == tab && items.length > 1){
28160             var newTab = this.getNextAvailable(index);
28161             if(newTab) {
28162                 newTab.activate();
28163             }
28164         }
28165         this.stripEl.dom.removeChild(tab.pnode.dom);
28166         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
28167             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
28168         }
28169         items.splice(index, 1);
28170         delete this.items[tab.id];
28171         tab.fireEvent("close", tab);
28172         tab.purgeListeners();
28173         this.autoSizeTabs();
28174     },
28175
28176     getNextAvailable : function(start){
28177         var items = this.items;
28178         var index = start;
28179         // look for a next tab that will slide over to
28180         // replace the one being removed
28181         while(index < items.length){
28182             var item = items[++index];
28183             if(item && !item.isHidden()){
28184                 return item;
28185             }
28186         }
28187         // if one isn't found select the previous tab (on the left)
28188         index = start;
28189         while(index >= 0){
28190             var item = items[--index];
28191             if(item && !item.isHidden()){
28192                 return item;
28193             }
28194         }
28195         return null;
28196     },
28197
28198     /**
28199      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
28200      * @param {String/Number} id The id or index of the TabPanelItem to disable.
28201      */
28202     disableTab : function(id){
28203         var tab = this.items[id];
28204         if(tab && this.active != tab){
28205             tab.disable();
28206         }
28207     },
28208
28209     /**
28210      * Enables a {@link Roo.TabPanelItem} that is disabled.
28211      * @param {String/Number} id The id or index of the TabPanelItem to enable.
28212      */
28213     enableTab : function(id){
28214         var tab = this.items[id];
28215         tab.enable();
28216     },
28217
28218     /**
28219      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
28220      * @param {String/Number} id The id or index of the TabPanelItem to activate.
28221      * @return {Roo.TabPanelItem} The TabPanelItem.
28222      */
28223     activate : function(id){
28224         var tab = this.items[id];
28225         if(!tab){
28226             return null;
28227         }
28228         if(tab == this.active || tab.disabled){
28229             return tab;
28230         }
28231         var e = {};
28232         this.fireEvent("beforetabchange", this, e, tab);
28233         if(e.cancel !== true && !tab.disabled){
28234             if(this.active){
28235                 this.active.hide();
28236             }
28237             this.active = this.items[id];
28238             this.active.show();
28239             this.fireEvent("tabchange", this, this.active);
28240         }
28241         return tab;
28242     },
28243
28244     /**
28245      * Gets the active {@link Roo.TabPanelItem}.
28246      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
28247      */
28248     getActiveTab : function(){
28249         return this.active;
28250     },
28251
28252     /**
28253      * Updates the tab body element to fit the height of the container element
28254      * for overflow scrolling
28255      * @param {Number} targetHeight (optional) Override the starting height from the elements height
28256      */
28257     syncHeight : function(targetHeight){
28258         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28259         var bm = this.bodyEl.getMargins();
28260         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
28261         this.bodyEl.setHeight(newHeight);
28262         return newHeight;
28263     },
28264
28265     onResize : function(){
28266         if(this.monitorResize){
28267             this.autoSizeTabs();
28268         }
28269     },
28270
28271     /**
28272      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
28273      */
28274     beginUpdate : function(){
28275         this.updating = true;
28276     },
28277
28278     /**
28279      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
28280      */
28281     endUpdate : function(){
28282         this.updating = false;
28283         this.autoSizeTabs();
28284     },
28285
28286     /**
28287      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
28288      */
28289     autoSizeTabs : function(){
28290         var count = this.items.length;
28291         var vcount = count - this.hiddenCount;
28292         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
28293             return;
28294         }
28295         var w = Math.max(this.el.getWidth() - this.cpad, 10);
28296         var availWidth = Math.floor(w / vcount);
28297         var b = this.stripBody;
28298         if(b.getWidth() > w){
28299             var tabs = this.items;
28300             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
28301             if(availWidth < this.minTabWidth){
28302                 /*if(!this.sleft){    // incomplete scrolling code
28303                     this.createScrollButtons();
28304                 }
28305                 this.showScroll();
28306                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
28307             }
28308         }else{
28309             if(this.currentTabWidth < this.preferredTabWidth){
28310                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
28311             }
28312         }
28313     },
28314
28315     /**
28316      * Returns the number of tabs in this TabPanel.
28317      * @return {Number}
28318      */
28319      getCount : function(){
28320          return this.items.length;
28321      },
28322
28323     /**
28324      * Resizes all the tabs to the passed width
28325      * @param {Number} The new width
28326      */
28327     setTabWidth : function(width){
28328         this.currentTabWidth = width;
28329         for(var i = 0, len = this.items.length; i < len; i++) {
28330                 if(!this.items[i].isHidden()) {
28331                 this.items[i].setWidth(width);
28332             }
28333         }
28334     },
28335
28336     /**
28337      * Destroys this TabPanel
28338      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
28339      */
28340     destroy : function(removeEl){
28341         Roo.EventManager.removeResizeListener(this.onResize, this);
28342         for(var i = 0, len = this.items.length; i < len; i++){
28343             this.items[i].purgeListeners();
28344         }
28345         if(removeEl === true){
28346             this.el.update("");
28347             this.el.remove();
28348         }
28349     }
28350 });
28351
28352 /**
28353  * @class Roo.TabPanelItem
28354  * @extends Roo.util.Observable
28355  * Represents an individual item (tab plus body) in a TabPanel.
28356  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
28357  * @param {String} id The id of this TabPanelItem
28358  * @param {String} text The text for the tab of this TabPanelItem
28359  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
28360  */
28361 Roo.TabPanelItem = function(tabPanel, id, text, closable){
28362     /**
28363      * The {@link Roo.TabPanel} this TabPanelItem belongs to
28364      * @type Roo.TabPanel
28365      */
28366     this.tabPanel = tabPanel;
28367     /**
28368      * The id for this TabPanelItem
28369      * @type String
28370      */
28371     this.id = id;
28372     /** @private */
28373     this.disabled = false;
28374     /** @private */
28375     this.text = text;
28376     /** @private */
28377     this.loaded = false;
28378     this.closable = closable;
28379
28380     /**
28381      * The body element for this TabPanelItem.
28382      * @type Roo.Element
28383      */
28384     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
28385     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
28386     this.bodyEl.setStyle("display", "block");
28387     this.bodyEl.setStyle("zoom", "1");
28388     this.hideAction();
28389
28390     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
28391     /** @private */
28392     this.el = Roo.get(els.el, true);
28393     this.inner = Roo.get(els.inner, true);
28394     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
28395     this.pnode = Roo.get(els.el.parentNode, true);
28396     this.el.on("mousedown", this.onTabMouseDown, this);
28397     this.el.on("click", this.onTabClick, this);
28398     /** @private */
28399     if(closable){
28400         var c = Roo.get(els.close, true);
28401         c.dom.title = this.closeText;
28402         c.addClassOnOver("close-over");
28403         c.on("click", this.closeClick, this);
28404      }
28405
28406     this.addEvents({
28407          /**
28408          * @event activate
28409          * Fires when this tab becomes the active tab.
28410          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28411          * @param {Roo.TabPanelItem} this
28412          */
28413         "activate": true,
28414         /**
28415          * @event beforeclose
28416          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
28417          * @param {Roo.TabPanelItem} this
28418          * @param {Object} e Set cancel to true on this object to cancel the close.
28419          */
28420         "beforeclose": true,
28421         /**
28422          * @event close
28423          * Fires when this tab is closed.
28424          * @param {Roo.TabPanelItem} this
28425          */
28426          "close": true,
28427         /**
28428          * @event deactivate
28429          * Fires when this tab is no longer the active tab.
28430          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28431          * @param {Roo.TabPanelItem} this
28432          */
28433          "deactivate" : true
28434     });
28435     this.hidden = false;
28436
28437     Roo.TabPanelItem.superclass.constructor.call(this);
28438 };
28439
28440 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
28441     purgeListeners : function(){
28442        Roo.util.Observable.prototype.purgeListeners.call(this);
28443        this.el.removeAllListeners();
28444     },
28445     /**
28446      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
28447      */
28448     show : function(){
28449         this.pnode.addClass("on");
28450         this.showAction();
28451         if(Roo.isOpera){
28452             this.tabPanel.stripWrap.repaint();
28453         }
28454         this.fireEvent("activate", this.tabPanel, this);
28455     },
28456
28457     /**
28458      * Returns true if this tab is the active tab.
28459      * @return {Boolean}
28460      */
28461     isActive : function(){
28462         return this.tabPanel.getActiveTab() == this;
28463     },
28464
28465     /**
28466      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
28467      */
28468     hide : function(){
28469         this.pnode.removeClass("on");
28470         this.hideAction();
28471         this.fireEvent("deactivate", this.tabPanel, this);
28472     },
28473
28474     hideAction : function(){
28475         this.bodyEl.hide();
28476         this.bodyEl.setStyle("position", "absolute");
28477         this.bodyEl.setLeft("-20000px");
28478         this.bodyEl.setTop("-20000px");
28479     },
28480
28481     showAction : function(){
28482         this.bodyEl.setStyle("position", "relative");
28483         this.bodyEl.setTop("");
28484         this.bodyEl.setLeft("");
28485         this.bodyEl.show();
28486     },
28487
28488     /**
28489      * Set the tooltip for the tab.
28490      * @param {String} tooltip The tab's tooltip
28491      */
28492     setTooltip : function(text){
28493         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
28494             this.textEl.dom.qtip = text;
28495             this.textEl.dom.removeAttribute('title');
28496         }else{
28497             this.textEl.dom.title = text;
28498         }
28499     },
28500
28501     onTabClick : function(e){
28502         e.preventDefault();
28503         this.tabPanel.activate(this.id);
28504     },
28505
28506     onTabMouseDown : function(e){
28507         e.preventDefault();
28508         this.tabPanel.activate(this.id);
28509     },
28510
28511     getWidth : function(){
28512         return this.inner.getWidth();
28513     },
28514
28515     setWidth : function(width){
28516         var iwidth = width - this.pnode.getPadding("lr");
28517         this.inner.setWidth(iwidth);
28518         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
28519         this.pnode.setWidth(width);
28520     },
28521
28522     /**
28523      * Show or hide the tab
28524      * @param {Boolean} hidden True to hide or false to show.
28525      */
28526     setHidden : function(hidden){
28527         this.hidden = hidden;
28528         this.pnode.setStyle("display", hidden ? "none" : "");
28529     },
28530
28531     /**
28532      * Returns true if this tab is "hidden"
28533      * @return {Boolean}
28534      */
28535     isHidden : function(){
28536         return this.hidden;
28537     },
28538
28539     /**
28540      * Returns the text for this tab
28541      * @return {String}
28542      */
28543     getText : function(){
28544         return this.text;
28545     },
28546
28547     autoSize : function(){
28548         //this.el.beginMeasure();
28549         this.textEl.setWidth(1);
28550         /*
28551          *  #2804 [new] Tabs in Roojs
28552          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
28553          */
28554         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
28555         //this.el.endMeasure();
28556     },
28557
28558     /**
28559      * Sets the text for the tab (Note: this also sets the tooltip text)
28560      * @param {String} text The tab's text and tooltip
28561      */
28562     setText : function(text){
28563         this.text = text;
28564         this.textEl.update(text);
28565         this.setTooltip(text);
28566         if(!this.tabPanel.resizeTabs){
28567             this.autoSize();
28568         }
28569     },
28570     /**
28571      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
28572      */
28573     activate : function(){
28574         this.tabPanel.activate(this.id);
28575     },
28576
28577     /**
28578      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
28579      */
28580     disable : function(){
28581         if(this.tabPanel.active != this){
28582             this.disabled = true;
28583             this.pnode.addClass("disabled");
28584         }
28585     },
28586
28587     /**
28588      * Enables this TabPanelItem if it was previously disabled.
28589      */
28590     enable : function(){
28591         this.disabled = false;
28592         this.pnode.removeClass("disabled");
28593     },
28594
28595     /**
28596      * Sets the content for this TabPanelItem.
28597      * @param {String} content The content
28598      * @param {Boolean} loadScripts true to look for and load scripts
28599      */
28600     setContent : function(content, loadScripts){
28601         this.bodyEl.update(content, loadScripts);
28602     },
28603
28604     /**
28605      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
28606      * @return {Roo.UpdateManager} The UpdateManager
28607      */
28608     getUpdateManager : function(){
28609         return this.bodyEl.getUpdateManager();
28610     },
28611
28612     /**
28613      * Set a URL to be used to load the content for this TabPanelItem.
28614      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
28615      * @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)
28616      * @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)
28617      * @return {Roo.UpdateManager} The UpdateManager
28618      */
28619     setUrl : function(url, params, loadOnce){
28620         if(this.refreshDelegate){
28621             this.un('activate', this.refreshDelegate);
28622         }
28623         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
28624         this.on("activate", this.refreshDelegate);
28625         return this.bodyEl.getUpdateManager();
28626     },
28627
28628     /** @private */
28629     _handleRefresh : function(url, params, loadOnce){
28630         if(!loadOnce || !this.loaded){
28631             var updater = this.bodyEl.getUpdateManager();
28632             updater.update(url, params, this._setLoaded.createDelegate(this));
28633         }
28634     },
28635
28636     /**
28637      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
28638      *   Will fail silently if the setUrl method has not been called.
28639      *   This does not activate the panel, just updates its content.
28640      */
28641     refresh : function(){
28642         if(this.refreshDelegate){
28643            this.loaded = false;
28644            this.refreshDelegate();
28645         }
28646     },
28647
28648     /** @private */
28649     _setLoaded : function(){
28650         this.loaded = true;
28651     },
28652
28653     /** @private */
28654     closeClick : function(e){
28655         var o = {};
28656         e.stopEvent();
28657         this.fireEvent("beforeclose", this, o);
28658         if(o.cancel !== true){
28659             this.tabPanel.removeTab(this.id);
28660         }
28661     },
28662     /**
28663      * The text displayed in the tooltip for the close icon.
28664      * @type String
28665      */
28666     closeText : "Close this tab"
28667 });
28668
28669 /** @private */
28670 Roo.TabPanel.prototype.createStrip = function(container){
28671     var strip = document.createElement("div");
28672     strip.className = "x-tabs-wrap";
28673     container.appendChild(strip);
28674     return strip;
28675 };
28676 /** @private */
28677 Roo.TabPanel.prototype.createStripList = function(strip){
28678     // div wrapper for retard IE
28679     // returns the "tr" element.
28680     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
28681         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
28682         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
28683     return strip.firstChild.firstChild.firstChild.firstChild;
28684 };
28685 /** @private */
28686 Roo.TabPanel.prototype.createBody = function(container){
28687     var body = document.createElement("div");
28688     Roo.id(body, "tab-body");
28689     Roo.fly(body).addClass("x-tabs-body");
28690     container.appendChild(body);
28691     return body;
28692 };
28693 /** @private */
28694 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
28695     var body = Roo.getDom(id);
28696     if(!body){
28697         body = document.createElement("div");
28698         body.id = id;
28699     }
28700     Roo.fly(body).addClass("x-tabs-item-body");
28701     bodyEl.insertBefore(body, bodyEl.firstChild);
28702     return body;
28703 };
28704 /** @private */
28705 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
28706     var td = document.createElement("td");
28707     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
28708     //stripEl.appendChild(td);
28709     if(closable){
28710         td.className = "x-tabs-closable";
28711         if(!this.closeTpl){
28712             this.closeTpl = new Roo.Template(
28713                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
28714                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
28715                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
28716             );
28717         }
28718         var el = this.closeTpl.overwrite(td, {"text": text});
28719         var close = el.getElementsByTagName("div")[0];
28720         var inner = el.getElementsByTagName("em")[0];
28721         return {"el": el, "close": close, "inner": inner};
28722     } else {
28723         if(!this.tabTpl){
28724             this.tabTpl = new Roo.Template(
28725                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
28726                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
28727             );
28728         }
28729         var el = this.tabTpl.overwrite(td, {"text": text});
28730         var inner = el.getElementsByTagName("em")[0];
28731         return {"el": el, "inner": inner};
28732     }
28733 };/*
28734  * Based on:
28735  * Ext JS Library 1.1.1
28736  * Copyright(c) 2006-2007, Ext JS, LLC.
28737  *
28738  * Originally Released Under LGPL - original licence link has changed is not relivant.
28739  *
28740  * Fork - LGPL
28741  * <script type="text/javascript">
28742  */
28743
28744 /**
28745  * @class Roo.Button
28746  * @extends Roo.util.Observable
28747  * Simple Button class
28748  * @cfg {String} text The button text
28749  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
28750  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
28751  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
28752  * @cfg {Object} scope The scope of the handler
28753  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
28754  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
28755  * @cfg {Boolean} hidden True to start hidden (defaults to false)
28756  * @cfg {Boolean} disabled True to start disabled (defaults to false)
28757  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
28758  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
28759    applies if enableToggle = true)
28760  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
28761  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
28762   an {@link Roo.util.ClickRepeater} config object (defaults to false).
28763  * @constructor
28764  * Create a new button
28765  * @param {Object} config The config object
28766  */
28767 Roo.Button = function(renderTo, config)
28768 {
28769     if (!config) {
28770         config = renderTo;
28771         renderTo = config.renderTo || false;
28772     }
28773     
28774     Roo.apply(this, config);
28775     this.addEvents({
28776         /**
28777              * @event click
28778              * Fires when this button is clicked
28779              * @param {Button} this
28780              * @param {EventObject} e The click event
28781              */
28782             "click" : true,
28783         /**
28784              * @event toggle
28785              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
28786              * @param {Button} this
28787              * @param {Boolean} pressed
28788              */
28789             "toggle" : true,
28790         /**
28791              * @event mouseover
28792              * Fires when the mouse hovers over the button
28793              * @param {Button} this
28794              * @param {Event} e The event object
28795              */
28796         'mouseover' : true,
28797         /**
28798              * @event mouseout
28799              * Fires when the mouse exits the button
28800              * @param {Button} this
28801              * @param {Event} e The event object
28802              */
28803         'mouseout': true,
28804          /**
28805              * @event render
28806              * Fires when the button is rendered
28807              * @param {Button} this
28808              */
28809         'render': true
28810     });
28811     if(this.menu){
28812         this.menu = Roo.menu.MenuMgr.get(this.menu);
28813     }
28814     // register listeners first!!  - so render can be captured..
28815     Roo.util.Observable.call(this);
28816     if(renderTo){
28817         this.render(renderTo);
28818     }
28819     
28820   
28821 };
28822
28823 Roo.extend(Roo.Button, Roo.util.Observable, {
28824     /**
28825      * 
28826      */
28827     
28828     /**
28829      * Read-only. True if this button is hidden
28830      * @type Boolean
28831      */
28832     hidden : false,
28833     /**
28834      * Read-only. True if this button is disabled
28835      * @type Boolean
28836      */
28837     disabled : false,
28838     /**
28839      * Read-only. True if this button is pressed (only if enableToggle = true)
28840      * @type Boolean
28841      */
28842     pressed : false,
28843
28844     /**
28845      * @cfg {Number} tabIndex 
28846      * The DOM tabIndex for this button (defaults to undefined)
28847      */
28848     tabIndex : undefined,
28849
28850     /**
28851      * @cfg {Boolean} enableToggle
28852      * True to enable pressed/not pressed toggling (defaults to false)
28853      */
28854     enableToggle: false,
28855     /**
28856      * @cfg {Mixed} menu
28857      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
28858      */
28859     menu : undefined,
28860     /**
28861      * @cfg {String} menuAlign
28862      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
28863      */
28864     menuAlign : "tl-bl?",
28865
28866     /**
28867      * @cfg {String} iconCls
28868      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
28869      */
28870     iconCls : undefined,
28871     /**
28872      * @cfg {String} type
28873      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
28874      */
28875     type : 'button',
28876
28877     // private
28878     menuClassTarget: 'tr',
28879
28880     /**
28881      * @cfg {String} clickEvent
28882      * The type of event to map to the button's event handler (defaults to 'click')
28883      */
28884     clickEvent : 'click',
28885
28886     /**
28887      * @cfg {Boolean} handleMouseEvents
28888      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
28889      */
28890     handleMouseEvents : true,
28891
28892     /**
28893      * @cfg {String} tooltipType
28894      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
28895      */
28896     tooltipType : 'qtip',
28897
28898     /**
28899      * @cfg {String} cls
28900      * A CSS class to apply to the button's main element.
28901      */
28902     
28903     /**
28904      * @cfg {Roo.Template} template (Optional)
28905      * An {@link Roo.Template} with which to create the Button's main element. This Template must
28906      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
28907      * require code modifications if required elements (e.g. a button) aren't present.
28908      */
28909
28910     // private
28911     render : function(renderTo){
28912         var btn;
28913         if(this.hideParent){
28914             this.parentEl = Roo.get(renderTo);
28915         }
28916         if(!this.dhconfig){
28917             if(!this.template){
28918                 if(!Roo.Button.buttonTemplate){
28919                     // hideous table template
28920                     Roo.Button.buttonTemplate = new Roo.Template(
28921                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
28922                         '<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>',
28923                         "</tr></tbody></table>");
28924                 }
28925                 this.template = Roo.Button.buttonTemplate;
28926             }
28927             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
28928             var btnEl = btn.child("button:first");
28929             btnEl.on('focus', this.onFocus, this);
28930             btnEl.on('blur', this.onBlur, this);
28931             if(this.cls){
28932                 btn.addClass(this.cls);
28933             }
28934             if(this.icon){
28935                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
28936             }
28937             if(this.iconCls){
28938                 btnEl.addClass(this.iconCls);
28939                 if(!this.cls){
28940                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
28941                 }
28942             }
28943             if(this.tabIndex !== undefined){
28944                 btnEl.dom.tabIndex = this.tabIndex;
28945             }
28946             if(this.tooltip){
28947                 if(typeof this.tooltip == 'object'){
28948                     Roo.QuickTips.tips(Roo.apply({
28949                           target: btnEl.id
28950                     }, this.tooltip));
28951                 } else {
28952                     btnEl.dom[this.tooltipType] = this.tooltip;
28953                 }
28954             }
28955         }else{
28956             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
28957         }
28958         this.el = btn;
28959         if(this.id){
28960             this.el.dom.id = this.el.id = this.id;
28961         }
28962         if(this.menu){
28963             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
28964             this.menu.on("show", this.onMenuShow, this);
28965             this.menu.on("hide", this.onMenuHide, this);
28966         }
28967         btn.addClass("x-btn");
28968         if(Roo.isIE && !Roo.isIE7){
28969             this.autoWidth.defer(1, this);
28970         }else{
28971             this.autoWidth();
28972         }
28973         if(this.handleMouseEvents){
28974             btn.on("mouseover", this.onMouseOver, this);
28975             btn.on("mouseout", this.onMouseOut, this);
28976             btn.on("mousedown", this.onMouseDown, this);
28977         }
28978         btn.on(this.clickEvent, this.onClick, this);
28979         //btn.on("mouseup", this.onMouseUp, this);
28980         if(this.hidden){
28981             this.hide();
28982         }
28983         if(this.disabled){
28984             this.disable();
28985         }
28986         Roo.ButtonToggleMgr.register(this);
28987         if(this.pressed){
28988             this.el.addClass("x-btn-pressed");
28989         }
28990         if(this.repeat){
28991             var repeater = new Roo.util.ClickRepeater(btn,
28992                 typeof this.repeat == "object" ? this.repeat : {}
28993             );
28994             repeater.on("click", this.onClick,  this);
28995         }
28996         
28997         this.fireEvent('render', this);
28998         
28999     },
29000     /**
29001      * Returns the button's underlying element
29002      * @return {Roo.Element} The element
29003      */
29004     getEl : function(){
29005         return this.el;  
29006     },
29007     
29008     /**
29009      * Destroys this Button and removes any listeners.
29010      */
29011     destroy : function(){
29012         Roo.ButtonToggleMgr.unregister(this);
29013         this.el.removeAllListeners();
29014         this.purgeListeners();
29015         this.el.remove();
29016     },
29017
29018     // private
29019     autoWidth : function(){
29020         if(this.el){
29021             this.el.setWidth("auto");
29022             if(Roo.isIE7 && Roo.isStrict){
29023                 var ib = this.el.child('button');
29024                 if(ib && ib.getWidth() > 20){
29025                     ib.clip();
29026                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29027                 }
29028             }
29029             if(this.minWidth){
29030                 if(this.hidden){
29031                     this.el.beginMeasure();
29032                 }
29033                 if(this.el.getWidth() < this.minWidth){
29034                     this.el.setWidth(this.minWidth);
29035                 }
29036                 if(this.hidden){
29037                     this.el.endMeasure();
29038                 }
29039             }
29040         }
29041     },
29042
29043     /**
29044      * Assigns this button's click handler
29045      * @param {Function} handler The function to call when the button is clicked
29046      * @param {Object} scope (optional) Scope for the function passed in
29047      */
29048     setHandler : function(handler, scope){
29049         this.handler = handler;
29050         this.scope = scope;  
29051     },
29052     
29053     /**
29054      * Sets this button's text
29055      * @param {String} text The button text
29056      */
29057     setText : function(text){
29058         this.text = text;
29059         if(this.el){
29060             this.el.child("td.x-btn-center button.x-btn-text").update(text);
29061         }
29062         this.autoWidth();
29063     },
29064     
29065     /**
29066      * Gets the text for this button
29067      * @return {String} The button text
29068      */
29069     getText : function(){
29070         return this.text;  
29071     },
29072     
29073     /**
29074      * Show this button
29075      */
29076     show: function(){
29077         this.hidden = false;
29078         if(this.el){
29079             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
29080         }
29081     },
29082     
29083     /**
29084      * Hide this button
29085      */
29086     hide: function(){
29087         this.hidden = true;
29088         if(this.el){
29089             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
29090         }
29091     },
29092     
29093     /**
29094      * Convenience function for boolean show/hide
29095      * @param {Boolean} visible True to show, false to hide
29096      */
29097     setVisible: function(visible){
29098         if(visible) {
29099             this.show();
29100         }else{
29101             this.hide();
29102         }
29103     },
29104     
29105     /**
29106      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
29107      * @param {Boolean} state (optional) Force a particular state
29108      */
29109     toggle : function(state){
29110         state = state === undefined ? !this.pressed : state;
29111         if(state != this.pressed){
29112             if(state){
29113                 this.el.addClass("x-btn-pressed");
29114                 this.pressed = true;
29115                 this.fireEvent("toggle", this, true);
29116             }else{
29117                 this.el.removeClass("x-btn-pressed");
29118                 this.pressed = false;
29119                 this.fireEvent("toggle", this, false);
29120             }
29121             if(this.toggleHandler){
29122                 this.toggleHandler.call(this.scope || this, this, state);
29123             }
29124         }
29125     },
29126     
29127     /**
29128      * Focus the button
29129      */
29130     focus : function(){
29131         this.el.child('button:first').focus();
29132     },
29133     
29134     /**
29135      * Disable this button
29136      */
29137     disable : function(){
29138         if(this.el){
29139             this.el.addClass("x-btn-disabled");
29140         }
29141         this.disabled = true;
29142     },
29143     
29144     /**
29145      * Enable this button
29146      */
29147     enable : function(){
29148         if(this.el){
29149             this.el.removeClass("x-btn-disabled");
29150         }
29151         this.disabled = false;
29152     },
29153
29154     /**
29155      * Convenience function for boolean enable/disable
29156      * @param {Boolean} enabled True to enable, false to disable
29157      */
29158     setDisabled : function(v){
29159         this[v !== true ? "enable" : "disable"]();
29160     },
29161
29162     // private
29163     onClick : function(e)
29164     {
29165         if(e){
29166             e.preventDefault();
29167         }
29168         if(e.button != 0){
29169             return;
29170         }
29171         if(!this.disabled){
29172             if(this.enableToggle){
29173                 this.toggle();
29174             }
29175             if(this.menu && !this.menu.isVisible()){
29176                 this.menu.show(this.el, this.menuAlign);
29177             }
29178             this.fireEvent("click", this, e);
29179             if(this.handler){
29180                 this.el.removeClass("x-btn-over");
29181                 this.handler.call(this.scope || this, this, e);
29182             }
29183         }
29184     },
29185     // private
29186     onMouseOver : function(e){
29187         if(!this.disabled){
29188             this.el.addClass("x-btn-over");
29189             this.fireEvent('mouseover', this, e);
29190         }
29191     },
29192     // private
29193     onMouseOut : function(e){
29194         if(!e.within(this.el,  true)){
29195             this.el.removeClass("x-btn-over");
29196             this.fireEvent('mouseout', this, e);
29197         }
29198     },
29199     // private
29200     onFocus : function(e){
29201         if(!this.disabled){
29202             this.el.addClass("x-btn-focus");
29203         }
29204     },
29205     // private
29206     onBlur : function(e){
29207         this.el.removeClass("x-btn-focus");
29208     },
29209     // private
29210     onMouseDown : function(e){
29211         if(!this.disabled && e.button == 0){
29212             this.el.addClass("x-btn-click");
29213             Roo.get(document).on('mouseup', this.onMouseUp, this);
29214         }
29215     },
29216     // private
29217     onMouseUp : function(e){
29218         if(e.button == 0){
29219             this.el.removeClass("x-btn-click");
29220             Roo.get(document).un('mouseup', this.onMouseUp, this);
29221         }
29222     },
29223     // private
29224     onMenuShow : function(e){
29225         this.el.addClass("x-btn-menu-active");
29226     },
29227     // private
29228     onMenuHide : function(e){
29229         this.el.removeClass("x-btn-menu-active");
29230     }   
29231 });
29232
29233 // Private utility class used by Button
29234 Roo.ButtonToggleMgr = function(){
29235    var groups = {};
29236    
29237    function toggleGroup(btn, state){
29238        if(state){
29239            var g = groups[btn.toggleGroup];
29240            for(var i = 0, l = g.length; i < l; i++){
29241                if(g[i] != btn){
29242                    g[i].toggle(false);
29243                }
29244            }
29245        }
29246    }
29247    
29248    return {
29249        register : function(btn){
29250            if(!btn.toggleGroup){
29251                return;
29252            }
29253            var g = groups[btn.toggleGroup];
29254            if(!g){
29255                g = groups[btn.toggleGroup] = [];
29256            }
29257            g.push(btn);
29258            btn.on("toggle", toggleGroup);
29259        },
29260        
29261        unregister : function(btn){
29262            if(!btn.toggleGroup){
29263                return;
29264            }
29265            var g = groups[btn.toggleGroup];
29266            if(g){
29267                g.remove(btn);
29268                btn.un("toggle", toggleGroup);
29269            }
29270        }
29271    };
29272 }();/*
29273  * Based on:
29274  * Ext JS Library 1.1.1
29275  * Copyright(c) 2006-2007, Ext JS, LLC.
29276  *
29277  * Originally Released Under LGPL - original licence link has changed is not relivant.
29278  *
29279  * Fork - LGPL
29280  * <script type="text/javascript">
29281  */
29282  
29283 /**
29284  * @class Roo.SplitButton
29285  * @extends Roo.Button
29286  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
29287  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
29288  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
29289  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
29290  * @cfg {String} arrowTooltip The title attribute of the arrow
29291  * @constructor
29292  * Create a new menu button
29293  * @param {String/HTMLElement/Element} renderTo The element to append the button to
29294  * @param {Object} config The config object
29295  */
29296 Roo.SplitButton = function(renderTo, config){
29297     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
29298     /**
29299      * @event arrowclick
29300      * Fires when this button's arrow is clicked
29301      * @param {SplitButton} this
29302      * @param {EventObject} e The click event
29303      */
29304     this.addEvents({"arrowclick":true});
29305 };
29306
29307 Roo.extend(Roo.SplitButton, Roo.Button, {
29308     render : function(renderTo){
29309         // this is one sweet looking template!
29310         var tpl = new Roo.Template(
29311             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
29312             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
29313             '<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>',
29314             "</tbody></table></td><td>",
29315             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
29316             '<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>',
29317             "</tbody></table></td></tr></table>"
29318         );
29319         var btn = tpl.append(renderTo, [this.text, this.type], true);
29320         var btnEl = btn.child("button");
29321         if(this.cls){
29322             btn.addClass(this.cls);
29323         }
29324         if(this.icon){
29325             btnEl.setStyle('background-image', 'url(' +this.icon +')');
29326         }
29327         if(this.iconCls){
29328             btnEl.addClass(this.iconCls);
29329             if(!this.cls){
29330                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29331             }
29332         }
29333         this.el = btn;
29334         if(this.handleMouseEvents){
29335             btn.on("mouseover", this.onMouseOver, this);
29336             btn.on("mouseout", this.onMouseOut, this);
29337             btn.on("mousedown", this.onMouseDown, this);
29338             btn.on("mouseup", this.onMouseUp, this);
29339         }
29340         btn.on(this.clickEvent, this.onClick, this);
29341         if(this.tooltip){
29342             if(typeof this.tooltip == 'object'){
29343                 Roo.QuickTips.tips(Roo.apply({
29344                       target: btnEl.id
29345                 }, this.tooltip));
29346             } else {
29347                 btnEl.dom[this.tooltipType] = this.tooltip;
29348             }
29349         }
29350         if(this.arrowTooltip){
29351             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
29352         }
29353         if(this.hidden){
29354             this.hide();
29355         }
29356         if(this.disabled){
29357             this.disable();
29358         }
29359         if(this.pressed){
29360             this.el.addClass("x-btn-pressed");
29361         }
29362         if(Roo.isIE && !Roo.isIE7){
29363             this.autoWidth.defer(1, this);
29364         }else{
29365             this.autoWidth();
29366         }
29367         if(this.menu){
29368             this.menu.on("show", this.onMenuShow, this);
29369             this.menu.on("hide", this.onMenuHide, this);
29370         }
29371         this.fireEvent('render', this);
29372     },
29373
29374     // private
29375     autoWidth : function(){
29376         if(this.el){
29377             var tbl = this.el.child("table:first");
29378             var tbl2 = this.el.child("table:last");
29379             this.el.setWidth("auto");
29380             tbl.setWidth("auto");
29381             if(Roo.isIE7 && Roo.isStrict){
29382                 var ib = this.el.child('button:first');
29383                 if(ib && ib.getWidth() > 20){
29384                     ib.clip();
29385                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29386                 }
29387             }
29388             if(this.minWidth){
29389                 if(this.hidden){
29390                     this.el.beginMeasure();
29391                 }
29392                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
29393                     tbl.setWidth(this.minWidth-tbl2.getWidth());
29394                 }
29395                 if(this.hidden){
29396                     this.el.endMeasure();
29397                 }
29398             }
29399             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
29400         } 
29401     },
29402     /**
29403      * Sets this button's click handler
29404      * @param {Function} handler The function to call when the button is clicked
29405      * @param {Object} scope (optional) Scope for the function passed above
29406      */
29407     setHandler : function(handler, scope){
29408         this.handler = handler;
29409         this.scope = scope;  
29410     },
29411     
29412     /**
29413      * Sets this button's arrow click handler
29414      * @param {Function} handler The function to call when the arrow is clicked
29415      * @param {Object} scope (optional) Scope for the function passed above
29416      */
29417     setArrowHandler : function(handler, scope){
29418         this.arrowHandler = handler;
29419         this.scope = scope;  
29420     },
29421     
29422     /**
29423      * Focus the button
29424      */
29425     focus : function(){
29426         if(this.el){
29427             this.el.child("button:first").focus();
29428         }
29429     },
29430
29431     // private
29432     onClick : function(e){
29433         e.preventDefault();
29434         if(!this.disabled){
29435             if(e.getTarget(".x-btn-menu-arrow-wrap")){
29436                 if(this.menu && !this.menu.isVisible()){
29437                     this.menu.show(this.el, this.menuAlign);
29438                 }
29439                 this.fireEvent("arrowclick", this, e);
29440                 if(this.arrowHandler){
29441                     this.arrowHandler.call(this.scope || this, this, e);
29442                 }
29443             }else{
29444                 this.fireEvent("click", this, e);
29445                 if(this.handler){
29446                     this.handler.call(this.scope || this, this, e);
29447                 }
29448             }
29449         }
29450     },
29451     // private
29452     onMouseDown : function(e){
29453         if(!this.disabled){
29454             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
29455         }
29456     },
29457     // private
29458     onMouseUp : function(e){
29459         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
29460     }   
29461 });
29462
29463
29464 // backwards compat
29465 Roo.MenuButton = Roo.SplitButton;/*
29466  * Based on:
29467  * Ext JS Library 1.1.1
29468  * Copyright(c) 2006-2007, Ext JS, LLC.
29469  *
29470  * Originally Released Under LGPL - original licence link has changed is not relivant.
29471  *
29472  * Fork - LGPL
29473  * <script type="text/javascript">
29474  */
29475
29476 /**
29477  * @class Roo.Toolbar
29478  * Basic Toolbar class.
29479  * @constructor
29480  * Creates a new Toolbar
29481  * @param {Object} container The config object
29482  */ 
29483 Roo.Toolbar = function(container, buttons, config)
29484 {
29485     /// old consturctor format still supported..
29486     if(container instanceof Array){ // omit the container for later rendering
29487         buttons = container;
29488         config = buttons;
29489         container = null;
29490     }
29491     if (typeof(container) == 'object' && container.xtype) {
29492         config = container;
29493         container = config.container;
29494         buttons = config.buttons || []; // not really - use items!!
29495     }
29496     var xitems = [];
29497     if (config && config.items) {
29498         xitems = config.items;
29499         delete config.items;
29500     }
29501     Roo.apply(this, config);
29502     this.buttons = buttons;
29503     
29504     if(container){
29505         this.render(container);
29506     }
29507     this.xitems = xitems;
29508     Roo.each(xitems, function(b) {
29509         this.add(b);
29510     }, this);
29511     
29512 };
29513
29514 Roo.Toolbar.prototype = {
29515     /**
29516      * @cfg {Array} items
29517      * array of button configs or elements to add (will be converted to a MixedCollection)
29518      */
29519     
29520     /**
29521      * @cfg {String/HTMLElement/Element} container
29522      * The id or element that will contain the toolbar
29523      */
29524     // private
29525     render : function(ct){
29526         this.el = Roo.get(ct);
29527         if(this.cls){
29528             this.el.addClass(this.cls);
29529         }
29530         // using a table allows for vertical alignment
29531         // 100% width is needed by Safari...
29532         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
29533         this.tr = this.el.child("tr", true);
29534         var autoId = 0;
29535         this.items = new Roo.util.MixedCollection(false, function(o){
29536             return o.id || ("item" + (++autoId));
29537         });
29538         if(this.buttons){
29539             this.add.apply(this, this.buttons);
29540             delete this.buttons;
29541         }
29542     },
29543
29544     /**
29545      * Adds element(s) to the toolbar -- this function takes a variable number of 
29546      * arguments of mixed type and adds them to the toolbar.
29547      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
29548      * <ul>
29549      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
29550      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
29551      * <li>Field: Any form field (equivalent to {@link #addField})</li>
29552      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
29553      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
29554      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
29555      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
29556      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
29557      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
29558      * </ul>
29559      * @param {Mixed} arg2
29560      * @param {Mixed} etc.
29561      */
29562     add : function(){
29563         var a = arguments, l = a.length;
29564         for(var i = 0; i < l; i++){
29565             this._add(a[i]);
29566         }
29567     },
29568     // private..
29569     _add : function(el) {
29570         
29571         if (el.xtype) {
29572             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
29573         }
29574         
29575         if (el.applyTo){ // some kind of form field
29576             return this.addField(el);
29577         } 
29578         if (el.render){ // some kind of Toolbar.Item
29579             return this.addItem(el);
29580         }
29581         if (typeof el == "string"){ // string
29582             if(el == "separator" || el == "-"){
29583                 return this.addSeparator();
29584             }
29585             if (el == " "){
29586                 return this.addSpacer();
29587             }
29588             if(el == "->"){
29589                 return this.addFill();
29590             }
29591             return this.addText(el);
29592             
29593         }
29594         if(el.tagName){ // element
29595             return this.addElement(el);
29596         }
29597         if(typeof el == "object"){ // must be button config?
29598             return this.addButton(el);
29599         }
29600         // and now what?!?!
29601         return false;
29602         
29603     },
29604     
29605     /**
29606      * Add an Xtype element
29607      * @param {Object} xtype Xtype Object
29608      * @return {Object} created Object
29609      */
29610     addxtype : function(e){
29611         return this.add(e);  
29612     },
29613     
29614     /**
29615      * Returns the Element for this toolbar.
29616      * @return {Roo.Element}
29617      */
29618     getEl : function(){
29619         return this.el;  
29620     },
29621     
29622     /**
29623      * Adds a separator
29624      * @return {Roo.Toolbar.Item} The separator item
29625      */
29626     addSeparator : function(){
29627         return this.addItem(new Roo.Toolbar.Separator());
29628     },
29629
29630     /**
29631      * Adds a spacer element
29632      * @return {Roo.Toolbar.Spacer} The spacer item
29633      */
29634     addSpacer : function(){
29635         return this.addItem(new Roo.Toolbar.Spacer());
29636     },
29637
29638     /**
29639      * Adds a fill element that forces subsequent additions to the right side of the toolbar
29640      * @return {Roo.Toolbar.Fill} The fill item
29641      */
29642     addFill : function(){
29643         return this.addItem(new Roo.Toolbar.Fill());
29644     },
29645
29646     /**
29647      * Adds any standard HTML element to the toolbar
29648      * @param {String/HTMLElement/Element} el The element or id of the element to add
29649      * @return {Roo.Toolbar.Item} The element's item
29650      */
29651     addElement : function(el){
29652         return this.addItem(new Roo.Toolbar.Item(el));
29653     },
29654     /**
29655      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
29656      * @type Roo.util.MixedCollection  
29657      */
29658     items : false,
29659      
29660     /**
29661      * Adds any Toolbar.Item or subclass
29662      * @param {Roo.Toolbar.Item} item
29663      * @return {Roo.Toolbar.Item} The item
29664      */
29665     addItem : function(item){
29666         var td = this.nextBlock();
29667         item.render(td);
29668         this.items.add(item);
29669         return item;
29670     },
29671     
29672     /**
29673      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
29674      * @param {Object/Array} config A button config or array of configs
29675      * @return {Roo.Toolbar.Button/Array}
29676      */
29677     addButton : function(config){
29678         if(config instanceof Array){
29679             var buttons = [];
29680             for(var i = 0, len = config.length; i < len; i++) {
29681                 buttons.push(this.addButton(config[i]));
29682             }
29683             return buttons;
29684         }
29685         var b = config;
29686         if(!(config instanceof Roo.Toolbar.Button)){
29687             b = config.split ?
29688                 new Roo.Toolbar.SplitButton(config) :
29689                 new Roo.Toolbar.Button(config);
29690         }
29691         var td = this.nextBlock();
29692         b.render(td);
29693         this.items.add(b);
29694         return b;
29695     },
29696     
29697     /**
29698      * Adds text to the toolbar
29699      * @param {String} text The text to add
29700      * @return {Roo.Toolbar.Item} The element's item
29701      */
29702     addText : function(text){
29703         return this.addItem(new Roo.Toolbar.TextItem(text));
29704     },
29705     
29706     /**
29707      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
29708      * @param {Number} index The index where the item is to be inserted
29709      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
29710      * @return {Roo.Toolbar.Button/Item}
29711      */
29712     insertButton : function(index, item){
29713         if(item instanceof Array){
29714             var buttons = [];
29715             for(var i = 0, len = item.length; i < len; i++) {
29716                buttons.push(this.insertButton(index + i, item[i]));
29717             }
29718             return buttons;
29719         }
29720         if (!(item instanceof Roo.Toolbar.Button)){
29721            item = new Roo.Toolbar.Button(item);
29722         }
29723         var td = document.createElement("td");
29724         this.tr.insertBefore(td, this.tr.childNodes[index]);
29725         item.render(td);
29726         this.items.insert(index, item);
29727         return item;
29728     },
29729     
29730     /**
29731      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
29732      * @param {Object} config
29733      * @return {Roo.Toolbar.Item} The element's item
29734      */
29735     addDom : function(config, returnEl){
29736         var td = this.nextBlock();
29737         Roo.DomHelper.overwrite(td, config);
29738         var ti = new Roo.Toolbar.Item(td.firstChild);
29739         ti.render(td);
29740         this.items.add(ti);
29741         return ti;
29742     },
29743
29744     /**
29745      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
29746      * @type Roo.util.MixedCollection  
29747      */
29748     fields : false,
29749     
29750     /**
29751      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
29752      * Note: the field should not have been rendered yet. For a field that has already been
29753      * rendered, use {@link #addElement}.
29754      * @param {Roo.form.Field} field
29755      * @return {Roo.ToolbarItem}
29756      */
29757      
29758       
29759     addField : function(field) {
29760         if (!this.fields) {
29761             var autoId = 0;
29762             this.fields = new Roo.util.MixedCollection(false, function(o){
29763                 return o.id || ("item" + (++autoId));
29764             });
29765
29766         }
29767         
29768         var td = this.nextBlock();
29769         field.render(td);
29770         var ti = new Roo.Toolbar.Item(td.firstChild);
29771         ti.render(td);
29772         this.items.add(ti);
29773         this.fields.add(field);
29774         return ti;
29775     },
29776     /**
29777      * Hide the toolbar
29778      * @method hide
29779      */
29780      
29781       
29782     hide : function()
29783     {
29784         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
29785         this.el.child('div').hide();
29786     },
29787     /**
29788      * Show the toolbar
29789      * @method show
29790      */
29791     show : function()
29792     {
29793         this.el.child('div').show();
29794     },
29795       
29796     // private
29797     nextBlock : function(){
29798         var td = document.createElement("td");
29799         this.tr.appendChild(td);
29800         return td;
29801     },
29802
29803     // private
29804     destroy : function(){
29805         if(this.items){ // rendered?
29806             Roo.destroy.apply(Roo, this.items.items);
29807         }
29808         if(this.fields){ // rendered?
29809             Roo.destroy.apply(Roo, this.fields.items);
29810         }
29811         Roo.Element.uncache(this.el, this.tr);
29812     }
29813 };
29814
29815 /**
29816  * @class Roo.Toolbar.Item
29817  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
29818  * @constructor
29819  * Creates a new Item
29820  * @param {HTMLElement} el 
29821  */
29822 Roo.Toolbar.Item = function(el){
29823     var cfg = {};
29824     if (typeof (el.xtype) != 'undefined') {
29825         cfg = el;
29826         el = cfg.el;
29827     }
29828     
29829     this.el = Roo.getDom(el);
29830     this.id = Roo.id(this.el);
29831     this.hidden = false;
29832     
29833     this.addEvents({
29834          /**
29835              * @event render
29836              * Fires when the button is rendered
29837              * @param {Button} this
29838              */
29839         'render': true
29840     });
29841     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
29842 };
29843 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
29844 //Roo.Toolbar.Item.prototype = {
29845     
29846     /**
29847      * Get this item's HTML Element
29848      * @return {HTMLElement}
29849      */
29850     getEl : function(){
29851        return this.el;  
29852     },
29853
29854     // private
29855     render : function(td){
29856         
29857          this.td = td;
29858         td.appendChild(this.el);
29859         
29860         this.fireEvent('render', this);
29861     },
29862     
29863     /**
29864      * Removes and destroys this item.
29865      */
29866     destroy : function(){
29867         this.td.parentNode.removeChild(this.td);
29868     },
29869     
29870     /**
29871      * Shows this item.
29872      */
29873     show: function(){
29874         this.hidden = false;
29875         this.td.style.display = "";
29876     },
29877     
29878     /**
29879      * Hides this item.
29880      */
29881     hide: function(){
29882         this.hidden = true;
29883         this.td.style.display = "none";
29884     },
29885     
29886     /**
29887      * Convenience function for boolean show/hide.
29888      * @param {Boolean} visible true to show/false to hide
29889      */
29890     setVisible: function(visible){
29891         if(visible) {
29892             this.show();
29893         }else{
29894             this.hide();
29895         }
29896     },
29897     
29898     /**
29899      * Try to focus this item.
29900      */
29901     focus : function(){
29902         Roo.fly(this.el).focus();
29903     },
29904     
29905     /**
29906      * Disables this item.
29907      */
29908     disable : function(){
29909         Roo.fly(this.td).addClass("x-item-disabled");
29910         this.disabled = true;
29911         this.el.disabled = true;
29912     },
29913     
29914     /**
29915      * Enables this item.
29916      */
29917     enable : function(){
29918         Roo.fly(this.td).removeClass("x-item-disabled");
29919         this.disabled = false;
29920         this.el.disabled = false;
29921     }
29922 });
29923
29924
29925 /**
29926  * @class Roo.Toolbar.Separator
29927  * @extends Roo.Toolbar.Item
29928  * A simple toolbar separator class
29929  * @constructor
29930  * Creates a new Separator
29931  */
29932 Roo.Toolbar.Separator = function(cfg){
29933     
29934     var s = document.createElement("span");
29935     s.className = "ytb-sep";
29936     if (cfg) {
29937         cfg.el = s;
29938     }
29939     
29940     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
29941 };
29942 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
29943     enable:Roo.emptyFn,
29944     disable:Roo.emptyFn,
29945     focus:Roo.emptyFn
29946 });
29947
29948 /**
29949  * @class Roo.Toolbar.Spacer
29950  * @extends Roo.Toolbar.Item
29951  * A simple element that adds extra horizontal space to a toolbar.
29952  * @constructor
29953  * Creates a new Spacer
29954  */
29955 Roo.Toolbar.Spacer = function(cfg){
29956     var s = document.createElement("div");
29957     s.className = "ytb-spacer";
29958     if (cfg) {
29959         cfg.el = s;
29960     }
29961     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
29962 };
29963 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
29964     enable:Roo.emptyFn,
29965     disable:Roo.emptyFn,
29966     focus:Roo.emptyFn
29967 });
29968
29969 /**
29970  * @class Roo.Toolbar.Fill
29971  * @extends Roo.Toolbar.Spacer
29972  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
29973  * @constructor
29974  * Creates a new Spacer
29975  */
29976 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
29977     // private
29978     render : function(td){
29979         td.style.width = '100%';
29980         Roo.Toolbar.Fill.superclass.render.call(this, td);
29981     }
29982 });
29983
29984 /**
29985  * @class Roo.Toolbar.TextItem
29986  * @extends Roo.Toolbar.Item
29987  * A simple class that renders text directly into a toolbar.
29988  * @constructor
29989  * Creates a new TextItem
29990  * @cfg {string} text 
29991  */
29992 Roo.Toolbar.TextItem = function(cfg){
29993     var  text = cfg || "";
29994     if (typeof(cfg) == 'object') {
29995         text = cfg.text || "";
29996     }  else {
29997         cfg = null;
29998     }
29999     var s = document.createElement("span");
30000     s.className = "ytb-text";
30001     s.innerHTML = text;
30002     if (cfg) {
30003         cfg.el  = s;
30004     }
30005     
30006     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
30007 };
30008 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
30009     
30010      
30011     enable:Roo.emptyFn,
30012     disable:Roo.emptyFn,
30013     focus:Roo.emptyFn
30014 });
30015
30016 /**
30017  * @class Roo.Toolbar.Button
30018  * @extends Roo.Button
30019  * A button that renders into a toolbar.
30020  * @constructor
30021  * Creates a new Button
30022  * @param {Object} config A standard {@link Roo.Button} config object
30023  */
30024 Roo.Toolbar.Button = function(config){
30025     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
30026 };
30027 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
30028     render : function(td){
30029         this.td = td;
30030         Roo.Toolbar.Button.superclass.render.call(this, td);
30031     },
30032     
30033     /**
30034      * Removes and destroys this button
30035      */
30036     destroy : function(){
30037         Roo.Toolbar.Button.superclass.destroy.call(this);
30038         this.td.parentNode.removeChild(this.td);
30039     },
30040     
30041     /**
30042      * Shows this button
30043      */
30044     show: function(){
30045         this.hidden = false;
30046         this.td.style.display = "";
30047     },
30048     
30049     /**
30050      * Hides this button
30051      */
30052     hide: function(){
30053         this.hidden = true;
30054         this.td.style.display = "none";
30055     },
30056
30057     /**
30058      * Disables this item
30059      */
30060     disable : function(){
30061         Roo.fly(this.td).addClass("x-item-disabled");
30062         this.disabled = true;
30063     },
30064
30065     /**
30066      * Enables this item
30067      */
30068     enable : function(){
30069         Roo.fly(this.td).removeClass("x-item-disabled");
30070         this.disabled = false;
30071     }
30072 });
30073 // backwards compat
30074 Roo.ToolbarButton = Roo.Toolbar.Button;
30075
30076 /**
30077  * @class Roo.Toolbar.SplitButton
30078  * @extends Roo.SplitButton
30079  * A menu button that renders into a toolbar.
30080  * @constructor
30081  * Creates a new SplitButton
30082  * @param {Object} config A standard {@link Roo.SplitButton} config object
30083  */
30084 Roo.Toolbar.SplitButton = function(config){
30085     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
30086 };
30087 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
30088     render : function(td){
30089         this.td = td;
30090         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
30091     },
30092     
30093     /**
30094      * Removes and destroys this button
30095      */
30096     destroy : function(){
30097         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
30098         this.td.parentNode.removeChild(this.td);
30099     },
30100     
30101     /**
30102      * Shows this button
30103      */
30104     show: function(){
30105         this.hidden = false;
30106         this.td.style.display = "";
30107     },
30108     
30109     /**
30110      * Hides this button
30111      */
30112     hide: function(){
30113         this.hidden = true;
30114         this.td.style.display = "none";
30115     }
30116 });
30117
30118 // backwards compat
30119 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
30120  * Based on:
30121  * Ext JS Library 1.1.1
30122  * Copyright(c) 2006-2007, Ext JS, LLC.
30123  *
30124  * Originally Released Under LGPL - original licence link has changed is not relivant.
30125  *
30126  * Fork - LGPL
30127  * <script type="text/javascript">
30128  */
30129  
30130 /**
30131  * @class Roo.PagingToolbar
30132  * @extends Roo.Toolbar
30133  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
30134  * @constructor
30135  * Create a new PagingToolbar
30136  * @param {Object} config The config object
30137  */
30138 Roo.PagingToolbar = function(el, ds, config)
30139 {
30140     // old args format still supported... - xtype is prefered..
30141     if (typeof(el) == 'object' && el.xtype) {
30142         // created from xtype...
30143         config = el;
30144         ds = el.dataSource;
30145         el = config.container;
30146     }
30147     var items = [];
30148     if (config.items) {
30149         items = config.items;
30150         config.items = [];
30151     }
30152     
30153     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
30154     this.ds = ds;
30155     this.cursor = 0;
30156     this.renderButtons(this.el);
30157     this.bind(ds);
30158     
30159     // supprot items array.
30160    
30161     Roo.each(items, function(e) {
30162         this.add(Roo.factory(e));
30163     },this);
30164     
30165 };
30166
30167 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
30168     /**
30169      * @cfg {Roo.data.Store} dataSource
30170      * The underlying data store providing the paged data
30171      */
30172     /**
30173      * @cfg {String/HTMLElement/Element} container
30174      * container The id or element that will contain the toolbar
30175      */
30176     /**
30177      * @cfg {Boolean} displayInfo
30178      * True to display the displayMsg (defaults to false)
30179      */
30180     /**
30181      * @cfg {Number} pageSize
30182      * The number of records to display per page (defaults to 20)
30183      */
30184     pageSize: 20,
30185     /**
30186      * @cfg {String} displayMsg
30187      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
30188      */
30189     displayMsg : 'Displaying {0} - {1} of {2}',
30190     /**
30191      * @cfg {String} emptyMsg
30192      * The message to display when no records are found (defaults to "No data to display")
30193      */
30194     emptyMsg : 'No data to display',
30195     /**
30196      * Customizable piece of the default paging text (defaults to "Page")
30197      * @type String
30198      */
30199     beforePageText : "Page",
30200     /**
30201      * Customizable piece of the default paging text (defaults to "of %0")
30202      * @type String
30203      */
30204     afterPageText : "of {0}",
30205     /**
30206      * Customizable piece of the default paging text (defaults to "First Page")
30207      * @type String
30208      */
30209     firstText : "First Page",
30210     /**
30211      * Customizable piece of the default paging text (defaults to "Previous Page")
30212      * @type String
30213      */
30214     prevText : "Previous Page",
30215     /**
30216      * Customizable piece of the default paging text (defaults to "Next Page")
30217      * @type String
30218      */
30219     nextText : "Next Page",
30220     /**
30221      * Customizable piece of the default paging text (defaults to "Last Page")
30222      * @type String
30223      */
30224     lastText : "Last Page",
30225     /**
30226      * Customizable piece of the default paging text (defaults to "Refresh")
30227      * @type String
30228      */
30229     refreshText : "Refresh",
30230
30231     // private
30232     renderButtons : function(el){
30233         Roo.PagingToolbar.superclass.render.call(this, el);
30234         this.first = this.addButton({
30235             tooltip: this.firstText,
30236             cls: "x-btn-icon x-grid-page-first",
30237             disabled: true,
30238             handler: this.onClick.createDelegate(this, ["first"])
30239         });
30240         this.prev = this.addButton({
30241             tooltip: this.prevText,
30242             cls: "x-btn-icon x-grid-page-prev",
30243             disabled: true,
30244             handler: this.onClick.createDelegate(this, ["prev"])
30245         });
30246         //this.addSeparator();
30247         this.add(this.beforePageText);
30248         this.field = Roo.get(this.addDom({
30249            tag: "input",
30250            type: "text",
30251            size: "3",
30252            value: "1",
30253            cls: "x-grid-page-number"
30254         }).el);
30255         this.field.on("keydown", this.onPagingKeydown, this);
30256         this.field.on("focus", function(){this.dom.select();});
30257         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
30258         this.field.setHeight(18);
30259         //this.addSeparator();
30260         this.next = this.addButton({
30261             tooltip: this.nextText,
30262             cls: "x-btn-icon x-grid-page-next",
30263             disabled: true,
30264             handler: this.onClick.createDelegate(this, ["next"])
30265         });
30266         this.last = this.addButton({
30267             tooltip: this.lastText,
30268             cls: "x-btn-icon x-grid-page-last",
30269             disabled: true,
30270             handler: this.onClick.createDelegate(this, ["last"])
30271         });
30272         //this.addSeparator();
30273         this.loading = this.addButton({
30274             tooltip: this.refreshText,
30275             cls: "x-btn-icon x-grid-loading",
30276             handler: this.onClick.createDelegate(this, ["refresh"])
30277         });
30278
30279         if(this.displayInfo){
30280             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
30281         }
30282     },
30283
30284     // private
30285     updateInfo : function(){
30286         if(this.displayEl){
30287             var count = this.ds.getCount();
30288             var msg = count == 0 ?
30289                 this.emptyMsg :
30290                 String.format(
30291                     this.displayMsg,
30292                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
30293                 );
30294             this.displayEl.update(msg);
30295         }
30296     },
30297
30298     // private
30299     onLoad : function(ds, r, o){
30300        this.cursor = o.params ? o.params.start : 0;
30301        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
30302
30303        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
30304        this.field.dom.value = ap;
30305        this.first.setDisabled(ap == 1);
30306        this.prev.setDisabled(ap == 1);
30307        this.next.setDisabled(ap == ps);
30308        this.last.setDisabled(ap == ps);
30309        this.loading.enable();
30310        this.updateInfo();
30311     },
30312
30313     // private
30314     getPageData : function(){
30315         var total = this.ds.getTotalCount();
30316         return {
30317             total : total,
30318             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
30319             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
30320         };
30321     },
30322
30323     // private
30324     onLoadError : function(){
30325         this.loading.enable();
30326     },
30327
30328     // private
30329     onPagingKeydown : function(e){
30330         var k = e.getKey();
30331         var d = this.getPageData();
30332         if(k == e.RETURN){
30333             var v = this.field.dom.value, pageNum;
30334             if(!v || isNaN(pageNum = parseInt(v, 10))){
30335                 this.field.dom.value = d.activePage;
30336                 return;
30337             }
30338             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
30339             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30340             e.stopEvent();
30341         }
30342         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))
30343         {
30344           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
30345           this.field.dom.value = pageNum;
30346           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
30347           e.stopEvent();
30348         }
30349         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
30350         {
30351           var v = this.field.dom.value, pageNum; 
30352           var increment = (e.shiftKey) ? 10 : 1;
30353           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
30354             increment *= -1;
30355           }
30356           if(!v || isNaN(pageNum = parseInt(v, 10))) {
30357             this.field.dom.value = d.activePage;
30358             return;
30359           }
30360           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
30361           {
30362             this.field.dom.value = parseInt(v, 10) + increment;
30363             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
30364             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30365           }
30366           e.stopEvent();
30367         }
30368     },
30369
30370     // private
30371     beforeLoad : function(){
30372         if(this.loading){
30373             this.loading.disable();
30374         }
30375     },
30376
30377     // private
30378     onClick : function(which){
30379         var ds = this.ds;
30380         switch(which){
30381             case "first":
30382                 ds.load({params:{start: 0, limit: this.pageSize}});
30383             break;
30384             case "prev":
30385                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
30386             break;
30387             case "next":
30388                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
30389             break;
30390             case "last":
30391                 var total = ds.getTotalCount();
30392                 var extra = total % this.pageSize;
30393                 var lastStart = extra ? (total - extra) : total-this.pageSize;
30394                 ds.load({params:{start: lastStart, limit: this.pageSize}});
30395             break;
30396             case "refresh":
30397                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
30398             break;
30399         }
30400     },
30401
30402     /**
30403      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
30404      * @param {Roo.data.Store} store The data store to unbind
30405      */
30406     unbind : function(ds){
30407         ds.un("beforeload", this.beforeLoad, this);
30408         ds.un("load", this.onLoad, this);
30409         ds.un("loadexception", this.onLoadError, this);
30410         ds.un("remove", this.updateInfo, this);
30411         ds.un("add", this.updateInfo, this);
30412         this.ds = undefined;
30413     },
30414
30415     /**
30416      * Binds the paging toolbar to the specified {@link Roo.data.Store}
30417      * @param {Roo.data.Store} store The data store to bind
30418      */
30419     bind : function(ds){
30420         ds.on("beforeload", this.beforeLoad, this);
30421         ds.on("load", this.onLoad, this);
30422         ds.on("loadexception", this.onLoadError, this);
30423         ds.on("remove", this.updateInfo, this);
30424         ds.on("add", this.updateInfo, this);
30425         this.ds = ds;
30426     }
30427 });/*
30428  * Based on:
30429  * Ext JS Library 1.1.1
30430  * Copyright(c) 2006-2007, Ext JS, LLC.
30431  *
30432  * Originally Released Under LGPL - original licence link has changed is not relivant.
30433  *
30434  * Fork - LGPL
30435  * <script type="text/javascript">
30436  */
30437
30438 /**
30439  * @class Roo.Resizable
30440  * @extends Roo.util.Observable
30441  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
30442  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
30443  * 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
30444  * the element will be wrapped for you automatically.</p>
30445  * <p>Here is the list of valid resize handles:</p>
30446  * <pre>
30447 Value   Description
30448 ------  -------------------
30449  'n'     north
30450  's'     south
30451  'e'     east
30452  'w'     west
30453  'nw'    northwest
30454  'sw'    southwest
30455  'se'    southeast
30456  'ne'    northeast
30457  'hd'    horizontal drag
30458  'all'   all
30459 </pre>
30460  * <p>Here's an example showing the creation of a typical Resizable:</p>
30461  * <pre><code>
30462 var resizer = new Roo.Resizable("element-id", {
30463     handles: 'all',
30464     minWidth: 200,
30465     minHeight: 100,
30466     maxWidth: 500,
30467     maxHeight: 400,
30468     pinned: true
30469 });
30470 resizer.on("resize", myHandler);
30471 </code></pre>
30472  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
30473  * resizer.east.setDisplayed(false);</p>
30474  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
30475  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
30476  * resize operation's new size (defaults to [0, 0])
30477  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
30478  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
30479  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
30480  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
30481  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
30482  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
30483  * @cfg {Number} width The width of the element in pixels (defaults to null)
30484  * @cfg {Number} height The height of the element in pixels (defaults to null)
30485  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
30486  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
30487  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
30488  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
30489  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
30490  * in favor of the handles config option (defaults to false)
30491  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
30492  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
30493  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
30494  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
30495  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
30496  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
30497  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
30498  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
30499  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
30500  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
30501  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
30502  * @constructor
30503  * Create a new resizable component
30504  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
30505  * @param {Object} config configuration options
30506   */
30507 Roo.Resizable = function(el, config)
30508 {
30509     this.el = Roo.get(el);
30510
30511     if(config && config.wrap){
30512         config.resizeChild = this.el;
30513         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
30514         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
30515         this.el.setStyle("overflow", "hidden");
30516         this.el.setPositioning(config.resizeChild.getPositioning());
30517         config.resizeChild.clearPositioning();
30518         if(!config.width || !config.height){
30519             var csize = config.resizeChild.getSize();
30520             this.el.setSize(csize.width, csize.height);
30521         }
30522         if(config.pinned && !config.adjustments){
30523             config.adjustments = "auto";
30524         }
30525     }
30526
30527     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
30528     this.proxy.unselectable();
30529     this.proxy.enableDisplayMode('block');
30530
30531     Roo.apply(this, config);
30532
30533     if(this.pinned){
30534         this.disableTrackOver = true;
30535         this.el.addClass("x-resizable-pinned");
30536     }
30537     // if the element isn't positioned, make it relative
30538     var position = this.el.getStyle("position");
30539     if(position != "absolute" && position != "fixed"){
30540         this.el.setStyle("position", "relative");
30541     }
30542     if(!this.handles){ // no handles passed, must be legacy style
30543         this.handles = 's,e,se';
30544         if(this.multiDirectional){
30545             this.handles += ',n,w';
30546         }
30547     }
30548     if(this.handles == "all"){
30549         this.handles = "n s e w ne nw se sw";
30550     }
30551     var hs = this.handles.split(/\s*?[,;]\s*?| /);
30552     var ps = Roo.Resizable.positions;
30553     for(var i = 0, len = hs.length; i < len; i++){
30554         if(hs[i] && ps[hs[i]]){
30555             var pos = ps[hs[i]];
30556             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
30557         }
30558     }
30559     // legacy
30560     this.corner = this.southeast;
30561     
30562     // updateBox = the box can move..
30563     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
30564         this.updateBox = true;
30565     }
30566
30567     this.activeHandle = null;
30568
30569     if(this.resizeChild){
30570         if(typeof this.resizeChild == "boolean"){
30571             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
30572         }else{
30573             this.resizeChild = Roo.get(this.resizeChild, true);
30574         }
30575     }
30576     
30577     if(this.adjustments == "auto"){
30578         var rc = this.resizeChild;
30579         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
30580         if(rc && (hw || hn)){
30581             rc.position("relative");
30582             rc.setLeft(hw ? hw.el.getWidth() : 0);
30583             rc.setTop(hn ? hn.el.getHeight() : 0);
30584         }
30585         this.adjustments = [
30586             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
30587             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
30588         ];
30589     }
30590
30591     if(this.draggable){
30592         this.dd = this.dynamic ?
30593             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
30594         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
30595     }
30596
30597     // public events
30598     this.addEvents({
30599         /**
30600          * @event beforeresize
30601          * Fired before resize is allowed. Set enabled to false to cancel resize.
30602          * @param {Roo.Resizable} this
30603          * @param {Roo.EventObject} e The mousedown event
30604          */
30605         "beforeresize" : true,
30606         /**
30607          * @event resizing
30608          * Fired a resizing.
30609          * @param {Roo.Resizable} this
30610          * @param {Number} x The new x position
30611          * @param {Number} y The new y position
30612          * @param {Number} w The new w width
30613          * @param {Number} h The new h hight
30614          * @param {Roo.EventObject} e The mouseup event
30615          */
30616         "resizing" : true,
30617         /**
30618          * @event resize
30619          * Fired after a resize.
30620          * @param {Roo.Resizable} this
30621          * @param {Number} width The new width
30622          * @param {Number} height The new height
30623          * @param {Roo.EventObject} e The mouseup event
30624          */
30625         "resize" : true
30626     });
30627
30628     if(this.width !== null && this.height !== null){
30629         this.resizeTo(this.width, this.height);
30630     }else{
30631         this.updateChildSize();
30632     }
30633     if(Roo.isIE){
30634         this.el.dom.style.zoom = 1;
30635     }
30636     Roo.Resizable.superclass.constructor.call(this);
30637 };
30638
30639 Roo.extend(Roo.Resizable, Roo.util.Observable, {
30640         resizeChild : false,
30641         adjustments : [0, 0],
30642         minWidth : 5,
30643         minHeight : 5,
30644         maxWidth : 10000,
30645         maxHeight : 10000,
30646         enabled : true,
30647         animate : false,
30648         duration : .35,
30649         dynamic : false,
30650         handles : false,
30651         multiDirectional : false,
30652         disableTrackOver : false,
30653         easing : 'easeOutStrong',
30654         widthIncrement : 0,
30655         heightIncrement : 0,
30656         pinned : false,
30657         width : null,
30658         height : null,
30659         preserveRatio : false,
30660         transparent: false,
30661         minX: 0,
30662         minY: 0,
30663         draggable: false,
30664
30665         /**
30666          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
30667          */
30668         constrainTo: undefined,
30669         /**
30670          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
30671          */
30672         resizeRegion: undefined,
30673
30674
30675     /**
30676      * Perform a manual resize
30677      * @param {Number} width
30678      * @param {Number} height
30679      */
30680     resizeTo : function(width, height){
30681         this.el.setSize(width, height);
30682         this.updateChildSize();
30683         this.fireEvent("resize", this, width, height, null);
30684     },
30685
30686     // private
30687     startSizing : function(e, handle){
30688         this.fireEvent("beforeresize", this, e);
30689         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
30690
30691             if(!this.overlay){
30692                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
30693                 this.overlay.unselectable();
30694                 this.overlay.enableDisplayMode("block");
30695                 this.overlay.on("mousemove", this.onMouseMove, this);
30696                 this.overlay.on("mouseup", this.onMouseUp, this);
30697             }
30698             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
30699
30700             this.resizing = true;
30701             this.startBox = this.el.getBox();
30702             this.startPoint = e.getXY();
30703             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
30704                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
30705
30706             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30707             this.overlay.show();
30708
30709             if(this.constrainTo) {
30710                 var ct = Roo.get(this.constrainTo);
30711                 this.resizeRegion = ct.getRegion().adjust(
30712                     ct.getFrameWidth('t'),
30713                     ct.getFrameWidth('l'),
30714                     -ct.getFrameWidth('b'),
30715                     -ct.getFrameWidth('r')
30716                 );
30717             }
30718
30719             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
30720             this.proxy.show();
30721             this.proxy.setBox(this.startBox);
30722             if(!this.dynamic){
30723                 this.proxy.setStyle('visibility', 'visible');
30724             }
30725         }
30726     },
30727
30728     // private
30729     onMouseDown : function(handle, e){
30730         if(this.enabled){
30731             e.stopEvent();
30732             this.activeHandle = handle;
30733             this.startSizing(e, handle);
30734         }
30735     },
30736
30737     // private
30738     onMouseUp : function(e){
30739         var size = this.resizeElement();
30740         this.resizing = false;
30741         this.handleOut();
30742         this.overlay.hide();
30743         this.proxy.hide();
30744         this.fireEvent("resize", this, size.width, size.height, e);
30745     },
30746
30747     // private
30748     updateChildSize : function(){
30749         
30750         if(this.resizeChild){
30751             var el = this.el;
30752             var child = this.resizeChild;
30753             var adj = this.adjustments;
30754             if(el.dom.offsetWidth){
30755                 var b = el.getSize(true);
30756                 child.setSize(b.width+adj[0], b.height+adj[1]);
30757             }
30758             // Second call here for IE
30759             // The first call enables instant resizing and
30760             // the second call corrects scroll bars if they
30761             // exist
30762             if(Roo.isIE){
30763                 setTimeout(function(){
30764                     if(el.dom.offsetWidth){
30765                         var b = el.getSize(true);
30766                         child.setSize(b.width+adj[0], b.height+adj[1]);
30767                     }
30768                 }, 10);
30769             }
30770         }
30771     },
30772
30773     // private
30774     snap : function(value, inc, min){
30775         if(!inc || !value) {
30776             return value;
30777         }
30778         var newValue = value;
30779         var m = value % inc;
30780         if(m > 0){
30781             if(m > (inc/2)){
30782                 newValue = value + (inc-m);
30783             }else{
30784                 newValue = value - m;
30785             }
30786         }
30787         return Math.max(min, newValue);
30788     },
30789
30790     // private
30791     resizeElement : function(){
30792         var box = this.proxy.getBox();
30793         if(this.updateBox){
30794             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
30795         }else{
30796             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
30797         }
30798         this.updateChildSize();
30799         if(!this.dynamic){
30800             this.proxy.hide();
30801         }
30802         return box;
30803     },
30804
30805     // private
30806     constrain : function(v, diff, m, mx){
30807         if(v - diff < m){
30808             diff = v - m;
30809         }else if(v - diff > mx){
30810             diff = mx - v;
30811         }
30812         return diff;
30813     },
30814
30815     // private
30816     onMouseMove : function(e){
30817         
30818         if(this.enabled){
30819             try{// try catch so if something goes wrong the user doesn't get hung
30820
30821             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
30822                 return;
30823             }
30824
30825             //var curXY = this.startPoint;
30826             var curSize = this.curSize || this.startBox;
30827             var x = this.startBox.x, y = this.startBox.y;
30828             var ox = x, oy = y;
30829             var w = curSize.width, h = curSize.height;
30830             var ow = w, oh = h;
30831             var mw = this.minWidth, mh = this.minHeight;
30832             var mxw = this.maxWidth, mxh = this.maxHeight;
30833             var wi = this.widthIncrement;
30834             var hi = this.heightIncrement;
30835
30836             var eventXY = e.getXY();
30837             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
30838             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
30839
30840             var pos = this.activeHandle.position;
30841
30842             switch(pos){
30843                 case "east":
30844                     w += diffX;
30845                     w = Math.min(Math.max(mw, w), mxw);
30846                     break;
30847              
30848                 case "south":
30849                     h += diffY;
30850                     h = Math.min(Math.max(mh, h), mxh);
30851                     break;
30852                 case "southeast":
30853                     w += diffX;
30854                     h += diffY;
30855                     w = Math.min(Math.max(mw, w), mxw);
30856                     h = Math.min(Math.max(mh, h), mxh);
30857                     break;
30858                 case "north":
30859                     diffY = this.constrain(h, diffY, mh, mxh);
30860                     y += diffY;
30861                     h -= diffY;
30862                     break;
30863                 case "hdrag":
30864                     
30865                     if (wi) {
30866                         var adiffX = Math.abs(diffX);
30867                         var sub = (adiffX % wi); // how much 
30868                         if (sub > (wi/2)) { // far enough to snap
30869                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
30870                         } else {
30871                             // remove difference.. 
30872                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
30873                         }
30874                     }
30875                     x += diffX;
30876                     x = Math.max(this.minX, x);
30877                     break;
30878                 case "west":
30879                     diffX = this.constrain(w, diffX, mw, mxw);
30880                     x += diffX;
30881                     w -= diffX;
30882                     break;
30883                 case "northeast":
30884                     w += diffX;
30885                     w = Math.min(Math.max(mw, w), mxw);
30886                     diffY = this.constrain(h, diffY, mh, mxh);
30887                     y += diffY;
30888                     h -= diffY;
30889                     break;
30890                 case "northwest":
30891                     diffX = this.constrain(w, diffX, mw, mxw);
30892                     diffY = this.constrain(h, diffY, mh, mxh);
30893                     y += diffY;
30894                     h -= diffY;
30895                     x += diffX;
30896                     w -= diffX;
30897                     break;
30898                case "southwest":
30899                     diffX = this.constrain(w, diffX, mw, mxw);
30900                     h += diffY;
30901                     h = Math.min(Math.max(mh, h), mxh);
30902                     x += diffX;
30903                     w -= diffX;
30904                     break;
30905             }
30906
30907             var sw = this.snap(w, wi, mw);
30908             var sh = this.snap(h, hi, mh);
30909             if(sw != w || sh != h){
30910                 switch(pos){
30911                     case "northeast":
30912                         y -= sh - h;
30913                     break;
30914                     case "north":
30915                         y -= sh - h;
30916                         break;
30917                     case "southwest":
30918                         x -= sw - w;
30919                     break;
30920                     case "west":
30921                         x -= sw - w;
30922                         break;
30923                     case "northwest":
30924                         x -= sw - w;
30925                         y -= sh - h;
30926                     break;
30927                 }
30928                 w = sw;
30929                 h = sh;
30930             }
30931
30932             if(this.preserveRatio){
30933                 switch(pos){
30934                     case "southeast":
30935                     case "east":
30936                         h = oh * (w/ow);
30937                         h = Math.min(Math.max(mh, h), mxh);
30938                         w = ow * (h/oh);
30939                        break;
30940                     case "south":
30941                         w = ow * (h/oh);
30942                         w = Math.min(Math.max(mw, w), mxw);
30943                         h = oh * (w/ow);
30944                         break;
30945                     case "northeast":
30946                         w = ow * (h/oh);
30947                         w = Math.min(Math.max(mw, w), mxw);
30948                         h = oh * (w/ow);
30949                     break;
30950                     case "north":
30951                         var tw = w;
30952                         w = ow * (h/oh);
30953                         w = Math.min(Math.max(mw, w), mxw);
30954                         h = oh * (w/ow);
30955                         x += (tw - w) / 2;
30956                         break;
30957                     case "southwest":
30958                         h = oh * (w/ow);
30959                         h = Math.min(Math.max(mh, h), mxh);
30960                         var tw = w;
30961                         w = ow * (h/oh);
30962                         x += tw - w;
30963                         break;
30964                     case "west":
30965                         var th = h;
30966                         h = oh * (w/ow);
30967                         h = Math.min(Math.max(mh, h), mxh);
30968                         y += (th - h) / 2;
30969                         var tw = w;
30970                         w = ow * (h/oh);
30971                         x += tw - w;
30972                        break;
30973                     case "northwest":
30974                         var tw = w;
30975                         var th = h;
30976                         h = oh * (w/ow);
30977                         h = Math.min(Math.max(mh, h), mxh);
30978                         w = ow * (h/oh);
30979                         y += th - h;
30980                         x += tw - w;
30981                        break;
30982
30983                 }
30984             }
30985             if (pos == 'hdrag') {
30986                 w = ow;
30987             }
30988             this.proxy.setBounds(x, y, w, h);
30989             if(this.dynamic){
30990                 this.resizeElement();
30991             }
30992             }catch(e){}
30993         }
30994         this.fireEvent("resizing", this, x, y, w, h, e);
30995     },
30996
30997     // private
30998     handleOver : function(){
30999         if(this.enabled){
31000             this.el.addClass("x-resizable-over");
31001         }
31002     },
31003
31004     // private
31005     handleOut : function(){
31006         if(!this.resizing){
31007             this.el.removeClass("x-resizable-over");
31008         }
31009     },
31010
31011     /**
31012      * Returns the element this component is bound to.
31013      * @return {Roo.Element}
31014      */
31015     getEl : function(){
31016         return this.el;
31017     },
31018
31019     /**
31020      * Returns the resizeChild element (or null).
31021      * @return {Roo.Element}
31022      */
31023     getResizeChild : function(){
31024         return this.resizeChild;
31025     },
31026     groupHandler : function()
31027     {
31028         
31029     },
31030     /**
31031      * Destroys this resizable. If the element was wrapped and
31032      * removeEl is not true then the element remains.
31033      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
31034      */
31035     destroy : function(removeEl){
31036         this.proxy.remove();
31037         if(this.overlay){
31038             this.overlay.removeAllListeners();
31039             this.overlay.remove();
31040         }
31041         var ps = Roo.Resizable.positions;
31042         for(var k in ps){
31043             if(typeof ps[k] != "function" && this[ps[k]]){
31044                 var h = this[ps[k]];
31045                 h.el.removeAllListeners();
31046                 h.el.remove();
31047             }
31048         }
31049         if(removeEl){
31050             this.el.update("");
31051             this.el.remove();
31052         }
31053     }
31054 });
31055
31056 // private
31057 // hash to map config positions to true positions
31058 Roo.Resizable.positions = {
31059     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
31060     hd: "hdrag"
31061 };
31062
31063 // private
31064 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
31065     if(!this.tpl){
31066         // only initialize the template if resizable is used
31067         var tpl = Roo.DomHelper.createTemplate(
31068             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
31069         );
31070         tpl.compile();
31071         Roo.Resizable.Handle.prototype.tpl = tpl;
31072     }
31073     this.position = pos;
31074     this.rz = rz;
31075     // show north drag fro topdra
31076     var handlepos = pos == 'hdrag' ? 'north' : pos;
31077     
31078     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
31079     if (pos == 'hdrag') {
31080         this.el.setStyle('cursor', 'pointer');
31081     }
31082     this.el.unselectable();
31083     if(transparent){
31084         this.el.setOpacity(0);
31085     }
31086     this.el.on("mousedown", this.onMouseDown, this);
31087     if(!disableTrackOver){
31088         this.el.on("mouseover", this.onMouseOver, this);
31089         this.el.on("mouseout", this.onMouseOut, this);
31090     }
31091 };
31092
31093 // private
31094 Roo.Resizable.Handle.prototype = {
31095     afterResize : function(rz){
31096         Roo.log('after?');
31097         // do nothing
31098     },
31099     // private
31100     onMouseDown : function(e){
31101         this.rz.onMouseDown(this, e);
31102     },
31103     // private
31104     onMouseOver : function(e){
31105         this.rz.handleOver(this, e);
31106     },
31107     // private
31108     onMouseOut : function(e){
31109         this.rz.handleOut(this, e);
31110     }
31111 };/*
31112  * Based on:
31113  * Ext JS Library 1.1.1
31114  * Copyright(c) 2006-2007, Ext JS, LLC.
31115  *
31116  * Originally Released Under LGPL - original licence link has changed is not relivant.
31117  *
31118  * Fork - LGPL
31119  * <script type="text/javascript">
31120  */
31121
31122 /**
31123  * @class Roo.Editor
31124  * @extends Roo.Component
31125  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
31126  * @constructor
31127  * Create a new Editor
31128  * @param {Roo.form.Field} field The Field object (or descendant)
31129  * @param {Object} config The config object
31130  */
31131 Roo.Editor = function(field, config){
31132     Roo.Editor.superclass.constructor.call(this, config);
31133     this.field = field;
31134     this.addEvents({
31135         /**
31136              * @event beforestartedit
31137              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
31138              * false from the handler of this event.
31139              * @param {Editor} this
31140              * @param {Roo.Element} boundEl The underlying element bound to this editor
31141              * @param {Mixed} value The field value being set
31142              */
31143         "beforestartedit" : true,
31144         /**
31145              * @event startedit
31146              * Fires when this editor is displayed
31147              * @param {Roo.Element} boundEl The underlying element bound to this editor
31148              * @param {Mixed} value The starting field value
31149              */
31150         "startedit" : true,
31151         /**
31152              * @event beforecomplete
31153              * Fires after a change has been made to the field, but before the change is reflected in the underlying
31154              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
31155              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
31156              * event will not fire since no edit actually occurred.
31157              * @param {Editor} this
31158              * @param {Mixed} value The current field value
31159              * @param {Mixed} startValue The original field value
31160              */
31161         "beforecomplete" : true,
31162         /**
31163              * @event complete
31164              * Fires after editing is complete and any changed value has been written to the underlying field.
31165              * @param {Editor} this
31166              * @param {Mixed} value The current field value
31167              * @param {Mixed} startValue The original field value
31168              */
31169         "complete" : true,
31170         /**
31171          * @event specialkey
31172          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
31173          * {@link Roo.EventObject#getKey} to determine which key was pressed.
31174          * @param {Roo.form.Field} this
31175          * @param {Roo.EventObject} e The event object
31176          */
31177         "specialkey" : true
31178     });
31179 };
31180
31181 Roo.extend(Roo.Editor, Roo.Component, {
31182     /**
31183      * @cfg {Boolean/String} autosize
31184      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
31185      * or "height" to adopt the height only (defaults to false)
31186      */
31187     /**
31188      * @cfg {Boolean} revertInvalid
31189      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
31190      * validation fails (defaults to true)
31191      */
31192     /**
31193      * @cfg {Boolean} ignoreNoChange
31194      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
31195      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
31196      * will never be ignored.
31197      */
31198     /**
31199      * @cfg {Boolean} hideEl
31200      * False to keep the bound element visible while the editor is displayed (defaults to true)
31201      */
31202     /**
31203      * @cfg {Mixed} value
31204      * The data value of the underlying field (defaults to "")
31205      */
31206     value : "",
31207     /**
31208      * @cfg {String} alignment
31209      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
31210      */
31211     alignment: "c-c?",
31212     /**
31213      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
31214      * for bottom-right shadow (defaults to "frame")
31215      */
31216     shadow : "frame",
31217     /**
31218      * @cfg {Boolean} constrain True to constrain the editor to the viewport
31219      */
31220     constrain : false,
31221     /**
31222      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
31223      */
31224     completeOnEnter : false,
31225     /**
31226      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
31227      */
31228     cancelOnEsc : false,
31229     /**
31230      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
31231      */
31232     updateEl : false,
31233
31234     // private
31235     onRender : function(ct, position){
31236         this.el = new Roo.Layer({
31237             shadow: this.shadow,
31238             cls: "x-editor",
31239             parentEl : ct,
31240             shim : this.shim,
31241             shadowOffset:4,
31242             id: this.id,
31243             constrain: this.constrain
31244         });
31245         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
31246         if(this.field.msgTarget != 'title'){
31247             this.field.msgTarget = 'qtip';
31248         }
31249         this.field.render(this.el);
31250         if(Roo.isGecko){
31251             this.field.el.dom.setAttribute('autocomplete', 'off');
31252         }
31253         this.field.on("specialkey", this.onSpecialKey, this);
31254         if(this.swallowKeys){
31255             this.field.el.swallowEvent(['keydown','keypress']);
31256         }
31257         this.field.show();
31258         this.field.on("blur", this.onBlur, this);
31259         if(this.field.grow){
31260             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
31261         }
31262     },
31263
31264     onSpecialKey : function(field, e)
31265     {
31266         //Roo.log('editor onSpecialKey');
31267         if(this.completeOnEnter && e.getKey() == e.ENTER){
31268             e.stopEvent();
31269             this.completeEdit();
31270             return;
31271         }
31272         // do not fire special key otherwise it might hide close the editor...
31273         if(e.getKey() == e.ENTER){    
31274             return;
31275         }
31276         if(this.cancelOnEsc && e.getKey() == e.ESC){
31277             this.cancelEdit();
31278             return;
31279         } 
31280         this.fireEvent('specialkey', field, e);
31281     
31282     },
31283
31284     /**
31285      * Starts the editing process and shows the editor.
31286      * @param {String/HTMLElement/Element} el The element to edit
31287      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
31288       * to the innerHTML of el.
31289      */
31290     startEdit : function(el, value){
31291         if(this.editing){
31292             this.completeEdit();
31293         }
31294         this.boundEl = Roo.get(el);
31295         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
31296         if(!this.rendered){
31297             this.render(this.parentEl || document.body);
31298         }
31299         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
31300             return;
31301         }
31302         this.startValue = v;
31303         this.field.setValue(v);
31304         if(this.autoSize){
31305             var sz = this.boundEl.getSize();
31306             switch(this.autoSize){
31307                 case "width":
31308                 this.setSize(sz.width,  "");
31309                 break;
31310                 case "height":
31311                 this.setSize("",  sz.height);
31312                 break;
31313                 default:
31314                 this.setSize(sz.width,  sz.height);
31315             }
31316         }
31317         this.el.alignTo(this.boundEl, this.alignment);
31318         this.editing = true;
31319         if(Roo.QuickTips){
31320             Roo.QuickTips.disable();
31321         }
31322         this.show();
31323     },
31324
31325     /**
31326      * Sets the height and width of this editor.
31327      * @param {Number} width The new width
31328      * @param {Number} height The new height
31329      */
31330     setSize : function(w, h){
31331         this.field.setSize(w, h);
31332         if(this.el){
31333             this.el.sync();
31334         }
31335     },
31336
31337     /**
31338      * Realigns the editor to the bound field based on the current alignment config value.
31339      */
31340     realign : function(){
31341         this.el.alignTo(this.boundEl, this.alignment);
31342     },
31343
31344     /**
31345      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
31346      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
31347      */
31348     completeEdit : function(remainVisible){
31349         if(!this.editing){
31350             return;
31351         }
31352         var v = this.getValue();
31353         if(this.revertInvalid !== false && !this.field.isValid()){
31354             v = this.startValue;
31355             this.cancelEdit(true);
31356         }
31357         if(String(v) === String(this.startValue) && this.ignoreNoChange){
31358             this.editing = false;
31359             this.hide();
31360             return;
31361         }
31362         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
31363             this.editing = false;
31364             if(this.updateEl && this.boundEl){
31365                 this.boundEl.update(v);
31366             }
31367             if(remainVisible !== true){
31368                 this.hide();
31369             }
31370             this.fireEvent("complete", this, v, this.startValue);
31371         }
31372     },
31373
31374     // private
31375     onShow : function(){
31376         this.el.show();
31377         if(this.hideEl !== false){
31378             this.boundEl.hide();
31379         }
31380         this.field.show();
31381         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
31382             this.fixIEFocus = true;
31383             this.deferredFocus.defer(50, this);
31384         }else{
31385             this.field.focus();
31386         }
31387         this.fireEvent("startedit", this.boundEl, this.startValue);
31388     },
31389
31390     deferredFocus : function(){
31391         if(this.editing){
31392             this.field.focus();
31393         }
31394     },
31395
31396     /**
31397      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
31398      * reverted to the original starting value.
31399      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
31400      * cancel (defaults to false)
31401      */
31402     cancelEdit : function(remainVisible){
31403         if(this.editing){
31404             this.setValue(this.startValue);
31405             if(remainVisible !== true){
31406                 this.hide();
31407             }
31408         }
31409     },
31410
31411     // private
31412     onBlur : function(){
31413         if(this.allowBlur !== true && this.editing){
31414             this.completeEdit();
31415         }
31416     },
31417
31418     // private
31419     onHide : function(){
31420         if(this.editing){
31421             this.completeEdit();
31422             return;
31423         }
31424         this.field.blur();
31425         if(this.field.collapse){
31426             this.field.collapse();
31427         }
31428         this.el.hide();
31429         if(this.hideEl !== false){
31430             this.boundEl.show();
31431         }
31432         if(Roo.QuickTips){
31433             Roo.QuickTips.enable();
31434         }
31435     },
31436
31437     /**
31438      * Sets the data value of the editor
31439      * @param {Mixed} value Any valid value supported by the underlying field
31440      */
31441     setValue : function(v){
31442         this.field.setValue(v);
31443     },
31444
31445     /**
31446      * Gets the data value of the editor
31447      * @return {Mixed} The data value
31448      */
31449     getValue : function(){
31450         return this.field.getValue();
31451     }
31452 });/*
31453  * Based on:
31454  * Ext JS Library 1.1.1
31455  * Copyright(c) 2006-2007, Ext JS, LLC.
31456  *
31457  * Originally Released Under LGPL - original licence link has changed is not relivant.
31458  *
31459  * Fork - LGPL
31460  * <script type="text/javascript">
31461  */
31462  
31463 /**
31464  * @class Roo.BasicDialog
31465  * @extends Roo.util.Observable
31466  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
31467  * <pre><code>
31468 var dlg = new Roo.BasicDialog("my-dlg", {
31469     height: 200,
31470     width: 300,
31471     minHeight: 100,
31472     minWidth: 150,
31473     modal: true,
31474     proxyDrag: true,
31475     shadow: true
31476 });
31477 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
31478 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
31479 dlg.addButton('Cancel', dlg.hide, dlg);
31480 dlg.show();
31481 </code></pre>
31482   <b>A Dialog should always be a direct child of the body element.</b>
31483  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
31484  * @cfg {String} title Default text to display in the title bar (defaults to null)
31485  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31486  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31487  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
31488  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
31489  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
31490  * (defaults to null with no animation)
31491  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
31492  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
31493  * property for valid values (defaults to 'all')
31494  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
31495  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
31496  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
31497  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
31498  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
31499  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
31500  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
31501  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
31502  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
31503  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
31504  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
31505  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
31506  * draggable = true (defaults to false)
31507  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
31508  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31509  * shadow (defaults to false)
31510  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
31511  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
31512  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
31513  * @cfg {Array} buttons Array of buttons
31514  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
31515  * @constructor
31516  * Create a new BasicDialog.
31517  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
31518  * @param {Object} config Configuration options
31519  */
31520 Roo.BasicDialog = function(el, config){
31521     this.el = Roo.get(el);
31522     var dh = Roo.DomHelper;
31523     if(!this.el && config && config.autoCreate){
31524         if(typeof config.autoCreate == "object"){
31525             if(!config.autoCreate.id){
31526                 config.autoCreate.id = el;
31527             }
31528             this.el = dh.append(document.body,
31529                         config.autoCreate, true);
31530         }else{
31531             this.el = dh.append(document.body,
31532                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
31533         }
31534     }
31535     el = this.el;
31536     el.setDisplayed(true);
31537     el.hide = this.hideAction;
31538     this.id = el.id;
31539     el.addClass("x-dlg");
31540
31541     Roo.apply(this, config);
31542
31543     this.proxy = el.createProxy("x-dlg-proxy");
31544     this.proxy.hide = this.hideAction;
31545     this.proxy.setOpacity(.5);
31546     this.proxy.hide();
31547
31548     if(config.width){
31549         el.setWidth(config.width);
31550     }
31551     if(config.height){
31552         el.setHeight(config.height);
31553     }
31554     this.size = el.getSize();
31555     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
31556         this.xy = [config.x,config.y];
31557     }else{
31558         this.xy = el.getCenterXY(true);
31559     }
31560     /** The header element @type Roo.Element */
31561     this.header = el.child("> .x-dlg-hd");
31562     /** The body element @type Roo.Element */
31563     this.body = el.child("> .x-dlg-bd");
31564     /** The footer element @type Roo.Element */
31565     this.footer = el.child("> .x-dlg-ft");
31566
31567     if(!this.header){
31568         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
31569     }
31570     if(!this.body){
31571         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
31572     }
31573
31574     this.header.unselectable();
31575     if(this.title){
31576         this.header.update(this.title);
31577     }
31578     // this element allows the dialog to be focused for keyboard event
31579     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
31580     this.focusEl.swallowEvent("click", true);
31581
31582     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
31583
31584     // wrap the body and footer for special rendering
31585     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
31586     if(this.footer){
31587         this.bwrap.dom.appendChild(this.footer.dom);
31588     }
31589
31590     this.bg = this.el.createChild({
31591         tag: "div", cls:"x-dlg-bg",
31592         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
31593     });
31594     this.centerBg = this.bg.child("div.x-dlg-bg-center");
31595
31596
31597     if(this.autoScroll !== false && !this.autoTabs){
31598         this.body.setStyle("overflow", "auto");
31599     }
31600
31601     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
31602
31603     if(this.closable !== false){
31604         this.el.addClass("x-dlg-closable");
31605         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
31606         this.close.on("click", this.closeClick, this);
31607         this.close.addClassOnOver("x-dlg-close-over");
31608     }
31609     if(this.collapsible !== false){
31610         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
31611         this.collapseBtn.on("click", this.collapseClick, this);
31612         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
31613         this.header.on("dblclick", this.collapseClick, this);
31614     }
31615     if(this.resizable !== false){
31616         this.el.addClass("x-dlg-resizable");
31617         this.resizer = new Roo.Resizable(el, {
31618             minWidth: this.minWidth || 80,
31619             minHeight:this.minHeight || 80,
31620             handles: this.resizeHandles || "all",
31621             pinned: true
31622         });
31623         this.resizer.on("beforeresize", this.beforeResize, this);
31624         this.resizer.on("resize", this.onResize, this);
31625     }
31626     if(this.draggable !== false){
31627         el.addClass("x-dlg-draggable");
31628         if (!this.proxyDrag) {
31629             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
31630         }
31631         else {
31632             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
31633         }
31634         dd.setHandleElId(this.header.id);
31635         dd.endDrag = this.endMove.createDelegate(this);
31636         dd.startDrag = this.startMove.createDelegate(this);
31637         dd.onDrag = this.onDrag.createDelegate(this);
31638         dd.scroll = false;
31639         this.dd = dd;
31640     }
31641     if(this.modal){
31642         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
31643         this.mask.enableDisplayMode("block");
31644         this.mask.hide();
31645         this.el.addClass("x-dlg-modal");
31646     }
31647     if(this.shadow){
31648         this.shadow = new Roo.Shadow({
31649             mode : typeof this.shadow == "string" ? this.shadow : "sides",
31650             offset : this.shadowOffset
31651         });
31652     }else{
31653         this.shadowOffset = 0;
31654     }
31655     if(Roo.useShims && this.shim !== false){
31656         this.shim = this.el.createShim();
31657         this.shim.hide = this.hideAction;
31658         this.shim.hide();
31659     }else{
31660         this.shim = false;
31661     }
31662     if(this.autoTabs){
31663         this.initTabs();
31664     }
31665     if (this.buttons) { 
31666         var bts= this.buttons;
31667         this.buttons = [];
31668         Roo.each(bts, function(b) {
31669             this.addButton(b);
31670         }, this);
31671     }
31672     
31673     
31674     this.addEvents({
31675         /**
31676          * @event keydown
31677          * Fires when a key is pressed
31678          * @param {Roo.BasicDialog} this
31679          * @param {Roo.EventObject} e
31680          */
31681         "keydown" : true,
31682         /**
31683          * @event move
31684          * Fires when this dialog is moved by the user.
31685          * @param {Roo.BasicDialog} this
31686          * @param {Number} x The new page X
31687          * @param {Number} y The new page Y
31688          */
31689         "move" : true,
31690         /**
31691          * @event resize
31692          * Fires when this dialog is resized by the user.
31693          * @param {Roo.BasicDialog} this
31694          * @param {Number} width The new width
31695          * @param {Number} height The new height
31696          */
31697         "resize" : true,
31698         /**
31699          * @event beforehide
31700          * Fires before this dialog is hidden.
31701          * @param {Roo.BasicDialog} this
31702          */
31703         "beforehide" : true,
31704         /**
31705          * @event hide
31706          * Fires when this dialog is hidden.
31707          * @param {Roo.BasicDialog} this
31708          */
31709         "hide" : true,
31710         /**
31711          * @event beforeshow
31712          * Fires before this dialog is shown.
31713          * @param {Roo.BasicDialog} this
31714          */
31715         "beforeshow" : true,
31716         /**
31717          * @event show
31718          * Fires when this dialog is shown.
31719          * @param {Roo.BasicDialog} this
31720          */
31721         "show" : true
31722     });
31723     el.on("keydown", this.onKeyDown, this);
31724     el.on("mousedown", this.toFront, this);
31725     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
31726     this.el.hide();
31727     Roo.DialogManager.register(this);
31728     Roo.BasicDialog.superclass.constructor.call(this);
31729 };
31730
31731 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
31732     shadowOffset: Roo.isIE ? 6 : 5,
31733     minHeight: 80,
31734     minWidth: 200,
31735     minButtonWidth: 75,
31736     defaultButton: null,
31737     buttonAlign: "right",
31738     tabTag: 'div',
31739     firstShow: true,
31740
31741     /**
31742      * Sets the dialog title text
31743      * @param {String} text The title text to display
31744      * @return {Roo.BasicDialog} this
31745      */
31746     setTitle : function(text){
31747         this.header.update(text);
31748         return this;
31749     },
31750
31751     // private
31752     closeClick : function(){
31753         this.hide();
31754     },
31755
31756     // private
31757     collapseClick : function(){
31758         this[this.collapsed ? "expand" : "collapse"]();
31759     },
31760
31761     /**
31762      * Collapses the dialog to its minimized state (only the title bar is visible).
31763      * Equivalent to the user clicking the collapse dialog button.
31764      */
31765     collapse : function(){
31766         if(!this.collapsed){
31767             this.collapsed = true;
31768             this.el.addClass("x-dlg-collapsed");
31769             this.restoreHeight = this.el.getHeight();
31770             this.resizeTo(this.el.getWidth(), this.header.getHeight());
31771         }
31772     },
31773
31774     /**
31775      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
31776      * clicking the expand dialog button.
31777      */
31778     expand : function(){
31779         if(this.collapsed){
31780             this.collapsed = false;
31781             this.el.removeClass("x-dlg-collapsed");
31782             this.resizeTo(this.el.getWidth(), this.restoreHeight);
31783         }
31784     },
31785
31786     /**
31787      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
31788      * @return {Roo.TabPanel} The tabs component
31789      */
31790     initTabs : function(){
31791         var tabs = this.getTabs();
31792         while(tabs.getTab(0)){
31793             tabs.removeTab(0);
31794         }
31795         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
31796             var dom = el.dom;
31797             tabs.addTab(Roo.id(dom), dom.title);
31798             dom.title = "";
31799         });
31800         tabs.activate(0);
31801         return tabs;
31802     },
31803
31804     // private
31805     beforeResize : function(){
31806         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
31807     },
31808
31809     // private
31810     onResize : function(){
31811         this.refreshSize();
31812         this.syncBodyHeight();
31813         this.adjustAssets();
31814         this.focus();
31815         this.fireEvent("resize", this, this.size.width, this.size.height);
31816     },
31817
31818     // private
31819     onKeyDown : function(e){
31820         if(this.isVisible()){
31821             this.fireEvent("keydown", this, e);
31822         }
31823     },
31824
31825     /**
31826      * Resizes the dialog.
31827      * @param {Number} width
31828      * @param {Number} height
31829      * @return {Roo.BasicDialog} this
31830      */
31831     resizeTo : function(width, height){
31832         this.el.setSize(width, height);
31833         this.size = {width: width, height: height};
31834         this.syncBodyHeight();
31835         if(this.fixedcenter){
31836             this.center();
31837         }
31838         if(this.isVisible()){
31839             this.constrainXY();
31840             this.adjustAssets();
31841         }
31842         this.fireEvent("resize", this, width, height);
31843         return this;
31844     },
31845
31846
31847     /**
31848      * Resizes the dialog to fit the specified content size.
31849      * @param {Number} width
31850      * @param {Number} height
31851      * @return {Roo.BasicDialog} this
31852      */
31853     setContentSize : function(w, h){
31854         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
31855         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
31856         //if(!this.el.isBorderBox()){
31857             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
31858             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
31859         //}
31860         if(this.tabs){
31861             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
31862             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
31863         }
31864         this.resizeTo(w, h);
31865         return this;
31866     },
31867
31868     /**
31869      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
31870      * executed in response to a particular key being pressed while the dialog is active.
31871      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
31872      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
31873      * @param {Function} fn The function to call
31874      * @param {Object} scope (optional) The scope of the function
31875      * @return {Roo.BasicDialog} this
31876      */
31877     addKeyListener : function(key, fn, scope){
31878         var keyCode, shift, ctrl, alt;
31879         if(typeof key == "object" && !(key instanceof Array)){
31880             keyCode = key["key"];
31881             shift = key["shift"];
31882             ctrl = key["ctrl"];
31883             alt = key["alt"];
31884         }else{
31885             keyCode = key;
31886         }
31887         var handler = function(dlg, e){
31888             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
31889                 var k = e.getKey();
31890                 if(keyCode instanceof Array){
31891                     for(var i = 0, len = keyCode.length; i < len; i++){
31892                         if(keyCode[i] == k){
31893                           fn.call(scope || window, dlg, k, e);
31894                           return;
31895                         }
31896                     }
31897                 }else{
31898                     if(k == keyCode){
31899                         fn.call(scope || window, dlg, k, e);
31900                     }
31901                 }
31902             }
31903         };
31904         this.on("keydown", handler);
31905         return this;
31906     },
31907
31908     /**
31909      * Returns the TabPanel component (creates it if it doesn't exist).
31910      * Note: If you wish to simply check for the existence of tabs without creating them,
31911      * check for a null 'tabs' property.
31912      * @return {Roo.TabPanel} The tabs component
31913      */
31914     getTabs : function(){
31915         if(!this.tabs){
31916             this.el.addClass("x-dlg-auto-tabs");
31917             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
31918             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
31919         }
31920         return this.tabs;
31921     },
31922
31923     /**
31924      * Adds a button to the footer section of the dialog.
31925      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
31926      * object or a valid Roo.DomHelper element config
31927      * @param {Function} handler The function called when the button is clicked
31928      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
31929      * @return {Roo.Button} The new button
31930      */
31931     addButton : function(config, handler, scope){
31932         var dh = Roo.DomHelper;
31933         if(!this.footer){
31934             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
31935         }
31936         if(!this.btnContainer){
31937             var tb = this.footer.createChild({
31938
31939                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
31940                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
31941             }, null, true);
31942             this.btnContainer = tb.firstChild.firstChild.firstChild;
31943         }
31944         var bconfig = {
31945             handler: handler,
31946             scope: scope,
31947             minWidth: this.minButtonWidth,
31948             hideParent:true
31949         };
31950         if(typeof config == "string"){
31951             bconfig.text = config;
31952         }else{
31953             if(config.tag){
31954                 bconfig.dhconfig = config;
31955             }else{
31956                 Roo.apply(bconfig, config);
31957             }
31958         }
31959         var fc = false;
31960         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
31961             bconfig.position = Math.max(0, bconfig.position);
31962             fc = this.btnContainer.childNodes[bconfig.position];
31963         }
31964          
31965         var btn = new Roo.Button(
31966             fc ? 
31967                 this.btnContainer.insertBefore(document.createElement("td"),fc)
31968                 : this.btnContainer.appendChild(document.createElement("td")),
31969             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
31970             bconfig
31971         );
31972         this.syncBodyHeight();
31973         if(!this.buttons){
31974             /**
31975              * Array of all the buttons that have been added to this dialog via addButton
31976              * @type Array
31977              */
31978             this.buttons = [];
31979         }
31980         this.buttons.push(btn);
31981         return btn;
31982     },
31983
31984     /**
31985      * Sets the default button to be focused when the dialog is displayed.
31986      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
31987      * @return {Roo.BasicDialog} this
31988      */
31989     setDefaultButton : function(btn){
31990         this.defaultButton = btn;
31991         return this;
31992     },
31993
31994     // private
31995     getHeaderFooterHeight : function(safe){
31996         var height = 0;
31997         if(this.header){
31998            height += this.header.getHeight();
31999         }
32000         if(this.footer){
32001            var fm = this.footer.getMargins();
32002             height += (this.footer.getHeight()+fm.top+fm.bottom);
32003         }
32004         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
32005         height += this.centerBg.getPadding("tb");
32006         return height;
32007     },
32008
32009     // private
32010     syncBodyHeight : function()
32011     {
32012         var bd = this.body, // the text
32013             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
32014             bw = this.bwrap;
32015         var height = this.size.height - this.getHeaderFooterHeight(false);
32016         bd.setHeight(height-bd.getMargins("tb"));
32017         var hh = this.header.getHeight();
32018         var h = this.size.height-hh;
32019         cb.setHeight(h);
32020         
32021         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
32022         bw.setHeight(h-cb.getPadding("tb"));
32023         
32024         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
32025         bd.setWidth(bw.getWidth(true));
32026         if(this.tabs){
32027             this.tabs.syncHeight();
32028             if(Roo.isIE){
32029                 this.tabs.el.repaint();
32030             }
32031         }
32032     },
32033
32034     /**
32035      * Restores the previous state of the dialog if Roo.state is configured.
32036      * @return {Roo.BasicDialog} this
32037      */
32038     restoreState : function(){
32039         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
32040         if(box && box.width){
32041             this.xy = [box.x, box.y];
32042             this.resizeTo(box.width, box.height);
32043         }
32044         return this;
32045     },
32046
32047     // private
32048     beforeShow : function(){
32049         this.expand();
32050         if(this.fixedcenter){
32051             this.xy = this.el.getCenterXY(true);
32052         }
32053         if(this.modal){
32054             Roo.get(document.body).addClass("x-body-masked");
32055             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32056             this.mask.show();
32057         }
32058         this.constrainXY();
32059     },
32060
32061     // private
32062     animShow : function(){
32063         var b = Roo.get(this.animateTarget).getBox();
32064         this.proxy.setSize(b.width, b.height);
32065         this.proxy.setLocation(b.x, b.y);
32066         this.proxy.show();
32067         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
32068                     true, .35, this.showEl.createDelegate(this));
32069     },
32070
32071     /**
32072      * Shows the dialog.
32073      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
32074      * @return {Roo.BasicDialog} this
32075      */
32076     show : function(animateTarget){
32077         if (this.fireEvent("beforeshow", this) === false){
32078             return;
32079         }
32080         if(this.syncHeightBeforeShow){
32081             this.syncBodyHeight();
32082         }else if(this.firstShow){
32083             this.firstShow = false;
32084             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
32085         }
32086         this.animateTarget = animateTarget || this.animateTarget;
32087         if(!this.el.isVisible()){
32088             this.beforeShow();
32089             if(this.animateTarget && Roo.get(this.animateTarget)){
32090                 this.animShow();
32091             }else{
32092                 this.showEl();
32093             }
32094         }
32095         return this;
32096     },
32097
32098     // private
32099     showEl : function(){
32100         this.proxy.hide();
32101         this.el.setXY(this.xy);
32102         this.el.show();
32103         this.adjustAssets(true);
32104         this.toFront();
32105         this.focus();
32106         // IE peekaboo bug - fix found by Dave Fenwick
32107         if(Roo.isIE){
32108             this.el.repaint();
32109         }
32110         this.fireEvent("show", this);
32111     },
32112
32113     /**
32114      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
32115      * dialog itself will receive focus.
32116      */
32117     focus : function(){
32118         if(this.defaultButton){
32119             this.defaultButton.focus();
32120         }else{
32121             this.focusEl.focus();
32122         }
32123     },
32124
32125     // private
32126     constrainXY : function(){
32127         if(this.constraintoviewport !== false){
32128             if(!this.viewSize){
32129                 if(this.container){
32130                     var s = this.container.getSize();
32131                     this.viewSize = [s.width, s.height];
32132                 }else{
32133                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
32134                 }
32135             }
32136             var s = Roo.get(this.container||document).getScroll();
32137
32138             var x = this.xy[0], y = this.xy[1];
32139             var w = this.size.width, h = this.size.height;
32140             var vw = this.viewSize[0], vh = this.viewSize[1];
32141             // only move it if it needs it
32142             var moved = false;
32143             // first validate right/bottom
32144             if(x + w > vw+s.left){
32145                 x = vw - w;
32146                 moved = true;
32147             }
32148             if(y + h > vh+s.top){
32149                 y = vh - h;
32150                 moved = true;
32151             }
32152             // then make sure top/left isn't negative
32153             if(x < s.left){
32154                 x = s.left;
32155                 moved = true;
32156             }
32157             if(y < s.top){
32158                 y = s.top;
32159                 moved = true;
32160             }
32161             if(moved){
32162                 // cache xy
32163                 this.xy = [x, y];
32164                 if(this.isVisible()){
32165                     this.el.setLocation(x, y);
32166                     this.adjustAssets();
32167                 }
32168             }
32169         }
32170     },
32171
32172     // private
32173     onDrag : function(){
32174         if(!this.proxyDrag){
32175             this.xy = this.el.getXY();
32176             this.adjustAssets();
32177         }
32178     },
32179
32180     // private
32181     adjustAssets : function(doShow){
32182         var x = this.xy[0], y = this.xy[1];
32183         var w = this.size.width, h = this.size.height;
32184         if(doShow === true){
32185             if(this.shadow){
32186                 this.shadow.show(this.el);
32187             }
32188             if(this.shim){
32189                 this.shim.show();
32190             }
32191         }
32192         if(this.shadow && this.shadow.isVisible()){
32193             this.shadow.show(this.el);
32194         }
32195         if(this.shim && this.shim.isVisible()){
32196             this.shim.setBounds(x, y, w, h);
32197         }
32198     },
32199
32200     // private
32201     adjustViewport : function(w, h){
32202         if(!w || !h){
32203             w = Roo.lib.Dom.getViewWidth();
32204             h = Roo.lib.Dom.getViewHeight();
32205         }
32206         // cache the size
32207         this.viewSize = [w, h];
32208         if(this.modal && this.mask.isVisible()){
32209             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
32210             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32211         }
32212         if(this.isVisible()){
32213             this.constrainXY();
32214         }
32215     },
32216
32217     /**
32218      * Destroys this dialog and all its supporting elements (including any tabs, shim,
32219      * shadow, proxy, mask, etc.)  Also removes all event listeners.
32220      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32221      */
32222     destroy : function(removeEl){
32223         if(this.isVisible()){
32224             this.animateTarget = null;
32225             this.hide();
32226         }
32227         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
32228         if(this.tabs){
32229             this.tabs.destroy(removeEl);
32230         }
32231         Roo.destroy(
32232              this.shim,
32233              this.proxy,
32234              this.resizer,
32235              this.close,
32236              this.mask
32237         );
32238         if(this.dd){
32239             this.dd.unreg();
32240         }
32241         if(this.buttons){
32242            for(var i = 0, len = this.buttons.length; i < len; i++){
32243                this.buttons[i].destroy();
32244            }
32245         }
32246         this.el.removeAllListeners();
32247         if(removeEl === true){
32248             this.el.update("");
32249             this.el.remove();
32250         }
32251         Roo.DialogManager.unregister(this);
32252     },
32253
32254     // private
32255     startMove : function(){
32256         if(this.proxyDrag){
32257             this.proxy.show();
32258         }
32259         if(this.constraintoviewport !== false){
32260             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
32261         }
32262     },
32263
32264     // private
32265     endMove : function(){
32266         if(!this.proxyDrag){
32267             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
32268         }else{
32269             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
32270             this.proxy.hide();
32271         }
32272         this.refreshSize();
32273         this.adjustAssets();
32274         this.focus();
32275         this.fireEvent("move", this, this.xy[0], this.xy[1]);
32276     },
32277
32278     /**
32279      * Brings this dialog to the front of any other visible dialogs
32280      * @return {Roo.BasicDialog} this
32281      */
32282     toFront : function(){
32283         Roo.DialogManager.bringToFront(this);
32284         return this;
32285     },
32286
32287     /**
32288      * Sends this dialog to the back (under) of any other visible dialogs
32289      * @return {Roo.BasicDialog} this
32290      */
32291     toBack : function(){
32292         Roo.DialogManager.sendToBack(this);
32293         return this;
32294     },
32295
32296     /**
32297      * Centers this dialog in the viewport
32298      * @return {Roo.BasicDialog} this
32299      */
32300     center : function(){
32301         var xy = this.el.getCenterXY(true);
32302         this.moveTo(xy[0], xy[1]);
32303         return this;
32304     },
32305
32306     /**
32307      * Moves the dialog's top-left corner to the specified point
32308      * @param {Number} x
32309      * @param {Number} y
32310      * @return {Roo.BasicDialog} this
32311      */
32312     moveTo : function(x, y){
32313         this.xy = [x,y];
32314         if(this.isVisible()){
32315             this.el.setXY(this.xy);
32316             this.adjustAssets();
32317         }
32318         return this;
32319     },
32320
32321     /**
32322      * Aligns the dialog to the specified element
32323      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32324      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
32325      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32326      * @return {Roo.BasicDialog} this
32327      */
32328     alignTo : function(element, position, offsets){
32329         this.xy = this.el.getAlignToXY(element, position, offsets);
32330         if(this.isVisible()){
32331             this.el.setXY(this.xy);
32332             this.adjustAssets();
32333         }
32334         return this;
32335     },
32336
32337     /**
32338      * Anchors an element to another element and realigns it when the window is resized.
32339      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32340      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
32341      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32342      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
32343      * is a number, it is used as the buffer delay (defaults to 50ms).
32344      * @return {Roo.BasicDialog} this
32345      */
32346     anchorTo : function(el, alignment, offsets, monitorScroll){
32347         var action = function(){
32348             this.alignTo(el, alignment, offsets);
32349         };
32350         Roo.EventManager.onWindowResize(action, this);
32351         var tm = typeof monitorScroll;
32352         if(tm != 'undefined'){
32353             Roo.EventManager.on(window, 'scroll', action, this,
32354                 {buffer: tm == 'number' ? monitorScroll : 50});
32355         }
32356         action.call(this);
32357         return this;
32358     },
32359
32360     /**
32361      * Returns true if the dialog is visible
32362      * @return {Boolean}
32363      */
32364     isVisible : function(){
32365         return this.el.isVisible();
32366     },
32367
32368     // private
32369     animHide : function(callback){
32370         var b = Roo.get(this.animateTarget).getBox();
32371         this.proxy.show();
32372         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
32373         this.el.hide();
32374         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
32375                     this.hideEl.createDelegate(this, [callback]));
32376     },
32377
32378     /**
32379      * Hides the dialog.
32380      * @param {Function} callback (optional) Function to call when the dialog is hidden
32381      * @return {Roo.BasicDialog} this
32382      */
32383     hide : function(callback){
32384         if (this.fireEvent("beforehide", this) === false){
32385             return;
32386         }
32387         if(this.shadow){
32388             this.shadow.hide();
32389         }
32390         if(this.shim) {
32391           this.shim.hide();
32392         }
32393         // sometimes animateTarget seems to get set.. causing problems...
32394         // this just double checks..
32395         if(this.animateTarget && Roo.get(this.animateTarget)) {
32396            this.animHide(callback);
32397         }else{
32398             this.el.hide();
32399             this.hideEl(callback);
32400         }
32401         return this;
32402     },
32403
32404     // private
32405     hideEl : function(callback){
32406         this.proxy.hide();
32407         if(this.modal){
32408             this.mask.hide();
32409             Roo.get(document.body).removeClass("x-body-masked");
32410         }
32411         this.fireEvent("hide", this);
32412         if(typeof callback == "function"){
32413             callback();
32414         }
32415     },
32416
32417     // private
32418     hideAction : function(){
32419         this.setLeft("-10000px");
32420         this.setTop("-10000px");
32421         this.setStyle("visibility", "hidden");
32422     },
32423
32424     // private
32425     refreshSize : function(){
32426         this.size = this.el.getSize();
32427         this.xy = this.el.getXY();
32428         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
32429     },
32430
32431     // private
32432     // z-index is managed by the DialogManager and may be overwritten at any time
32433     setZIndex : function(index){
32434         if(this.modal){
32435             this.mask.setStyle("z-index", index);
32436         }
32437         if(this.shim){
32438             this.shim.setStyle("z-index", ++index);
32439         }
32440         if(this.shadow){
32441             this.shadow.setZIndex(++index);
32442         }
32443         this.el.setStyle("z-index", ++index);
32444         if(this.proxy){
32445             this.proxy.setStyle("z-index", ++index);
32446         }
32447         if(this.resizer){
32448             this.resizer.proxy.setStyle("z-index", ++index);
32449         }
32450
32451         this.lastZIndex = index;
32452     },
32453
32454     /**
32455      * Returns the element for this dialog
32456      * @return {Roo.Element} The underlying dialog Element
32457      */
32458     getEl : function(){
32459         return this.el;
32460     }
32461 });
32462
32463 /**
32464  * @class Roo.DialogManager
32465  * Provides global access to BasicDialogs that have been created and
32466  * support for z-indexing (layering) multiple open dialogs.
32467  */
32468 Roo.DialogManager = function(){
32469     var list = {};
32470     var accessList = [];
32471     var front = null;
32472
32473     // private
32474     var sortDialogs = function(d1, d2){
32475         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
32476     };
32477
32478     // private
32479     var orderDialogs = function(){
32480         accessList.sort(sortDialogs);
32481         var seed = Roo.DialogManager.zseed;
32482         for(var i = 0, len = accessList.length; i < len; i++){
32483             var dlg = accessList[i];
32484             if(dlg){
32485                 dlg.setZIndex(seed + (i*10));
32486             }
32487         }
32488     };
32489
32490     return {
32491         /**
32492          * The starting z-index for BasicDialogs (defaults to 9000)
32493          * @type Number The z-index value
32494          */
32495         zseed : 9000,
32496
32497         // private
32498         register : function(dlg){
32499             list[dlg.id] = dlg;
32500             accessList.push(dlg);
32501         },
32502
32503         // private
32504         unregister : function(dlg){
32505             delete list[dlg.id];
32506             var i=0;
32507             var len=0;
32508             if(!accessList.indexOf){
32509                 for(  i = 0, len = accessList.length; i < len; i++){
32510                     if(accessList[i] == dlg){
32511                         accessList.splice(i, 1);
32512                         return;
32513                     }
32514                 }
32515             }else{
32516                  i = accessList.indexOf(dlg);
32517                 if(i != -1){
32518                     accessList.splice(i, 1);
32519                 }
32520             }
32521         },
32522
32523         /**
32524          * Gets a registered dialog by id
32525          * @param {String/Object} id The id of the dialog or a dialog
32526          * @return {Roo.BasicDialog} this
32527          */
32528         get : function(id){
32529             return typeof id == "object" ? id : list[id];
32530         },
32531
32532         /**
32533          * Brings the specified dialog to the front
32534          * @param {String/Object} dlg The id of the dialog or a dialog
32535          * @return {Roo.BasicDialog} this
32536          */
32537         bringToFront : function(dlg){
32538             dlg = this.get(dlg);
32539             if(dlg != front){
32540                 front = dlg;
32541                 dlg._lastAccess = new Date().getTime();
32542                 orderDialogs();
32543             }
32544             return dlg;
32545         },
32546
32547         /**
32548          * Sends the specified dialog to the back
32549          * @param {String/Object} dlg The id of the dialog or a dialog
32550          * @return {Roo.BasicDialog} this
32551          */
32552         sendToBack : function(dlg){
32553             dlg = this.get(dlg);
32554             dlg._lastAccess = -(new Date().getTime());
32555             orderDialogs();
32556             return dlg;
32557         },
32558
32559         /**
32560          * Hides all dialogs
32561          */
32562         hideAll : function(){
32563             for(var id in list){
32564                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
32565                     list[id].hide();
32566                 }
32567             }
32568         }
32569     };
32570 }();
32571
32572 /**
32573  * @class Roo.LayoutDialog
32574  * @extends Roo.BasicDialog
32575  * Dialog which provides adjustments for working with a layout in a Dialog.
32576  * Add your necessary layout config options to the dialog's config.<br>
32577  * Example usage (including a nested layout):
32578  * <pre><code>
32579 if(!dialog){
32580     dialog = new Roo.LayoutDialog("download-dlg", {
32581         modal: true,
32582         width:600,
32583         height:450,
32584         shadow:true,
32585         minWidth:500,
32586         minHeight:350,
32587         autoTabs:true,
32588         proxyDrag:true,
32589         // layout config merges with the dialog config
32590         center:{
32591             tabPosition: "top",
32592             alwaysShowTabs: true
32593         }
32594     });
32595     dialog.addKeyListener(27, dialog.hide, dialog);
32596     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
32597     dialog.addButton("Build It!", this.getDownload, this);
32598
32599     // we can even add nested layouts
32600     var innerLayout = new Roo.BorderLayout("dl-inner", {
32601         east: {
32602             initialSize: 200,
32603             autoScroll:true,
32604             split:true
32605         },
32606         center: {
32607             autoScroll:true
32608         }
32609     });
32610     innerLayout.beginUpdate();
32611     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
32612     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
32613     innerLayout.endUpdate(true);
32614
32615     var layout = dialog.getLayout();
32616     layout.beginUpdate();
32617     layout.add("center", new Roo.ContentPanel("standard-panel",
32618                         {title: "Download the Source", fitToFrame:true}));
32619     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
32620                {title: "Build your own roo.js"}));
32621     layout.getRegion("center").showPanel(sp);
32622     layout.endUpdate();
32623 }
32624 </code></pre>
32625     * @constructor
32626     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
32627     * @param {Object} config configuration options
32628   */
32629 Roo.LayoutDialog = function(el, cfg){
32630     
32631     var config=  cfg;
32632     if (typeof(cfg) == 'undefined') {
32633         config = Roo.apply({}, el);
32634         // not sure why we use documentElement here.. - it should always be body.
32635         // IE7 borks horribly if we use documentElement.
32636         // webkit also does not like documentElement - it creates a body element...
32637         el = Roo.get( document.body || document.documentElement ).createChild();
32638         //config.autoCreate = true;
32639     }
32640     
32641     
32642     config.autoTabs = false;
32643     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
32644     this.body.setStyle({overflow:"hidden", position:"relative"});
32645     this.layout = new Roo.BorderLayout(this.body.dom, config);
32646     this.layout.monitorWindowResize = false;
32647     this.el.addClass("x-dlg-auto-layout");
32648     // fix case when center region overwrites center function
32649     this.center = Roo.BasicDialog.prototype.center;
32650     this.on("show", this.layout.layout, this.layout, true);
32651     if (config.items) {
32652         var xitems = config.items;
32653         delete config.items;
32654         Roo.each(xitems, this.addxtype, this);
32655     }
32656     
32657     
32658 };
32659 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
32660     /**
32661      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
32662      * @deprecated
32663      */
32664     endUpdate : function(){
32665         this.layout.endUpdate();
32666     },
32667
32668     /**
32669      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
32670      *  @deprecated
32671      */
32672     beginUpdate : function(){
32673         this.layout.beginUpdate();
32674     },
32675
32676     /**
32677      * Get the BorderLayout for this dialog
32678      * @return {Roo.BorderLayout}
32679      */
32680     getLayout : function(){
32681         return this.layout;
32682     },
32683
32684     showEl : function(){
32685         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
32686         if(Roo.isIE7){
32687             this.layout.layout();
32688         }
32689     },
32690
32691     // private
32692     // Use the syncHeightBeforeShow config option to control this automatically
32693     syncBodyHeight : function(){
32694         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
32695         if(this.layout){this.layout.layout();}
32696     },
32697     
32698       /**
32699      * Add an xtype element (actually adds to the layout.)
32700      * @return {Object} xdata xtype object data.
32701      */
32702     
32703     addxtype : function(c) {
32704         return this.layout.addxtype(c);
32705     }
32706 });/*
32707  * Based on:
32708  * Ext JS Library 1.1.1
32709  * Copyright(c) 2006-2007, Ext JS, LLC.
32710  *
32711  * Originally Released Under LGPL - original licence link has changed is not relivant.
32712  *
32713  * Fork - LGPL
32714  * <script type="text/javascript">
32715  */
32716  
32717 /**
32718  * @class Roo.MessageBox
32719  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
32720  * Example usage:
32721  *<pre><code>
32722 // Basic alert:
32723 Roo.Msg.alert('Status', 'Changes saved successfully.');
32724
32725 // Prompt for user data:
32726 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
32727     if (btn == 'ok'){
32728         // process text value...
32729     }
32730 });
32731
32732 // Show a dialog using config options:
32733 Roo.Msg.show({
32734    title:'Save Changes?',
32735    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
32736    buttons: Roo.Msg.YESNOCANCEL,
32737    fn: processResult,
32738    animEl: 'elId'
32739 });
32740 </code></pre>
32741  * @singleton
32742  */
32743 Roo.MessageBox = function(){
32744     var dlg, opt, mask, waitTimer;
32745     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
32746     var buttons, activeTextEl, bwidth;
32747
32748     // private
32749     var handleButton = function(button){
32750         dlg.hide();
32751         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
32752     };
32753
32754     // private
32755     var handleHide = function(){
32756         if(opt && opt.cls){
32757             dlg.el.removeClass(opt.cls);
32758         }
32759         if(waitTimer){
32760             Roo.TaskMgr.stop(waitTimer);
32761             waitTimer = null;
32762         }
32763     };
32764
32765     // private
32766     var updateButtons = function(b){
32767         var width = 0;
32768         if(!b){
32769             buttons["ok"].hide();
32770             buttons["cancel"].hide();
32771             buttons["yes"].hide();
32772             buttons["no"].hide();
32773             dlg.footer.dom.style.display = 'none';
32774             return width;
32775         }
32776         dlg.footer.dom.style.display = '';
32777         for(var k in buttons){
32778             if(typeof buttons[k] != "function"){
32779                 if(b[k]){
32780                     buttons[k].show();
32781                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
32782                     width += buttons[k].el.getWidth()+15;
32783                 }else{
32784                     buttons[k].hide();
32785                 }
32786             }
32787         }
32788         return width;
32789     };
32790
32791     // private
32792     var handleEsc = function(d, k, e){
32793         if(opt && opt.closable !== false){
32794             dlg.hide();
32795         }
32796         if(e){
32797             e.stopEvent();
32798         }
32799     };
32800
32801     return {
32802         /**
32803          * Returns a reference to the underlying {@link Roo.BasicDialog} element
32804          * @return {Roo.BasicDialog} The BasicDialog element
32805          */
32806         getDialog : function(){
32807            if(!dlg){
32808                 dlg = new Roo.BasicDialog("x-msg-box", {
32809                     autoCreate : true,
32810                     shadow: true,
32811                     draggable: true,
32812                     resizable:false,
32813                     constraintoviewport:false,
32814                     fixedcenter:true,
32815                     collapsible : false,
32816                     shim:true,
32817                     modal: true,
32818                     width:400, height:100,
32819                     buttonAlign:"center",
32820                     closeClick : function(){
32821                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
32822                             handleButton("no");
32823                         }else{
32824                             handleButton("cancel");
32825                         }
32826                     }
32827                 });
32828                 dlg.on("hide", handleHide);
32829                 mask = dlg.mask;
32830                 dlg.addKeyListener(27, handleEsc);
32831                 buttons = {};
32832                 var bt = this.buttonText;
32833                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
32834                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
32835                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
32836                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
32837                 bodyEl = dlg.body.createChild({
32838
32839                     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>'
32840                 });
32841                 msgEl = bodyEl.dom.firstChild;
32842                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
32843                 textboxEl.enableDisplayMode();
32844                 textboxEl.addKeyListener([10,13], function(){
32845                     if(dlg.isVisible() && opt && opt.buttons){
32846                         if(opt.buttons.ok){
32847                             handleButton("ok");
32848                         }else if(opt.buttons.yes){
32849                             handleButton("yes");
32850                         }
32851                     }
32852                 });
32853                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
32854                 textareaEl.enableDisplayMode();
32855                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
32856                 progressEl.enableDisplayMode();
32857                 var pf = progressEl.dom.firstChild;
32858                 if (pf) {
32859                     pp = Roo.get(pf.firstChild);
32860                     pp.setHeight(pf.offsetHeight);
32861                 }
32862                 
32863             }
32864             return dlg;
32865         },
32866
32867         /**
32868          * Updates the message box body text
32869          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
32870          * the XHTML-compliant non-breaking space character '&amp;#160;')
32871          * @return {Roo.MessageBox} This message box
32872          */
32873         updateText : function(text){
32874             if(!dlg.isVisible() && !opt.width){
32875                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
32876             }
32877             msgEl.innerHTML = text || '&#160;';
32878       
32879             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
32880             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
32881             var w = Math.max(
32882                     Math.min(opt.width || cw , this.maxWidth), 
32883                     Math.max(opt.minWidth || this.minWidth, bwidth)
32884             );
32885             if(opt.prompt){
32886                 activeTextEl.setWidth(w);
32887             }
32888             if(dlg.isVisible()){
32889                 dlg.fixedcenter = false;
32890             }
32891             // to big, make it scroll. = But as usual stupid IE does not support
32892             // !important..
32893             
32894             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
32895                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
32896                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
32897             } else {
32898                 bodyEl.dom.style.height = '';
32899                 bodyEl.dom.style.overflowY = '';
32900             }
32901             if (cw > w) {
32902                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
32903             } else {
32904                 bodyEl.dom.style.overflowX = '';
32905             }
32906             
32907             dlg.setContentSize(w, bodyEl.getHeight());
32908             if(dlg.isVisible()){
32909                 dlg.fixedcenter = true;
32910             }
32911             return this;
32912         },
32913
32914         /**
32915          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
32916          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
32917          * @param {Number} value Any number between 0 and 1 (e.g., .5)
32918          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
32919          * @return {Roo.MessageBox} This message box
32920          */
32921         updateProgress : function(value, text){
32922             if(text){
32923                 this.updateText(text);
32924             }
32925             if (pp) { // weird bug on my firefox - for some reason this is not defined
32926                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
32927             }
32928             return this;
32929         },        
32930
32931         /**
32932          * Returns true if the message box is currently displayed
32933          * @return {Boolean} True if the message box is visible, else false
32934          */
32935         isVisible : function(){
32936             return dlg && dlg.isVisible();  
32937         },
32938
32939         /**
32940          * Hides the message box if it is displayed
32941          */
32942         hide : function(){
32943             if(this.isVisible()){
32944                 dlg.hide();
32945             }  
32946         },
32947
32948         /**
32949          * Displays a new message box, or reinitializes an existing message box, based on the config options
32950          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
32951          * The following config object properties are supported:
32952          * <pre>
32953 Property    Type             Description
32954 ----------  ---------------  ------------------------------------------------------------------------------------
32955 animEl            String/Element   An id or Element from which the message box should animate as it opens and
32956                                    closes (defaults to undefined)
32957 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
32958                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
32959 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
32960                                    progress and wait dialogs will ignore this property and always hide the
32961                                    close button as they can only be closed programmatically.
32962 cls               String           A custom CSS class to apply to the message box element
32963 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
32964                                    displayed (defaults to 75)
32965 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
32966                                    function will be btn (the name of the button that was clicked, if applicable,
32967                                    e.g. "ok"), and text (the value of the active text field, if applicable).
32968                                    Progress and wait dialogs will ignore this option since they do not respond to
32969                                    user actions and can only be closed programmatically, so any required function
32970                                    should be called by the same code after it closes the dialog.
32971 icon              String           A CSS class that provides a background image to be used as an icon for
32972                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
32973 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
32974 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
32975 modal             Boolean          False to allow user interaction with the page while the message box is
32976                                    displayed (defaults to true)
32977 msg               String           A string that will replace the existing message box body text (defaults
32978                                    to the XHTML-compliant non-breaking space character '&#160;')
32979 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
32980 progress          Boolean          True to display a progress bar (defaults to false)
32981 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
32982 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
32983 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
32984 title             String           The title text
32985 value             String           The string value to set into the active textbox element if displayed
32986 wait              Boolean          True to display a progress bar (defaults to false)
32987 width             Number           The width of the dialog in pixels
32988 </pre>
32989          *
32990          * Example usage:
32991          * <pre><code>
32992 Roo.Msg.show({
32993    title: 'Address',
32994    msg: 'Please enter your address:',
32995    width: 300,
32996    buttons: Roo.MessageBox.OKCANCEL,
32997    multiline: true,
32998    fn: saveAddress,
32999    animEl: 'addAddressBtn'
33000 });
33001 </code></pre>
33002          * @param {Object} config Configuration options
33003          * @return {Roo.MessageBox} This message box
33004          */
33005         show : function(options)
33006         {
33007             
33008             // this causes nightmares if you show one dialog after another
33009             // especially on callbacks..
33010              
33011             if(this.isVisible()){
33012                 
33013                 this.hide();
33014                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
33015                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
33016                 Roo.log("New Dialog Message:" +  options.msg )
33017                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
33018                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
33019                 
33020             }
33021             var d = this.getDialog();
33022             opt = options;
33023             d.setTitle(opt.title || "&#160;");
33024             d.close.setDisplayed(opt.closable !== false);
33025             activeTextEl = textboxEl;
33026             opt.prompt = opt.prompt || (opt.multiline ? true : false);
33027             if(opt.prompt){
33028                 if(opt.multiline){
33029                     textboxEl.hide();
33030                     textareaEl.show();
33031                     textareaEl.setHeight(typeof opt.multiline == "number" ?
33032                         opt.multiline : this.defaultTextHeight);
33033                     activeTextEl = textareaEl;
33034                 }else{
33035                     textboxEl.show();
33036                     textareaEl.hide();
33037                 }
33038             }else{
33039                 textboxEl.hide();
33040                 textareaEl.hide();
33041             }
33042             progressEl.setDisplayed(opt.progress === true);
33043             this.updateProgress(0);
33044             activeTextEl.dom.value = opt.value || "";
33045             if(opt.prompt){
33046                 dlg.setDefaultButton(activeTextEl);
33047             }else{
33048                 var bs = opt.buttons;
33049                 var db = null;
33050                 if(bs && bs.ok){
33051                     db = buttons["ok"];
33052                 }else if(bs && bs.yes){
33053                     db = buttons["yes"];
33054                 }
33055                 dlg.setDefaultButton(db);
33056             }
33057             bwidth = updateButtons(opt.buttons);
33058             this.updateText(opt.msg);
33059             if(opt.cls){
33060                 d.el.addClass(opt.cls);
33061             }
33062             d.proxyDrag = opt.proxyDrag === true;
33063             d.modal = opt.modal !== false;
33064             d.mask = opt.modal !== false ? mask : false;
33065             if(!d.isVisible()){
33066                 // force it to the end of the z-index stack so it gets a cursor in FF
33067                 document.body.appendChild(dlg.el.dom);
33068                 d.animateTarget = null;
33069                 d.show(options.animEl);
33070             }
33071             return this;
33072         },
33073
33074         /**
33075          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
33076          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
33077          * and closing the message box when the process is complete.
33078          * @param {String} title The title bar text
33079          * @param {String} msg The message box body text
33080          * @return {Roo.MessageBox} This message box
33081          */
33082         progress : function(title, msg){
33083             this.show({
33084                 title : title,
33085                 msg : msg,
33086                 buttons: false,
33087                 progress:true,
33088                 closable:false,
33089                 minWidth: this.minProgressWidth,
33090                 modal : true
33091             });
33092             return this;
33093         },
33094
33095         /**
33096          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
33097          * If a callback function is passed it will be called after the user clicks the button, and the
33098          * id of the button that was clicked will be passed as the only parameter to the callback
33099          * (could also be the top-right close button).
33100          * @param {String} title The title bar text
33101          * @param {String} msg The message box body text
33102          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33103          * @param {Object} scope (optional) The scope of the callback function
33104          * @return {Roo.MessageBox} This message box
33105          */
33106         alert : function(title, msg, fn, scope){
33107             this.show({
33108                 title : title,
33109                 msg : msg,
33110                 buttons: this.OK,
33111                 fn: fn,
33112                 scope : scope,
33113                 modal : true
33114             });
33115             return this;
33116         },
33117
33118         /**
33119          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
33120          * interaction while waiting for a long-running process to complete that does not have defined intervals.
33121          * You are responsible for closing the message box when the process is complete.
33122          * @param {String} msg The message box body text
33123          * @param {String} title (optional) The title bar text
33124          * @return {Roo.MessageBox} This message box
33125          */
33126         wait : function(msg, title){
33127             this.show({
33128                 title : title,
33129                 msg : msg,
33130                 buttons: false,
33131                 closable:false,
33132                 progress:true,
33133                 modal:true,
33134                 width:300,
33135                 wait:true
33136             });
33137             waitTimer = Roo.TaskMgr.start({
33138                 run: function(i){
33139                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
33140                 },
33141                 interval: 1000
33142             });
33143             return this;
33144         },
33145
33146         /**
33147          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
33148          * If a callback function is passed it will be called after the user clicks either button, and the id of the
33149          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
33150          * @param {String} title The title bar text
33151          * @param {String} msg The message box body text
33152          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33153          * @param {Object} scope (optional) The scope of the callback function
33154          * @return {Roo.MessageBox} This message box
33155          */
33156         confirm : function(title, msg, fn, scope){
33157             this.show({
33158                 title : title,
33159                 msg : msg,
33160                 buttons: this.YESNO,
33161                 fn: fn,
33162                 scope : scope,
33163                 modal : true
33164             });
33165             return this;
33166         },
33167
33168         /**
33169          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
33170          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
33171          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
33172          * (could also be the top-right close button) and the text that was entered will be passed as the two
33173          * parameters to the callback.
33174          * @param {String} title The title bar text
33175          * @param {String} msg The message box body text
33176          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33177          * @param {Object} scope (optional) The scope of the callback function
33178          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
33179          * property, or the height in pixels to create the textbox (defaults to false / single-line)
33180          * @return {Roo.MessageBox} This message box
33181          */
33182         prompt : function(title, msg, fn, scope, multiline){
33183             this.show({
33184                 title : title,
33185                 msg : msg,
33186                 buttons: this.OKCANCEL,
33187                 fn: fn,
33188                 minWidth:250,
33189                 scope : scope,
33190                 prompt:true,
33191                 multiline: multiline,
33192                 modal : true
33193             });
33194             return this;
33195         },
33196
33197         /**
33198          * Button config that displays a single OK button
33199          * @type Object
33200          */
33201         OK : {ok:true},
33202         /**
33203          * Button config that displays Yes and No buttons
33204          * @type Object
33205          */
33206         YESNO : {yes:true, no:true},
33207         /**
33208          * Button config that displays OK and Cancel buttons
33209          * @type Object
33210          */
33211         OKCANCEL : {ok:true, cancel:true},
33212         /**
33213          * Button config that displays Yes, No and Cancel buttons
33214          * @type Object
33215          */
33216         YESNOCANCEL : {yes:true, no:true, cancel:true},
33217
33218         /**
33219          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
33220          * @type Number
33221          */
33222         defaultTextHeight : 75,
33223         /**
33224          * The maximum width in pixels of the message box (defaults to 600)
33225          * @type Number
33226          */
33227         maxWidth : 600,
33228         /**
33229          * The minimum width in pixels of the message box (defaults to 100)
33230          * @type Number
33231          */
33232         minWidth : 100,
33233         /**
33234          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
33235          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
33236          * @type Number
33237          */
33238         minProgressWidth : 250,
33239         /**
33240          * An object containing the default button text strings that can be overriden for localized language support.
33241          * Supported properties are: ok, cancel, yes and no.
33242          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
33243          * @type Object
33244          */
33245         buttonText : {
33246             ok : "OK",
33247             cancel : "Cancel",
33248             yes : "Yes",
33249             no : "No"
33250         }
33251     };
33252 }();
33253
33254 /**
33255  * Shorthand for {@link Roo.MessageBox}
33256  */
33257 Roo.Msg = Roo.MessageBox;/*
33258  * Based on:
33259  * Ext JS Library 1.1.1
33260  * Copyright(c) 2006-2007, Ext JS, LLC.
33261  *
33262  * Originally Released Under LGPL - original licence link has changed is not relivant.
33263  *
33264  * Fork - LGPL
33265  * <script type="text/javascript">
33266  */
33267 /**
33268  * @class Roo.QuickTips
33269  * Provides attractive and customizable tooltips for any element.
33270  * @singleton
33271  */
33272 Roo.QuickTips = function(){
33273     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
33274     var ce, bd, xy, dd;
33275     var visible = false, disabled = true, inited = false;
33276     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
33277     
33278     var onOver = function(e){
33279         if(disabled){
33280             return;
33281         }
33282         var t = e.getTarget();
33283         if(!t || t.nodeType !== 1 || t == document || t == document.body){
33284             return;
33285         }
33286         if(ce && t == ce.el){
33287             clearTimeout(hideProc);
33288             return;
33289         }
33290         if(t && tagEls[t.id]){
33291             tagEls[t.id].el = t;
33292             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
33293             return;
33294         }
33295         var ttp, et = Roo.fly(t);
33296         var ns = cfg.namespace;
33297         if(tm.interceptTitles && t.title){
33298             ttp = t.title;
33299             t.qtip = ttp;
33300             t.removeAttribute("title");
33301             e.preventDefault();
33302         }else{
33303             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
33304         }
33305         if(ttp){
33306             showProc = show.defer(tm.showDelay, tm, [{
33307                 el: t, 
33308                 text: ttp.replace(/\\n/g,'<br/>'),
33309                 width: et.getAttributeNS(ns, cfg.width),
33310                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
33311                 title: et.getAttributeNS(ns, cfg.title),
33312                     cls: et.getAttributeNS(ns, cfg.cls)
33313             }]);
33314         }
33315     };
33316     
33317     var onOut = function(e){
33318         clearTimeout(showProc);
33319         var t = e.getTarget();
33320         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
33321             hideProc = setTimeout(hide, tm.hideDelay);
33322         }
33323     };
33324     
33325     var onMove = function(e){
33326         if(disabled){
33327             return;
33328         }
33329         xy = e.getXY();
33330         xy[1] += 18;
33331         if(tm.trackMouse && ce){
33332             el.setXY(xy);
33333         }
33334     };
33335     
33336     var onDown = function(e){
33337         clearTimeout(showProc);
33338         clearTimeout(hideProc);
33339         if(!e.within(el)){
33340             if(tm.hideOnClick){
33341                 hide();
33342                 tm.disable();
33343                 tm.enable.defer(100, tm);
33344             }
33345         }
33346     };
33347     
33348     var getPad = function(){
33349         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
33350     };
33351
33352     var show = function(o){
33353         if(disabled){
33354             return;
33355         }
33356         clearTimeout(dismissProc);
33357         ce = o;
33358         if(removeCls){ // in case manually hidden
33359             el.removeClass(removeCls);
33360             removeCls = null;
33361         }
33362         if(ce.cls){
33363             el.addClass(ce.cls);
33364             removeCls = ce.cls;
33365         }
33366         if(ce.title){
33367             tipTitle.update(ce.title);
33368             tipTitle.show();
33369         }else{
33370             tipTitle.update('');
33371             tipTitle.hide();
33372         }
33373         el.dom.style.width  = tm.maxWidth+'px';
33374         //tipBody.dom.style.width = '';
33375         tipBodyText.update(o.text);
33376         var p = getPad(), w = ce.width;
33377         if(!w){
33378             var td = tipBodyText.dom;
33379             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
33380             if(aw > tm.maxWidth){
33381                 w = tm.maxWidth;
33382             }else if(aw < tm.minWidth){
33383                 w = tm.minWidth;
33384             }else{
33385                 w = aw;
33386             }
33387         }
33388         //tipBody.setWidth(w);
33389         el.setWidth(parseInt(w, 10) + p);
33390         if(ce.autoHide === false){
33391             close.setDisplayed(true);
33392             if(dd){
33393                 dd.unlock();
33394             }
33395         }else{
33396             close.setDisplayed(false);
33397             if(dd){
33398                 dd.lock();
33399             }
33400         }
33401         if(xy){
33402             el.avoidY = xy[1]-18;
33403             el.setXY(xy);
33404         }
33405         if(tm.animate){
33406             el.setOpacity(.1);
33407             el.setStyle("visibility", "visible");
33408             el.fadeIn({callback: afterShow});
33409         }else{
33410             afterShow();
33411         }
33412     };
33413     
33414     var afterShow = function(){
33415         if(ce){
33416             el.show();
33417             esc.enable();
33418             if(tm.autoDismiss && ce.autoHide !== false){
33419                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
33420             }
33421         }
33422     };
33423     
33424     var hide = function(noanim){
33425         clearTimeout(dismissProc);
33426         clearTimeout(hideProc);
33427         ce = null;
33428         if(el.isVisible()){
33429             esc.disable();
33430             if(noanim !== true && tm.animate){
33431                 el.fadeOut({callback: afterHide});
33432             }else{
33433                 afterHide();
33434             } 
33435         }
33436     };
33437     
33438     var afterHide = function(){
33439         el.hide();
33440         if(removeCls){
33441             el.removeClass(removeCls);
33442             removeCls = null;
33443         }
33444     };
33445     
33446     return {
33447         /**
33448         * @cfg {Number} minWidth
33449         * The minimum width of the quick tip (defaults to 40)
33450         */
33451        minWidth : 40,
33452         /**
33453         * @cfg {Number} maxWidth
33454         * The maximum width of the quick tip (defaults to 300)
33455         */
33456        maxWidth : 300,
33457         /**
33458         * @cfg {Boolean} interceptTitles
33459         * True to automatically use the element's DOM title value if available (defaults to false)
33460         */
33461        interceptTitles : false,
33462         /**
33463         * @cfg {Boolean} trackMouse
33464         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
33465         */
33466        trackMouse : false,
33467         /**
33468         * @cfg {Boolean} hideOnClick
33469         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
33470         */
33471        hideOnClick : true,
33472         /**
33473         * @cfg {Number} showDelay
33474         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
33475         */
33476        showDelay : 500,
33477         /**
33478         * @cfg {Number} hideDelay
33479         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
33480         */
33481        hideDelay : 200,
33482         /**
33483         * @cfg {Boolean} autoHide
33484         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
33485         * Used in conjunction with hideDelay.
33486         */
33487        autoHide : true,
33488         /**
33489         * @cfg {Boolean}
33490         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
33491         * (defaults to true).  Used in conjunction with autoDismissDelay.
33492         */
33493        autoDismiss : true,
33494         /**
33495         * @cfg {Number}
33496         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
33497         */
33498        autoDismissDelay : 5000,
33499        /**
33500         * @cfg {Boolean} animate
33501         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
33502         */
33503        animate : false,
33504
33505        /**
33506         * @cfg {String} title
33507         * Title text to display (defaults to '').  This can be any valid HTML markup.
33508         */
33509         title: '',
33510        /**
33511         * @cfg {String} text
33512         * Body text to display (defaults to '').  This can be any valid HTML markup.
33513         */
33514         text : '',
33515        /**
33516         * @cfg {String} cls
33517         * A CSS class to apply to the base quick tip element (defaults to '').
33518         */
33519         cls : '',
33520        /**
33521         * @cfg {Number} width
33522         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
33523         * minWidth or maxWidth.
33524         */
33525         width : null,
33526
33527     /**
33528      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
33529      * or display QuickTips in a page.
33530      */
33531        init : function(){
33532           tm = Roo.QuickTips;
33533           cfg = tm.tagConfig;
33534           if(!inited){
33535               if(!Roo.isReady){ // allow calling of init() before onReady
33536                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
33537                   return;
33538               }
33539               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
33540               el.fxDefaults = {stopFx: true};
33541               // maximum custom styling
33542               //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>');
33543               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>');              
33544               tipTitle = el.child('h3');
33545               tipTitle.enableDisplayMode("block");
33546               tipBody = el.child('div.x-tip-bd');
33547               tipBodyText = el.child('div.x-tip-bd-inner');
33548               //bdLeft = el.child('div.x-tip-bd-left');
33549               //bdRight = el.child('div.x-tip-bd-right');
33550               close = el.child('div.x-tip-close');
33551               close.enableDisplayMode("block");
33552               close.on("click", hide);
33553               var d = Roo.get(document);
33554               d.on("mousedown", onDown);
33555               d.on("mouseover", onOver);
33556               d.on("mouseout", onOut);
33557               d.on("mousemove", onMove);
33558               esc = d.addKeyListener(27, hide);
33559               esc.disable();
33560               if(Roo.dd.DD){
33561                   dd = el.initDD("default", null, {
33562                       onDrag : function(){
33563                           el.sync();  
33564                       }
33565                   });
33566                   dd.setHandleElId(tipTitle.id);
33567                   dd.lock();
33568               }
33569               inited = true;
33570           }
33571           this.enable(); 
33572        },
33573
33574     /**
33575      * Configures a new quick tip instance and assigns it to a target element.  The following config options
33576      * are supported:
33577      * <pre>
33578 Property    Type                   Description
33579 ----------  ---------------------  ------------------------------------------------------------------------
33580 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
33581      * </ul>
33582      * @param {Object} config The config object
33583      */
33584        register : function(config){
33585            var cs = config instanceof Array ? config : arguments;
33586            for(var i = 0, len = cs.length; i < len; i++) {
33587                var c = cs[i];
33588                var target = c.target;
33589                if(target){
33590                    if(target instanceof Array){
33591                        for(var j = 0, jlen = target.length; j < jlen; j++){
33592                            tagEls[target[j]] = c;
33593                        }
33594                    }else{
33595                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
33596                    }
33597                }
33598            }
33599        },
33600
33601     /**
33602      * Removes this quick tip from its element and destroys it.
33603      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
33604      */
33605        unregister : function(el){
33606            delete tagEls[Roo.id(el)];
33607        },
33608
33609     /**
33610      * Enable this quick tip.
33611      */
33612        enable : function(){
33613            if(inited && disabled){
33614                locks.pop();
33615                if(locks.length < 1){
33616                    disabled = false;
33617                }
33618            }
33619        },
33620
33621     /**
33622      * Disable this quick tip.
33623      */
33624        disable : function(){
33625           disabled = true;
33626           clearTimeout(showProc);
33627           clearTimeout(hideProc);
33628           clearTimeout(dismissProc);
33629           if(ce){
33630               hide(true);
33631           }
33632           locks.push(1);
33633        },
33634
33635     /**
33636      * Returns true if the quick tip is enabled, else false.
33637      */
33638        isEnabled : function(){
33639             return !disabled;
33640        },
33641
33642         // private
33643        tagConfig : {
33644            namespace : "roo", // was ext?? this may break..
33645            alt_namespace : "ext",
33646            attribute : "qtip",
33647            width : "width",
33648            target : "target",
33649            title : "qtitle",
33650            hide : "hide",
33651            cls : "qclass"
33652        }
33653    };
33654 }();
33655
33656 // backwards compat
33657 Roo.QuickTips.tips = Roo.QuickTips.register;/*
33658  * Based on:
33659  * Ext JS Library 1.1.1
33660  * Copyright(c) 2006-2007, Ext JS, LLC.
33661  *
33662  * Originally Released Under LGPL - original licence link has changed is not relivant.
33663  *
33664  * Fork - LGPL
33665  * <script type="text/javascript">
33666  */
33667  
33668
33669 /**
33670  * @class Roo.tree.TreePanel
33671  * @extends Roo.data.Tree
33672
33673  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
33674  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
33675  * @cfg {Boolean} enableDD true to enable drag and drop
33676  * @cfg {Boolean} enableDrag true to enable just drag
33677  * @cfg {Boolean} enableDrop true to enable just drop
33678  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
33679  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
33680  * @cfg {String} ddGroup The DD group this TreePanel belongs to
33681  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
33682  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
33683  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
33684  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
33685  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
33686  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
33687  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
33688  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
33689  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
33690  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
33691  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
33692  * @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>
33693  * @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>
33694  * 
33695  * @constructor
33696  * @param {String/HTMLElement/Element} el The container element
33697  * @param {Object} config
33698  */
33699 Roo.tree.TreePanel = function(el, config){
33700     var root = false;
33701     var loader = false;
33702     if (config.root) {
33703         root = config.root;
33704         delete config.root;
33705     }
33706     if (config.loader) {
33707         loader = config.loader;
33708         delete config.loader;
33709     }
33710     
33711     Roo.apply(this, config);
33712     Roo.tree.TreePanel.superclass.constructor.call(this);
33713     this.el = Roo.get(el);
33714     this.el.addClass('x-tree');
33715     //console.log(root);
33716     if (root) {
33717         this.setRootNode( Roo.factory(root, Roo.tree));
33718     }
33719     if (loader) {
33720         this.loader = Roo.factory(loader, Roo.tree);
33721     }
33722    /**
33723     * Read-only. The id of the container element becomes this TreePanel's id.
33724     */
33725     this.id = this.el.id;
33726     this.addEvents({
33727         /**
33728         * @event beforeload
33729         * Fires before a node is loaded, return false to cancel
33730         * @param {Node} node The node being loaded
33731         */
33732         "beforeload" : true,
33733         /**
33734         * @event load
33735         * Fires when a node is loaded
33736         * @param {Node} node The node that was loaded
33737         */
33738         "load" : true,
33739         /**
33740         * @event textchange
33741         * Fires when the text for a node is changed
33742         * @param {Node} node The node
33743         * @param {String} text The new text
33744         * @param {String} oldText The old text
33745         */
33746         "textchange" : true,
33747         /**
33748         * @event beforeexpand
33749         * Fires before a node is expanded, return false to cancel.
33750         * @param {Node} node The node
33751         * @param {Boolean} deep
33752         * @param {Boolean} anim
33753         */
33754         "beforeexpand" : true,
33755         /**
33756         * @event beforecollapse
33757         * Fires before a node is collapsed, return false to cancel.
33758         * @param {Node} node The node
33759         * @param {Boolean} deep
33760         * @param {Boolean} anim
33761         */
33762         "beforecollapse" : true,
33763         /**
33764         * @event expand
33765         * Fires when a node is expanded
33766         * @param {Node} node The node
33767         */
33768         "expand" : true,
33769         /**
33770         * @event disabledchange
33771         * Fires when the disabled status of a node changes
33772         * @param {Node} node The node
33773         * @param {Boolean} disabled
33774         */
33775         "disabledchange" : true,
33776         /**
33777         * @event collapse
33778         * Fires when a node is collapsed
33779         * @param {Node} node The node
33780         */
33781         "collapse" : true,
33782         /**
33783         * @event beforeclick
33784         * Fires before click processing on a node. Return false to cancel the default action.
33785         * @param {Node} node The node
33786         * @param {Roo.EventObject} e The event object
33787         */
33788         "beforeclick":true,
33789         /**
33790         * @event checkchange
33791         * Fires when a node with a checkbox's checked property changes
33792         * @param {Node} this This node
33793         * @param {Boolean} checked
33794         */
33795         "checkchange":true,
33796         /**
33797         * @event click
33798         * Fires when a node is clicked
33799         * @param {Node} node The node
33800         * @param {Roo.EventObject} e The event object
33801         */
33802         "click":true,
33803         /**
33804         * @event dblclick
33805         * Fires when a node is double clicked
33806         * @param {Node} node The node
33807         * @param {Roo.EventObject} e The event object
33808         */
33809         "dblclick":true,
33810         /**
33811         * @event contextmenu
33812         * Fires when a node is right clicked
33813         * @param {Node} node The node
33814         * @param {Roo.EventObject} e The event object
33815         */
33816         "contextmenu":true,
33817         /**
33818         * @event beforechildrenrendered
33819         * Fires right before the child nodes for a node are rendered
33820         * @param {Node} node The node
33821         */
33822         "beforechildrenrendered":true,
33823         /**
33824         * @event startdrag
33825         * Fires when a node starts being dragged
33826         * @param {Roo.tree.TreePanel} this
33827         * @param {Roo.tree.TreeNode} node
33828         * @param {event} e The raw browser event
33829         */ 
33830        "startdrag" : true,
33831        /**
33832         * @event enddrag
33833         * Fires when a drag operation is complete
33834         * @param {Roo.tree.TreePanel} this
33835         * @param {Roo.tree.TreeNode} node
33836         * @param {event} e The raw browser event
33837         */
33838        "enddrag" : true,
33839        /**
33840         * @event dragdrop
33841         * Fires when a dragged node is dropped on a valid DD target
33842         * @param {Roo.tree.TreePanel} this
33843         * @param {Roo.tree.TreeNode} node
33844         * @param {DD} dd The dd it was dropped on
33845         * @param {event} e The raw browser event
33846         */
33847        "dragdrop" : true,
33848        /**
33849         * @event beforenodedrop
33850         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
33851         * passed to handlers has the following properties:<br />
33852         * <ul style="padding:5px;padding-left:16px;">
33853         * <li>tree - The TreePanel</li>
33854         * <li>target - The node being targeted for the drop</li>
33855         * <li>data - The drag data from the drag source</li>
33856         * <li>point - The point of the drop - append, above or below</li>
33857         * <li>source - The drag source</li>
33858         * <li>rawEvent - Raw mouse event</li>
33859         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
33860         * to be inserted by setting them on this object.</li>
33861         * <li>cancel - Set this to true to cancel the drop.</li>
33862         * </ul>
33863         * @param {Object} dropEvent
33864         */
33865        "beforenodedrop" : true,
33866        /**
33867         * @event nodedrop
33868         * Fires after a DD object is dropped on a node in this tree. The dropEvent
33869         * passed to handlers has the following properties:<br />
33870         * <ul style="padding:5px;padding-left:16px;">
33871         * <li>tree - The TreePanel</li>
33872         * <li>target - The node being targeted for the drop</li>
33873         * <li>data - The drag data from the drag source</li>
33874         * <li>point - The point of the drop - append, above or below</li>
33875         * <li>source - The drag source</li>
33876         * <li>rawEvent - Raw mouse event</li>
33877         * <li>dropNode - Dropped node(s).</li>
33878         * </ul>
33879         * @param {Object} dropEvent
33880         */
33881        "nodedrop" : true,
33882         /**
33883         * @event nodedragover
33884         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
33885         * passed to handlers has the following properties:<br />
33886         * <ul style="padding:5px;padding-left:16px;">
33887         * <li>tree - The TreePanel</li>
33888         * <li>target - The node being targeted for the drop</li>
33889         * <li>data - The drag data from the drag source</li>
33890         * <li>point - The point of the drop - append, above or below</li>
33891         * <li>source - The drag source</li>
33892         * <li>rawEvent - Raw mouse event</li>
33893         * <li>dropNode - Drop node(s) provided by the source.</li>
33894         * <li>cancel - Set this to true to signal drop not allowed.</li>
33895         * </ul>
33896         * @param {Object} dragOverEvent
33897         */
33898        "nodedragover" : true,
33899        /**
33900         * @event appendnode
33901         * Fires when append node to the tree
33902         * @param {Roo.tree.TreePanel} this
33903         * @param {Roo.tree.TreeNode} node
33904         * @param {Number} index The index of the newly appended node
33905         */
33906        "appendnode" : true
33907         
33908     });
33909     if(this.singleExpand){
33910        this.on("beforeexpand", this.restrictExpand, this);
33911     }
33912     if (this.editor) {
33913         this.editor.tree = this;
33914         this.editor = Roo.factory(this.editor, Roo.tree);
33915     }
33916     
33917     if (this.selModel) {
33918         this.selModel = Roo.factory(this.selModel, Roo.tree);
33919     }
33920    
33921 };
33922 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
33923     rootVisible : true,
33924     animate: Roo.enableFx,
33925     lines : true,
33926     enableDD : false,
33927     hlDrop : Roo.enableFx,
33928   
33929     renderer: false,
33930     
33931     rendererTip: false,
33932     // private
33933     restrictExpand : function(node){
33934         var p = node.parentNode;
33935         if(p){
33936             if(p.expandedChild && p.expandedChild.parentNode == p){
33937                 p.expandedChild.collapse();
33938             }
33939             p.expandedChild = node;
33940         }
33941     },
33942
33943     // private override
33944     setRootNode : function(node){
33945         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
33946         if(!this.rootVisible){
33947             node.ui = new Roo.tree.RootTreeNodeUI(node);
33948         }
33949         return node;
33950     },
33951
33952     /**
33953      * Returns the container element for this TreePanel
33954      */
33955     getEl : function(){
33956         return this.el;
33957     },
33958
33959     /**
33960      * Returns the default TreeLoader for this TreePanel
33961      */
33962     getLoader : function(){
33963         return this.loader;
33964     },
33965
33966     /**
33967      * Expand all nodes
33968      */
33969     expandAll : function(){
33970         this.root.expand(true);
33971     },
33972
33973     /**
33974      * Collapse all nodes
33975      */
33976     collapseAll : function(){
33977         this.root.collapse(true);
33978     },
33979
33980     /**
33981      * Returns the selection model used by this TreePanel
33982      */
33983     getSelectionModel : function(){
33984         if(!this.selModel){
33985             this.selModel = new Roo.tree.DefaultSelectionModel();
33986         }
33987         return this.selModel;
33988     },
33989
33990     /**
33991      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
33992      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
33993      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
33994      * @return {Array}
33995      */
33996     getChecked : function(a, startNode){
33997         startNode = startNode || this.root;
33998         var r = [];
33999         var f = function(){
34000             if(this.attributes.checked){
34001                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
34002             }
34003         }
34004         startNode.cascade(f);
34005         return r;
34006     },
34007
34008     /**
34009      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34010      * @param {String} path
34011      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34012      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
34013      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
34014      */
34015     expandPath : function(path, attr, callback){
34016         attr = attr || "id";
34017         var keys = path.split(this.pathSeparator);
34018         var curNode = this.root;
34019         if(curNode.attributes[attr] != keys[1]){ // invalid root
34020             if(callback){
34021                 callback(false, null);
34022             }
34023             return;
34024         }
34025         var index = 1;
34026         var f = function(){
34027             if(++index == keys.length){
34028                 if(callback){
34029                     callback(true, curNode);
34030                 }
34031                 return;
34032             }
34033             var c = curNode.findChild(attr, keys[index]);
34034             if(!c){
34035                 if(callback){
34036                     callback(false, curNode);
34037                 }
34038                 return;
34039             }
34040             curNode = c;
34041             c.expand(false, false, f);
34042         };
34043         curNode.expand(false, false, f);
34044     },
34045
34046     /**
34047      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34048      * @param {String} path
34049      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34050      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
34051      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
34052      */
34053     selectPath : function(path, attr, callback){
34054         attr = attr || "id";
34055         var keys = path.split(this.pathSeparator);
34056         var v = keys.pop();
34057         if(keys.length > 0){
34058             var f = function(success, node){
34059                 if(success && node){
34060                     var n = node.findChild(attr, v);
34061                     if(n){
34062                         n.select();
34063                         if(callback){
34064                             callback(true, n);
34065                         }
34066                     }else if(callback){
34067                         callback(false, n);
34068                     }
34069                 }else{
34070                     if(callback){
34071                         callback(false, n);
34072                     }
34073                 }
34074             };
34075             this.expandPath(keys.join(this.pathSeparator), attr, f);
34076         }else{
34077             this.root.select();
34078             if(callback){
34079                 callback(true, this.root);
34080             }
34081         }
34082     },
34083
34084     getTreeEl : function(){
34085         return this.el;
34086     },
34087
34088     /**
34089      * Trigger rendering of this TreePanel
34090      */
34091     render : function(){
34092         if (this.innerCt) {
34093             return this; // stop it rendering more than once!!
34094         }
34095         
34096         this.innerCt = this.el.createChild({tag:"ul",
34097                cls:"x-tree-root-ct " +
34098                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
34099
34100         if(this.containerScroll){
34101             Roo.dd.ScrollManager.register(this.el);
34102         }
34103         if((this.enableDD || this.enableDrop) && !this.dropZone){
34104            /**
34105             * The dropZone used by this tree if drop is enabled
34106             * @type Roo.tree.TreeDropZone
34107             */
34108              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
34109                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
34110            });
34111         }
34112         if((this.enableDD || this.enableDrag) && !this.dragZone){
34113            /**
34114             * The dragZone used by this tree if drag is enabled
34115             * @type Roo.tree.TreeDragZone
34116             */
34117             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
34118                ddGroup: this.ddGroup || "TreeDD",
34119                scroll: this.ddScroll
34120            });
34121         }
34122         this.getSelectionModel().init(this);
34123         if (!this.root) {
34124             Roo.log("ROOT not set in tree");
34125             return this;
34126         }
34127         this.root.render();
34128         if(!this.rootVisible){
34129             this.root.renderChildren();
34130         }
34131         return this;
34132     }
34133 });/*
34134  * Based on:
34135  * Ext JS Library 1.1.1
34136  * Copyright(c) 2006-2007, Ext JS, LLC.
34137  *
34138  * Originally Released Under LGPL - original licence link has changed is not relivant.
34139  *
34140  * Fork - LGPL
34141  * <script type="text/javascript">
34142  */
34143  
34144
34145 /**
34146  * @class Roo.tree.DefaultSelectionModel
34147  * @extends Roo.util.Observable
34148  * The default single selection for a TreePanel.
34149  * @param {Object} cfg Configuration
34150  */
34151 Roo.tree.DefaultSelectionModel = function(cfg){
34152    this.selNode = null;
34153    
34154    
34155    
34156    this.addEvents({
34157        /**
34158         * @event selectionchange
34159         * Fires when the selected node changes
34160         * @param {DefaultSelectionModel} this
34161         * @param {TreeNode} node the new selection
34162         */
34163        "selectionchange" : true,
34164
34165        /**
34166         * @event beforeselect
34167         * Fires before the selected node changes, return false to cancel the change
34168         * @param {DefaultSelectionModel} this
34169         * @param {TreeNode} node the new selection
34170         * @param {TreeNode} node the old selection
34171         */
34172        "beforeselect" : true
34173    });
34174    
34175     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
34176 };
34177
34178 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
34179     init : function(tree){
34180         this.tree = tree;
34181         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34182         tree.on("click", this.onNodeClick, this);
34183     },
34184     
34185     onNodeClick : function(node, e){
34186         if (e.ctrlKey && this.selNode == node)  {
34187             this.unselect(node);
34188             return;
34189         }
34190         this.select(node);
34191     },
34192     
34193     /**
34194      * Select a node.
34195      * @param {TreeNode} node The node to select
34196      * @return {TreeNode} The selected node
34197      */
34198     select : function(node){
34199         var last = this.selNode;
34200         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
34201             if(last){
34202                 last.ui.onSelectedChange(false);
34203             }
34204             this.selNode = node;
34205             node.ui.onSelectedChange(true);
34206             this.fireEvent("selectionchange", this, node, last);
34207         }
34208         return node;
34209     },
34210     
34211     /**
34212      * Deselect a node.
34213      * @param {TreeNode} node The node to unselect
34214      */
34215     unselect : function(node){
34216         if(this.selNode == node){
34217             this.clearSelections();
34218         }    
34219     },
34220     
34221     /**
34222      * Clear all selections
34223      */
34224     clearSelections : function(){
34225         var n = this.selNode;
34226         if(n){
34227             n.ui.onSelectedChange(false);
34228             this.selNode = null;
34229             this.fireEvent("selectionchange", this, null);
34230         }
34231         return n;
34232     },
34233     
34234     /**
34235      * Get the selected node
34236      * @return {TreeNode} The selected node
34237      */
34238     getSelectedNode : function(){
34239         return this.selNode;    
34240     },
34241     
34242     /**
34243      * Returns true if the node is selected
34244      * @param {TreeNode} node The node to check
34245      * @return {Boolean}
34246      */
34247     isSelected : function(node){
34248         return this.selNode == node;  
34249     },
34250
34251     /**
34252      * Selects the node above the selected node in the tree, intelligently walking the nodes
34253      * @return TreeNode The new selection
34254      */
34255     selectPrevious : function(){
34256         var s = this.selNode || this.lastSelNode;
34257         if(!s){
34258             return null;
34259         }
34260         var ps = s.previousSibling;
34261         if(ps){
34262             if(!ps.isExpanded() || ps.childNodes.length < 1){
34263                 return this.select(ps);
34264             } else{
34265                 var lc = ps.lastChild;
34266                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
34267                     lc = lc.lastChild;
34268                 }
34269                 return this.select(lc);
34270             }
34271         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
34272             return this.select(s.parentNode);
34273         }
34274         return null;
34275     },
34276
34277     /**
34278      * Selects the node above the selected node in the tree, intelligently walking the nodes
34279      * @return TreeNode The new selection
34280      */
34281     selectNext : function(){
34282         var s = this.selNode || this.lastSelNode;
34283         if(!s){
34284             return null;
34285         }
34286         if(s.firstChild && s.isExpanded()){
34287              return this.select(s.firstChild);
34288          }else if(s.nextSibling){
34289              return this.select(s.nextSibling);
34290          }else if(s.parentNode){
34291             var newS = null;
34292             s.parentNode.bubble(function(){
34293                 if(this.nextSibling){
34294                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
34295                     return false;
34296                 }
34297             });
34298             return newS;
34299          }
34300         return null;
34301     },
34302
34303     onKeyDown : function(e){
34304         var s = this.selNode || this.lastSelNode;
34305         // undesirable, but required
34306         var sm = this;
34307         if(!s){
34308             return;
34309         }
34310         var k = e.getKey();
34311         switch(k){
34312              case e.DOWN:
34313                  e.stopEvent();
34314                  this.selectNext();
34315              break;
34316              case e.UP:
34317                  e.stopEvent();
34318                  this.selectPrevious();
34319              break;
34320              case e.RIGHT:
34321                  e.preventDefault();
34322                  if(s.hasChildNodes()){
34323                      if(!s.isExpanded()){
34324                          s.expand();
34325                      }else if(s.firstChild){
34326                          this.select(s.firstChild, e);
34327                      }
34328                  }
34329              break;
34330              case e.LEFT:
34331                  e.preventDefault();
34332                  if(s.hasChildNodes() && s.isExpanded()){
34333                      s.collapse();
34334                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
34335                      this.select(s.parentNode, e);
34336                  }
34337              break;
34338         };
34339     }
34340 });
34341
34342 /**
34343  * @class Roo.tree.MultiSelectionModel
34344  * @extends Roo.util.Observable
34345  * Multi selection for a TreePanel.
34346  * @param {Object} cfg Configuration
34347  */
34348 Roo.tree.MultiSelectionModel = function(){
34349    this.selNodes = [];
34350    this.selMap = {};
34351    this.addEvents({
34352        /**
34353         * @event selectionchange
34354         * Fires when the selected nodes change
34355         * @param {MultiSelectionModel} this
34356         * @param {Array} nodes Array of the selected nodes
34357         */
34358        "selectionchange" : true
34359    });
34360    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
34361    
34362 };
34363
34364 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
34365     init : function(tree){
34366         this.tree = tree;
34367         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34368         tree.on("click", this.onNodeClick, this);
34369     },
34370     
34371     onNodeClick : function(node, e){
34372         this.select(node, e, e.ctrlKey);
34373     },
34374     
34375     /**
34376      * Select a node.
34377      * @param {TreeNode} node The node to select
34378      * @param {EventObject} e (optional) An event associated with the selection
34379      * @param {Boolean} keepExisting True to retain existing selections
34380      * @return {TreeNode} The selected node
34381      */
34382     select : function(node, e, keepExisting){
34383         if(keepExisting !== true){
34384             this.clearSelections(true);
34385         }
34386         if(this.isSelected(node)){
34387             this.lastSelNode = node;
34388             return node;
34389         }
34390         this.selNodes.push(node);
34391         this.selMap[node.id] = node;
34392         this.lastSelNode = node;
34393         node.ui.onSelectedChange(true);
34394         this.fireEvent("selectionchange", this, this.selNodes);
34395         return node;
34396     },
34397     
34398     /**
34399      * Deselect a node.
34400      * @param {TreeNode} node The node to unselect
34401      */
34402     unselect : function(node){
34403         if(this.selMap[node.id]){
34404             node.ui.onSelectedChange(false);
34405             var sn = this.selNodes;
34406             var index = -1;
34407             if(sn.indexOf){
34408                 index = sn.indexOf(node);
34409             }else{
34410                 for(var i = 0, len = sn.length; i < len; i++){
34411                     if(sn[i] == node){
34412                         index = i;
34413                         break;
34414                     }
34415                 }
34416             }
34417             if(index != -1){
34418                 this.selNodes.splice(index, 1);
34419             }
34420             delete this.selMap[node.id];
34421             this.fireEvent("selectionchange", this, this.selNodes);
34422         }
34423     },
34424     
34425     /**
34426      * Clear all selections
34427      */
34428     clearSelections : function(suppressEvent){
34429         var sn = this.selNodes;
34430         if(sn.length > 0){
34431             for(var i = 0, len = sn.length; i < len; i++){
34432                 sn[i].ui.onSelectedChange(false);
34433             }
34434             this.selNodes = [];
34435             this.selMap = {};
34436             if(suppressEvent !== true){
34437                 this.fireEvent("selectionchange", this, this.selNodes);
34438             }
34439         }
34440     },
34441     
34442     /**
34443      * Returns true if the node is selected
34444      * @param {TreeNode} node The node to check
34445      * @return {Boolean}
34446      */
34447     isSelected : function(node){
34448         return this.selMap[node.id] ? true : false;  
34449     },
34450     
34451     /**
34452      * Returns an array of the selected nodes
34453      * @return {Array}
34454      */
34455     getSelectedNodes : function(){
34456         return this.selNodes;    
34457     },
34458
34459     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
34460
34461     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
34462
34463     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
34464 });/*
34465  * Based on:
34466  * Ext JS Library 1.1.1
34467  * Copyright(c) 2006-2007, Ext JS, LLC.
34468  *
34469  * Originally Released Under LGPL - original licence link has changed is not relivant.
34470  *
34471  * Fork - LGPL
34472  * <script type="text/javascript">
34473  */
34474  
34475 /**
34476  * @class Roo.tree.TreeNode
34477  * @extends Roo.data.Node
34478  * @cfg {String} text The text for this node
34479  * @cfg {Boolean} expanded true to start the node expanded
34480  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
34481  * @cfg {Boolean} allowDrop false if this node cannot be drop on
34482  * @cfg {Boolean} disabled true to start the node disabled
34483  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
34484  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
34485  * @cfg {String} cls A css class to be added to the node
34486  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
34487  * @cfg {String} href URL of the link used for the node (defaults to #)
34488  * @cfg {String} hrefTarget target frame for the link
34489  * @cfg {String} qtip An Ext QuickTip for the node
34490  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
34491  * @cfg {Boolean} singleClickExpand True for single click expand on this node
34492  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
34493  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
34494  * (defaults to undefined with no checkbox rendered)
34495  * @constructor
34496  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
34497  */
34498 Roo.tree.TreeNode = function(attributes){
34499     attributes = attributes || {};
34500     if(typeof attributes == "string"){
34501         attributes = {text: attributes};
34502     }
34503     this.childrenRendered = false;
34504     this.rendered = false;
34505     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
34506     this.expanded = attributes.expanded === true;
34507     this.isTarget = attributes.isTarget !== false;
34508     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
34509     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
34510
34511     /**
34512      * Read-only. The text for this node. To change it use setText().
34513      * @type String
34514      */
34515     this.text = attributes.text;
34516     /**
34517      * True if this node is disabled.
34518      * @type Boolean
34519      */
34520     this.disabled = attributes.disabled === true;
34521
34522     this.addEvents({
34523         /**
34524         * @event textchange
34525         * Fires when the text for this node is changed
34526         * @param {Node} this This node
34527         * @param {String} text The new text
34528         * @param {String} oldText The old text
34529         */
34530         "textchange" : true,
34531         /**
34532         * @event beforeexpand
34533         * Fires before this node is expanded, return false to cancel.
34534         * @param {Node} this This node
34535         * @param {Boolean} deep
34536         * @param {Boolean} anim
34537         */
34538         "beforeexpand" : true,
34539         /**
34540         * @event beforecollapse
34541         * Fires before this node is collapsed, return false to cancel.
34542         * @param {Node} this This node
34543         * @param {Boolean} deep
34544         * @param {Boolean} anim
34545         */
34546         "beforecollapse" : true,
34547         /**
34548         * @event expand
34549         * Fires when this node is expanded
34550         * @param {Node} this This node
34551         */
34552         "expand" : true,
34553         /**
34554         * @event disabledchange
34555         * Fires when the disabled status of this node changes
34556         * @param {Node} this This node
34557         * @param {Boolean} disabled
34558         */
34559         "disabledchange" : true,
34560         /**
34561         * @event collapse
34562         * Fires when this node is collapsed
34563         * @param {Node} this This node
34564         */
34565         "collapse" : true,
34566         /**
34567         * @event beforeclick
34568         * Fires before click processing. Return false to cancel the default action.
34569         * @param {Node} this This node
34570         * @param {Roo.EventObject} e The event object
34571         */
34572         "beforeclick":true,
34573         /**
34574         * @event checkchange
34575         * Fires when a node with a checkbox's checked property changes
34576         * @param {Node} this This node
34577         * @param {Boolean} checked
34578         */
34579         "checkchange":true,
34580         /**
34581         * @event click
34582         * Fires when this node is clicked
34583         * @param {Node} this This node
34584         * @param {Roo.EventObject} e The event object
34585         */
34586         "click":true,
34587         /**
34588         * @event dblclick
34589         * Fires when this node is double clicked
34590         * @param {Node} this This node
34591         * @param {Roo.EventObject} e The event object
34592         */
34593         "dblclick":true,
34594         /**
34595         * @event contextmenu
34596         * Fires when this node is right clicked
34597         * @param {Node} this This node
34598         * @param {Roo.EventObject} e The event object
34599         */
34600         "contextmenu":true,
34601         /**
34602         * @event beforechildrenrendered
34603         * Fires right before the child nodes for this node are rendered
34604         * @param {Node} this This node
34605         */
34606         "beforechildrenrendered":true
34607     });
34608
34609     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
34610
34611     /**
34612      * Read-only. The UI for this node
34613      * @type TreeNodeUI
34614      */
34615     this.ui = new uiClass(this);
34616     
34617     // finally support items[]
34618     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
34619         return;
34620     }
34621     
34622     
34623     Roo.each(this.attributes.items, function(c) {
34624         this.appendChild(Roo.factory(c,Roo.Tree));
34625     }, this);
34626     delete this.attributes.items;
34627     
34628     
34629     
34630 };
34631 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
34632     preventHScroll: true,
34633     /**
34634      * Returns true if this node is expanded
34635      * @return {Boolean}
34636      */
34637     isExpanded : function(){
34638         return this.expanded;
34639     },
34640
34641     /**
34642      * Returns the UI object for this node
34643      * @return {TreeNodeUI}
34644      */
34645     getUI : function(){
34646         return this.ui;
34647     },
34648
34649     // private override
34650     setFirstChild : function(node){
34651         var of = this.firstChild;
34652         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
34653         if(this.childrenRendered && of && node != of){
34654             of.renderIndent(true, true);
34655         }
34656         if(this.rendered){
34657             this.renderIndent(true, true);
34658         }
34659     },
34660
34661     // private override
34662     setLastChild : function(node){
34663         var ol = this.lastChild;
34664         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
34665         if(this.childrenRendered && ol && node != ol){
34666             ol.renderIndent(true, true);
34667         }
34668         if(this.rendered){
34669             this.renderIndent(true, true);
34670         }
34671     },
34672
34673     // these methods are overridden to provide lazy rendering support
34674     // private override
34675     appendChild : function()
34676     {
34677         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
34678         if(node && this.childrenRendered){
34679             node.render();
34680         }
34681         this.ui.updateExpandIcon();
34682         return node;
34683     },
34684
34685     // private override
34686     removeChild : function(node){
34687         this.ownerTree.getSelectionModel().unselect(node);
34688         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
34689         // if it's been rendered remove dom node
34690         if(this.childrenRendered){
34691             node.ui.remove();
34692         }
34693         if(this.childNodes.length < 1){
34694             this.collapse(false, false);
34695         }else{
34696             this.ui.updateExpandIcon();
34697         }
34698         if(!this.firstChild) {
34699             this.childrenRendered = false;
34700         }
34701         return node;
34702     },
34703
34704     // private override
34705     insertBefore : function(node, refNode){
34706         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
34707         if(newNode && refNode && this.childrenRendered){
34708             node.render();
34709         }
34710         this.ui.updateExpandIcon();
34711         return newNode;
34712     },
34713
34714     /**
34715      * Sets the text for this node
34716      * @param {String} text
34717      */
34718     setText : function(text){
34719         var oldText = this.text;
34720         this.text = text;
34721         this.attributes.text = text;
34722         if(this.rendered){ // event without subscribing
34723             this.ui.onTextChange(this, text, oldText);
34724         }
34725         this.fireEvent("textchange", this, text, oldText);
34726     },
34727
34728     /**
34729      * Triggers selection of this node
34730      */
34731     select : function(){
34732         this.getOwnerTree().getSelectionModel().select(this);
34733     },
34734
34735     /**
34736      * Triggers deselection of this node
34737      */
34738     unselect : function(){
34739         this.getOwnerTree().getSelectionModel().unselect(this);
34740     },
34741
34742     /**
34743      * Returns true if this node is selected
34744      * @return {Boolean}
34745      */
34746     isSelected : function(){
34747         return this.getOwnerTree().getSelectionModel().isSelected(this);
34748     },
34749
34750     /**
34751      * Expand this node.
34752      * @param {Boolean} deep (optional) True to expand all children as well
34753      * @param {Boolean} anim (optional) false to cancel the default animation
34754      * @param {Function} callback (optional) A callback to be called when
34755      * expanding this node completes (does not wait for deep expand to complete).
34756      * Called with 1 parameter, this node.
34757      */
34758     expand : function(deep, anim, callback){
34759         if(!this.expanded){
34760             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
34761                 return;
34762             }
34763             if(!this.childrenRendered){
34764                 this.renderChildren();
34765             }
34766             this.expanded = true;
34767             
34768             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
34769                 this.ui.animExpand(function(){
34770                     this.fireEvent("expand", this);
34771                     if(typeof callback == "function"){
34772                         callback(this);
34773                     }
34774                     if(deep === true){
34775                         this.expandChildNodes(true);
34776                     }
34777                 }.createDelegate(this));
34778                 return;
34779             }else{
34780                 this.ui.expand();
34781                 this.fireEvent("expand", this);
34782                 if(typeof callback == "function"){
34783                     callback(this);
34784                 }
34785             }
34786         }else{
34787            if(typeof callback == "function"){
34788                callback(this);
34789            }
34790         }
34791         if(deep === true){
34792             this.expandChildNodes(true);
34793         }
34794     },
34795
34796     isHiddenRoot : function(){
34797         return this.isRoot && !this.getOwnerTree().rootVisible;
34798     },
34799
34800     /**
34801      * Collapse this node.
34802      * @param {Boolean} deep (optional) True to collapse all children as well
34803      * @param {Boolean} anim (optional) false to cancel the default animation
34804      */
34805     collapse : function(deep, anim){
34806         if(this.expanded && !this.isHiddenRoot()){
34807             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
34808                 return;
34809             }
34810             this.expanded = false;
34811             if((this.getOwnerTree().animate && anim !== false) || anim){
34812                 this.ui.animCollapse(function(){
34813                     this.fireEvent("collapse", this);
34814                     if(deep === true){
34815                         this.collapseChildNodes(true);
34816                     }
34817                 }.createDelegate(this));
34818                 return;
34819             }else{
34820                 this.ui.collapse();
34821                 this.fireEvent("collapse", this);
34822             }
34823         }
34824         if(deep === true){
34825             var cs = this.childNodes;
34826             for(var i = 0, len = cs.length; i < len; i++) {
34827                 cs[i].collapse(true, false);
34828             }
34829         }
34830     },
34831
34832     // private
34833     delayedExpand : function(delay){
34834         if(!this.expandProcId){
34835             this.expandProcId = this.expand.defer(delay, this);
34836         }
34837     },
34838
34839     // private
34840     cancelExpand : function(){
34841         if(this.expandProcId){
34842             clearTimeout(this.expandProcId);
34843         }
34844         this.expandProcId = false;
34845     },
34846
34847     /**
34848      * Toggles expanded/collapsed state of the node
34849      */
34850     toggle : function(){
34851         if(this.expanded){
34852             this.collapse();
34853         }else{
34854             this.expand();
34855         }
34856     },
34857
34858     /**
34859      * Ensures all parent nodes are expanded
34860      */
34861     ensureVisible : function(callback){
34862         var tree = this.getOwnerTree();
34863         tree.expandPath(this.parentNode.getPath(), false, function(){
34864             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
34865             Roo.callback(callback);
34866         }.createDelegate(this));
34867     },
34868
34869     /**
34870      * Expand all child nodes
34871      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
34872      */
34873     expandChildNodes : function(deep){
34874         var cs = this.childNodes;
34875         for(var i = 0, len = cs.length; i < len; i++) {
34876                 cs[i].expand(deep);
34877         }
34878     },
34879
34880     /**
34881      * Collapse all child nodes
34882      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
34883      */
34884     collapseChildNodes : function(deep){
34885         var cs = this.childNodes;
34886         for(var i = 0, len = cs.length; i < len; i++) {
34887                 cs[i].collapse(deep);
34888         }
34889     },
34890
34891     /**
34892      * Disables this node
34893      */
34894     disable : function(){
34895         this.disabled = true;
34896         this.unselect();
34897         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
34898             this.ui.onDisableChange(this, true);
34899         }
34900         this.fireEvent("disabledchange", this, true);
34901     },
34902
34903     /**
34904      * Enables this node
34905      */
34906     enable : function(){
34907         this.disabled = false;
34908         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
34909             this.ui.onDisableChange(this, false);
34910         }
34911         this.fireEvent("disabledchange", this, false);
34912     },
34913
34914     // private
34915     renderChildren : function(suppressEvent){
34916         if(suppressEvent !== false){
34917             this.fireEvent("beforechildrenrendered", this);
34918         }
34919         var cs = this.childNodes;
34920         for(var i = 0, len = cs.length; i < len; i++){
34921             cs[i].render(true);
34922         }
34923         this.childrenRendered = true;
34924     },
34925
34926     // private
34927     sort : function(fn, scope){
34928         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
34929         if(this.childrenRendered){
34930             var cs = this.childNodes;
34931             for(var i = 0, len = cs.length; i < len; i++){
34932                 cs[i].render(true);
34933             }
34934         }
34935     },
34936
34937     // private
34938     render : function(bulkRender){
34939         this.ui.render(bulkRender);
34940         if(!this.rendered){
34941             this.rendered = true;
34942             if(this.expanded){
34943                 this.expanded = false;
34944                 this.expand(false, false);
34945             }
34946         }
34947     },
34948
34949     // private
34950     renderIndent : function(deep, refresh){
34951         if(refresh){
34952             this.ui.childIndent = null;
34953         }
34954         this.ui.renderIndent();
34955         if(deep === true && this.childrenRendered){
34956             var cs = this.childNodes;
34957             for(var i = 0, len = cs.length; i < len; i++){
34958                 cs[i].renderIndent(true, refresh);
34959             }
34960         }
34961     }
34962 });/*
34963  * Based on:
34964  * Ext JS Library 1.1.1
34965  * Copyright(c) 2006-2007, Ext JS, LLC.
34966  *
34967  * Originally Released Under LGPL - original licence link has changed is not relivant.
34968  *
34969  * Fork - LGPL
34970  * <script type="text/javascript">
34971  */
34972  
34973 /**
34974  * @class Roo.tree.AsyncTreeNode
34975  * @extends Roo.tree.TreeNode
34976  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
34977  * @constructor
34978  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
34979  */
34980  Roo.tree.AsyncTreeNode = function(config){
34981     this.loaded = false;
34982     this.loading = false;
34983     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
34984     /**
34985     * @event beforeload
34986     * Fires before this node is loaded, return false to cancel
34987     * @param {Node} this This node
34988     */
34989     this.addEvents({'beforeload':true, 'load': true});
34990     /**
34991     * @event load
34992     * Fires when this node is loaded
34993     * @param {Node} this This node
34994     */
34995     /**
34996      * The loader used by this node (defaults to using the tree's defined loader)
34997      * @type TreeLoader
34998      * @property loader
34999      */
35000 };
35001 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
35002     expand : function(deep, anim, callback){
35003         if(this.loading){ // if an async load is already running, waiting til it's done
35004             var timer;
35005             var f = function(){
35006                 if(!this.loading){ // done loading
35007                     clearInterval(timer);
35008                     this.expand(deep, anim, callback);
35009                 }
35010             }.createDelegate(this);
35011             timer = setInterval(f, 200);
35012             return;
35013         }
35014         if(!this.loaded){
35015             if(this.fireEvent("beforeload", this) === false){
35016                 return;
35017             }
35018             this.loading = true;
35019             this.ui.beforeLoad(this);
35020             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
35021             if(loader){
35022                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
35023                 return;
35024             }
35025         }
35026         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
35027     },
35028     
35029     /**
35030      * Returns true if this node is currently loading
35031      * @return {Boolean}
35032      */
35033     isLoading : function(){
35034         return this.loading;  
35035     },
35036     
35037     loadComplete : function(deep, anim, callback){
35038         this.loading = false;
35039         this.loaded = true;
35040         this.ui.afterLoad(this);
35041         this.fireEvent("load", this);
35042         this.expand(deep, anim, callback);
35043     },
35044     
35045     /**
35046      * Returns true if this node has been loaded
35047      * @return {Boolean}
35048      */
35049     isLoaded : function(){
35050         return this.loaded;
35051     },
35052     
35053     hasChildNodes : function(){
35054         if(!this.isLeaf() && !this.loaded){
35055             return true;
35056         }else{
35057             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
35058         }
35059     },
35060
35061     /**
35062      * Trigger a reload for this node
35063      * @param {Function} callback
35064      */
35065     reload : function(callback){
35066         this.collapse(false, false);
35067         while(this.firstChild){
35068             this.removeChild(this.firstChild);
35069         }
35070         this.childrenRendered = false;
35071         this.loaded = false;
35072         if(this.isHiddenRoot()){
35073             this.expanded = false;
35074         }
35075         this.expand(false, false, callback);
35076     }
35077 });/*
35078  * Based on:
35079  * Ext JS Library 1.1.1
35080  * Copyright(c) 2006-2007, Ext JS, LLC.
35081  *
35082  * Originally Released Under LGPL - original licence link has changed is not relivant.
35083  *
35084  * Fork - LGPL
35085  * <script type="text/javascript">
35086  */
35087  
35088 /**
35089  * @class Roo.tree.TreeNodeUI
35090  * @constructor
35091  * @param {Object} node The node to render
35092  * The TreeNode UI implementation is separate from the
35093  * tree implementation. Unless you are customizing the tree UI,
35094  * you should never have to use this directly.
35095  */
35096 Roo.tree.TreeNodeUI = function(node){
35097     this.node = node;
35098     this.rendered = false;
35099     this.animating = false;
35100     this.emptyIcon = Roo.BLANK_IMAGE_URL;
35101 };
35102
35103 Roo.tree.TreeNodeUI.prototype = {
35104     removeChild : function(node){
35105         if(this.rendered){
35106             this.ctNode.removeChild(node.ui.getEl());
35107         }
35108     },
35109
35110     beforeLoad : function(){
35111          this.addClass("x-tree-node-loading");
35112     },
35113
35114     afterLoad : function(){
35115          this.removeClass("x-tree-node-loading");
35116     },
35117
35118     onTextChange : function(node, text, oldText){
35119         if(this.rendered){
35120             this.textNode.innerHTML = text;
35121         }
35122     },
35123
35124     onDisableChange : function(node, state){
35125         this.disabled = state;
35126         if(state){
35127             this.addClass("x-tree-node-disabled");
35128         }else{
35129             this.removeClass("x-tree-node-disabled");
35130         }
35131     },
35132
35133     onSelectedChange : function(state){
35134         if(state){
35135             this.focus();
35136             this.addClass("x-tree-selected");
35137         }else{
35138             //this.blur();
35139             this.removeClass("x-tree-selected");
35140         }
35141     },
35142
35143     onMove : function(tree, node, oldParent, newParent, index, refNode){
35144         this.childIndent = null;
35145         if(this.rendered){
35146             var targetNode = newParent.ui.getContainer();
35147             if(!targetNode){//target not rendered
35148                 this.holder = document.createElement("div");
35149                 this.holder.appendChild(this.wrap);
35150                 return;
35151             }
35152             var insertBefore = refNode ? refNode.ui.getEl() : null;
35153             if(insertBefore){
35154                 targetNode.insertBefore(this.wrap, insertBefore);
35155             }else{
35156                 targetNode.appendChild(this.wrap);
35157             }
35158             this.node.renderIndent(true);
35159         }
35160     },
35161
35162     addClass : function(cls){
35163         if(this.elNode){
35164             Roo.fly(this.elNode).addClass(cls);
35165         }
35166     },
35167
35168     removeClass : function(cls){
35169         if(this.elNode){
35170             Roo.fly(this.elNode).removeClass(cls);
35171         }
35172     },
35173
35174     remove : function(){
35175         if(this.rendered){
35176             this.holder = document.createElement("div");
35177             this.holder.appendChild(this.wrap);
35178         }
35179     },
35180
35181     fireEvent : function(){
35182         return this.node.fireEvent.apply(this.node, arguments);
35183     },
35184
35185     initEvents : function(){
35186         this.node.on("move", this.onMove, this);
35187         var E = Roo.EventManager;
35188         var a = this.anchor;
35189
35190         var el = Roo.fly(a, '_treeui');
35191
35192         if(Roo.isOpera){ // opera render bug ignores the CSS
35193             el.setStyle("text-decoration", "none");
35194         }
35195
35196         el.on("click", this.onClick, this);
35197         el.on("dblclick", this.onDblClick, this);
35198
35199         if(this.checkbox){
35200             Roo.EventManager.on(this.checkbox,
35201                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
35202         }
35203
35204         el.on("contextmenu", this.onContextMenu, this);
35205
35206         var icon = Roo.fly(this.iconNode);
35207         icon.on("click", this.onClick, this);
35208         icon.on("dblclick", this.onDblClick, this);
35209         icon.on("contextmenu", this.onContextMenu, this);
35210         E.on(this.ecNode, "click", this.ecClick, this, true);
35211
35212         if(this.node.disabled){
35213             this.addClass("x-tree-node-disabled");
35214         }
35215         if(this.node.hidden){
35216             this.addClass("x-tree-node-disabled");
35217         }
35218         var ot = this.node.getOwnerTree();
35219         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
35220         if(dd && (!this.node.isRoot || ot.rootVisible)){
35221             Roo.dd.Registry.register(this.elNode, {
35222                 node: this.node,
35223                 handles: this.getDDHandles(),
35224                 isHandle: false
35225             });
35226         }
35227     },
35228
35229     getDDHandles : function(){
35230         return [this.iconNode, this.textNode];
35231     },
35232
35233     hide : function(){
35234         if(this.rendered){
35235             this.wrap.style.display = "none";
35236         }
35237     },
35238
35239     show : function(){
35240         if(this.rendered){
35241             this.wrap.style.display = "";
35242         }
35243     },
35244
35245     onContextMenu : function(e){
35246         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
35247             e.preventDefault();
35248             this.focus();
35249             this.fireEvent("contextmenu", this.node, e);
35250         }
35251     },
35252
35253     onClick : function(e){
35254         if(this.dropping){
35255             e.stopEvent();
35256             return;
35257         }
35258         if(this.fireEvent("beforeclick", this.node, e) !== false){
35259             if(!this.disabled && this.node.attributes.href){
35260                 this.fireEvent("click", this.node, e);
35261                 return;
35262             }
35263             e.preventDefault();
35264             if(this.disabled){
35265                 return;
35266             }
35267
35268             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
35269                 this.node.toggle();
35270             }
35271
35272             this.fireEvent("click", this.node, e);
35273         }else{
35274             e.stopEvent();
35275         }
35276     },
35277
35278     onDblClick : function(e){
35279         e.preventDefault();
35280         if(this.disabled){
35281             return;
35282         }
35283         if(this.checkbox){
35284             this.toggleCheck();
35285         }
35286         if(!this.animating && this.node.hasChildNodes()){
35287             this.node.toggle();
35288         }
35289         this.fireEvent("dblclick", this.node, e);
35290     },
35291
35292     onCheckChange : function(){
35293         var checked = this.checkbox.checked;
35294         this.node.attributes.checked = checked;
35295         this.fireEvent('checkchange', this.node, checked);
35296     },
35297
35298     ecClick : function(e){
35299         if(!this.animating && this.node.hasChildNodes()){
35300             this.node.toggle();
35301         }
35302     },
35303
35304     startDrop : function(){
35305         this.dropping = true;
35306     },
35307
35308     // delayed drop so the click event doesn't get fired on a drop
35309     endDrop : function(){
35310        setTimeout(function(){
35311            this.dropping = false;
35312        }.createDelegate(this), 50);
35313     },
35314
35315     expand : function(){
35316         this.updateExpandIcon();
35317         this.ctNode.style.display = "";
35318     },
35319
35320     focus : function(){
35321         if(!this.node.preventHScroll){
35322             try{this.anchor.focus();
35323             }catch(e){}
35324         }else if(!Roo.isIE){
35325             try{
35326                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
35327                 var l = noscroll.scrollLeft;
35328                 this.anchor.focus();
35329                 noscroll.scrollLeft = l;
35330             }catch(e){}
35331         }
35332     },
35333
35334     toggleCheck : function(value){
35335         var cb = this.checkbox;
35336         if(cb){
35337             cb.checked = (value === undefined ? !cb.checked : value);
35338         }
35339     },
35340
35341     blur : function(){
35342         try{
35343             this.anchor.blur();
35344         }catch(e){}
35345     },
35346
35347     animExpand : function(callback){
35348         var ct = Roo.get(this.ctNode);
35349         ct.stopFx();
35350         if(!this.node.hasChildNodes()){
35351             this.updateExpandIcon();
35352             this.ctNode.style.display = "";
35353             Roo.callback(callback);
35354             return;
35355         }
35356         this.animating = true;
35357         this.updateExpandIcon();
35358
35359         ct.slideIn('t', {
35360            callback : function(){
35361                this.animating = false;
35362                Roo.callback(callback);
35363             },
35364             scope: this,
35365             duration: this.node.ownerTree.duration || .25
35366         });
35367     },
35368
35369     highlight : function(){
35370         var tree = this.node.getOwnerTree();
35371         Roo.fly(this.wrap).highlight(
35372             tree.hlColor || "C3DAF9",
35373             {endColor: tree.hlBaseColor}
35374         );
35375     },
35376
35377     collapse : function(){
35378         this.updateExpandIcon();
35379         this.ctNode.style.display = "none";
35380     },
35381
35382     animCollapse : function(callback){
35383         var ct = Roo.get(this.ctNode);
35384         ct.enableDisplayMode('block');
35385         ct.stopFx();
35386
35387         this.animating = true;
35388         this.updateExpandIcon();
35389
35390         ct.slideOut('t', {
35391             callback : function(){
35392                this.animating = false;
35393                Roo.callback(callback);
35394             },
35395             scope: this,
35396             duration: this.node.ownerTree.duration || .25
35397         });
35398     },
35399
35400     getContainer : function(){
35401         return this.ctNode;
35402     },
35403
35404     getEl : function(){
35405         return this.wrap;
35406     },
35407
35408     appendDDGhost : function(ghostNode){
35409         ghostNode.appendChild(this.elNode.cloneNode(true));
35410     },
35411
35412     getDDRepairXY : function(){
35413         return Roo.lib.Dom.getXY(this.iconNode);
35414     },
35415
35416     onRender : function(){
35417         this.render();
35418     },
35419
35420     render : function(bulkRender){
35421         var n = this.node, a = n.attributes;
35422         var targetNode = n.parentNode ?
35423               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
35424
35425         if(!this.rendered){
35426             this.rendered = true;
35427
35428             this.renderElements(n, a, targetNode, bulkRender);
35429
35430             if(a.qtip){
35431                if(this.textNode.setAttributeNS){
35432                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
35433                    if(a.qtipTitle){
35434                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
35435                    }
35436                }else{
35437                    this.textNode.setAttribute("ext:qtip", a.qtip);
35438                    if(a.qtipTitle){
35439                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
35440                    }
35441                }
35442             }else if(a.qtipCfg){
35443                 a.qtipCfg.target = Roo.id(this.textNode);
35444                 Roo.QuickTips.register(a.qtipCfg);
35445             }
35446             this.initEvents();
35447             if(!this.node.expanded){
35448                 this.updateExpandIcon();
35449             }
35450         }else{
35451             if(bulkRender === true) {
35452                 targetNode.appendChild(this.wrap);
35453             }
35454         }
35455     },
35456
35457     renderElements : function(n, a, targetNode, bulkRender)
35458     {
35459         // add some indent caching, this helps performance when rendering a large tree
35460         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35461         var t = n.getOwnerTree();
35462         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
35463         if (typeof(n.attributes.html) != 'undefined') {
35464             txt = n.attributes.html;
35465         }
35466         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
35467         var cb = typeof a.checked == 'boolean';
35468         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35469         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
35470             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
35471             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
35472             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
35473             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
35474             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
35475              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
35476                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
35477             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35478             "</li>"];
35479
35480         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35481             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35482                                 n.nextSibling.ui.getEl(), buf.join(""));
35483         }else{
35484             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35485         }
35486
35487         this.elNode = this.wrap.childNodes[0];
35488         this.ctNode = this.wrap.childNodes[1];
35489         var cs = this.elNode.childNodes;
35490         this.indentNode = cs[0];
35491         this.ecNode = cs[1];
35492         this.iconNode = cs[2];
35493         var index = 3;
35494         if(cb){
35495             this.checkbox = cs[3];
35496             index++;
35497         }
35498         this.anchor = cs[index];
35499         this.textNode = cs[index].firstChild;
35500     },
35501
35502     getAnchor : function(){
35503         return this.anchor;
35504     },
35505
35506     getTextEl : function(){
35507         return this.textNode;
35508     },
35509
35510     getIconEl : function(){
35511         return this.iconNode;
35512     },
35513
35514     isChecked : function(){
35515         return this.checkbox ? this.checkbox.checked : false;
35516     },
35517
35518     updateExpandIcon : function(){
35519         if(this.rendered){
35520             var n = this.node, c1, c2;
35521             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
35522             var hasChild = n.hasChildNodes();
35523             if(hasChild){
35524                 if(n.expanded){
35525                     cls += "-minus";
35526                     c1 = "x-tree-node-collapsed";
35527                     c2 = "x-tree-node-expanded";
35528                 }else{
35529                     cls += "-plus";
35530                     c1 = "x-tree-node-expanded";
35531                     c2 = "x-tree-node-collapsed";
35532                 }
35533                 if(this.wasLeaf){
35534                     this.removeClass("x-tree-node-leaf");
35535                     this.wasLeaf = false;
35536                 }
35537                 if(this.c1 != c1 || this.c2 != c2){
35538                     Roo.fly(this.elNode).replaceClass(c1, c2);
35539                     this.c1 = c1; this.c2 = c2;
35540                 }
35541             }else{
35542                 // this changes non-leafs into leafs if they have no children.
35543                 // it's not very rational behaviour..
35544                 
35545                 if(!this.wasLeaf && this.node.leaf){
35546                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
35547                     delete this.c1;
35548                     delete this.c2;
35549                     this.wasLeaf = true;
35550                 }
35551             }
35552             var ecc = "x-tree-ec-icon "+cls;
35553             if(this.ecc != ecc){
35554                 this.ecNode.className = ecc;
35555                 this.ecc = ecc;
35556             }
35557         }
35558     },
35559
35560     getChildIndent : function(){
35561         if(!this.childIndent){
35562             var buf = [];
35563             var p = this.node;
35564             while(p){
35565                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
35566                     if(!p.isLast()) {
35567                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
35568                     } else {
35569                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
35570                     }
35571                 }
35572                 p = p.parentNode;
35573             }
35574             this.childIndent = buf.join("");
35575         }
35576         return this.childIndent;
35577     },
35578
35579     renderIndent : function(){
35580         if(this.rendered){
35581             var indent = "";
35582             var p = this.node.parentNode;
35583             if(p){
35584                 indent = p.ui.getChildIndent();
35585             }
35586             if(this.indentMarkup != indent){ // don't rerender if not required
35587                 this.indentNode.innerHTML = indent;
35588                 this.indentMarkup = indent;
35589             }
35590             this.updateExpandIcon();
35591         }
35592     }
35593 };
35594
35595 Roo.tree.RootTreeNodeUI = function(){
35596     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
35597 };
35598 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
35599     render : function(){
35600         if(!this.rendered){
35601             var targetNode = this.node.ownerTree.innerCt.dom;
35602             this.node.expanded = true;
35603             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
35604             this.wrap = this.ctNode = targetNode.firstChild;
35605         }
35606     },
35607     collapse : function(){
35608     },
35609     expand : function(){
35610     }
35611 });/*
35612  * Based on:
35613  * Ext JS Library 1.1.1
35614  * Copyright(c) 2006-2007, Ext JS, LLC.
35615  *
35616  * Originally Released Under LGPL - original licence link has changed is not relivant.
35617  *
35618  * Fork - LGPL
35619  * <script type="text/javascript">
35620  */
35621 /**
35622  * @class Roo.tree.TreeLoader
35623  * @extends Roo.util.Observable
35624  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
35625  * nodes from a specified URL. The response must be a javascript Array definition
35626  * who's elements are node definition objects. eg:
35627  * <pre><code>
35628 {  success : true,
35629    data :      [
35630    
35631     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
35632     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
35633     ]
35634 }
35635
35636
35637 </code></pre>
35638  * <br><br>
35639  * The old style respose with just an array is still supported, but not recommended.
35640  * <br><br>
35641  *
35642  * A server request is sent, and child nodes are loaded only when a node is expanded.
35643  * The loading node's id is passed to the server under the parameter name "node" to
35644  * enable the server to produce the correct child nodes.
35645  * <br><br>
35646  * To pass extra parameters, an event handler may be attached to the "beforeload"
35647  * event, and the parameters specified in the TreeLoader's baseParams property:
35648  * <pre><code>
35649     myTreeLoader.on("beforeload", function(treeLoader, node) {
35650         this.baseParams.category = node.attributes.category;
35651     }, this);
35652     
35653 </code></pre>
35654  *
35655  * This would pass an HTTP parameter called "category" to the server containing
35656  * the value of the Node's "category" attribute.
35657  * @constructor
35658  * Creates a new Treeloader.
35659  * @param {Object} config A config object containing config properties.
35660  */
35661 Roo.tree.TreeLoader = function(config){
35662     this.baseParams = {};
35663     this.requestMethod = "POST";
35664     Roo.apply(this, config);
35665
35666     this.addEvents({
35667     
35668         /**
35669          * @event beforeload
35670          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
35671          * @param {Object} This TreeLoader object.
35672          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
35673          * @param {Object} callback The callback function specified in the {@link #load} call.
35674          */
35675         beforeload : true,
35676         /**
35677          * @event load
35678          * Fires when the node has been successfuly loaded.
35679          * @param {Object} This TreeLoader object.
35680          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
35681          * @param {Object} response The response object containing the data from the server.
35682          */
35683         load : true,
35684         /**
35685          * @event loadexception
35686          * Fires if the network request failed.
35687          * @param {Object} This TreeLoader object.
35688          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
35689          * @param {Object} response The response object containing the data from the server.
35690          */
35691         loadexception : true,
35692         /**
35693          * @event create
35694          * Fires before a node is created, enabling you to return custom Node types 
35695          * @param {Object} This TreeLoader object.
35696          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
35697          */
35698         create : true
35699     });
35700
35701     Roo.tree.TreeLoader.superclass.constructor.call(this);
35702 };
35703
35704 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
35705     /**
35706     * @cfg {String} dataUrl The URL from which to request a Json string which
35707     * specifies an array of node definition object representing the child nodes
35708     * to be loaded.
35709     */
35710     /**
35711     * @cfg {String} requestMethod either GET or POST
35712     * defaults to POST (due to BC)
35713     * to be loaded.
35714     */
35715     /**
35716     * @cfg {Object} baseParams (optional) An object containing properties which
35717     * specify HTTP parameters to be passed to each request for child nodes.
35718     */
35719     /**
35720     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
35721     * created by this loader. If the attributes sent by the server have an attribute in this object,
35722     * they take priority.
35723     */
35724     /**
35725     * @cfg {Object} uiProviders (optional) An object containing properties which
35726     * 
35727     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
35728     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
35729     * <i>uiProvider</i> attribute of a returned child node is a string rather
35730     * than a reference to a TreeNodeUI implementation, this that string value
35731     * is used as a property name in the uiProviders object. You can define the provider named
35732     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
35733     */
35734     uiProviders : {},
35735
35736     /**
35737     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
35738     * child nodes before loading.
35739     */
35740     clearOnLoad : true,
35741
35742     /**
35743     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
35744     * property on loading, rather than expecting an array. (eg. more compatible to a standard
35745     * Grid query { data : [ .....] }
35746     */
35747     
35748     root : false,
35749      /**
35750     * @cfg {String} queryParam (optional) 
35751     * Name of the query as it will be passed on the querystring (defaults to 'node')
35752     * eg. the request will be ?node=[id]
35753     */
35754     
35755     
35756     queryParam: false,
35757     
35758     /**
35759      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
35760      * This is called automatically when a node is expanded, but may be used to reload
35761      * a node (or append new children if the {@link #clearOnLoad} option is false.)
35762      * @param {Roo.tree.TreeNode} node
35763      * @param {Function} callback
35764      */
35765     load : function(node, callback){
35766         if(this.clearOnLoad){
35767             while(node.firstChild){
35768                 node.removeChild(node.firstChild);
35769             }
35770         }
35771         if(node.attributes.children){ // preloaded json children
35772             var cs = node.attributes.children;
35773             for(var i = 0, len = cs.length; i < len; i++){
35774                 node.appendChild(this.createNode(cs[i]));
35775             }
35776             if(typeof callback == "function"){
35777                 callback();
35778             }
35779         }else if(this.dataUrl){
35780             this.requestData(node, callback);
35781         }
35782     },
35783
35784     getParams: function(node){
35785         var buf = [], bp = this.baseParams;
35786         for(var key in bp){
35787             if(typeof bp[key] != "function"){
35788                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
35789             }
35790         }
35791         var n = this.queryParam === false ? 'node' : this.queryParam;
35792         buf.push(n + "=", encodeURIComponent(node.id));
35793         return buf.join("");
35794     },
35795
35796     requestData : function(node, callback){
35797         if(this.fireEvent("beforeload", this, node, callback) !== false){
35798             this.transId = Roo.Ajax.request({
35799                 method:this.requestMethod,
35800                 url: this.dataUrl||this.url,
35801                 success: this.handleResponse,
35802                 failure: this.handleFailure,
35803                 scope: this,
35804                 argument: {callback: callback, node: node},
35805                 params: this.getParams(node)
35806             });
35807         }else{
35808             // if the load is cancelled, make sure we notify
35809             // the node that we are done
35810             if(typeof callback == "function"){
35811                 callback();
35812             }
35813         }
35814     },
35815
35816     isLoading : function(){
35817         return this.transId ? true : false;
35818     },
35819
35820     abort : function(){
35821         if(this.isLoading()){
35822             Roo.Ajax.abort(this.transId);
35823         }
35824     },
35825
35826     // private
35827     createNode : function(attr)
35828     {
35829         // apply baseAttrs, nice idea Corey!
35830         if(this.baseAttrs){
35831             Roo.applyIf(attr, this.baseAttrs);
35832         }
35833         if(this.applyLoader !== false){
35834             attr.loader = this;
35835         }
35836         // uiProvider = depreciated..
35837         
35838         if(typeof(attr.uiProvider) == 'string'){
35839            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
35840                 /**  eval:var:attr */ eval(attr.uiProvider);
35841         }
35842         if(typeof(this.uiProviders['default']) != 'undefined') {
35843             attr.uiProvider = this.uiProviders['default'];
35844         }
35845         
35846         this.fireEvent('create', this, attr);
35847         
35848         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
35849         return(attr.leaf ?
35850                         new Roo.tree.TreeNode(attr) :
35851                         new Roo.tree.AsyncTreeNode(attr));
35852     },
35853
35854     processResponse : function(response, node, callback)
35855     {
35856         var json = response.responseText;
35857         try {
35858             
35859             var o = Roo.decode(json);
35860             
35861             if (this.root === false && typeof(o.success) != undefined) {
35862                 this.root = 'data'; // the default behaviour for list like data..
35863                 }
35864                 
35865             if (this.root !== false &&  !o.success) {
35866                 // it's a failure condition.
35867                 var a = response.argument;
35868                 this.fireEvent("loadexception", this, a.node, response);
35869                 Roo.log("Load failed - should have a handler really");
35870                 return;
35871             }
35872             
35873             
35874             
35875             if (this.root !== false) {
35876                  o = o[this.root];
35877             }
35878             
35879             for(var i = 0, len = o.length; i < len; i++){
35880                 var n = this.createNode(o[i]);
35881                 if(n){
35882                     node.appendChild(n);
35883                 }
35884             }
35885             if(typeof callback == "function"){
35886                 callback(this, node);
35887             }
35888         }catch(e){
35889             this.handleFailure(response);
35890         }
35891     },
35892
35893     handleResponse : function(response){
35894         this.transId = false;
35895         var a = response.argument;
35896         this.processResponse(response, a.node, a.callback);
35897         this.fireEvent("load", this, a.node, response);
35898     },
35899
35900     handleFailure : function(response)
35901     {
35902         // should handle failure better..
35903         this.transId = false;
35904         var a = response.argument;
35905         this.fireEvent("loadexception", this, a.node, response);
35906         if(typeof a.callback == "function"){
35907             a.callback(this, a.node);
35908         }
35909     }
35910 });/*
35911  * Based on:
35912  * Ext JS Library 1.1.1
35913  * Copyright(c) 2006-2007, Ext JS, LLC.
35914  *
35915  * Originally Released Under LGPL - original licence link has changed is not relivant.
35916  *
35917  * Fork - LGPL
35918  * <script type="text/javascript">
35919  */
35920
35921 /**
35922 * @class Roo.tree.TreeFilter
35923 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
35924 * @param {TreePanel} tree
35925 * @param {Object} config (optional)
35926  */
35927 Roo.tree.TreeFilter = function(tree, config){
35928     this.tree = tree;
35929     this.filtered = {};
35930     Roo.apply(this, config);
35931 };
35932
35933 Roo.tree.TreeFilter.prototype = {
35934     clearBlank:false,
35935     reverse:false,
35936     autoClear:false,
35937     remove:false,
35938
35939      /**
35940      * Filter the data by a specific attribute.
35941      * @param {String/RegExp} value Either string that the attribute value
35942      * should start with or a RegExp to test against the attribute
35943      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
35944      * @param {TreeNode} startNode (optional) The node to start the filter at.
35945      */
35946     filter : function(value, attr, startNode){
35947         attr = attr || "text";
35948         var f;
35949         if(typeof value == "string"){
35950             var vlen = value.length;
35951             // auto clear empty filter
35952             if(vlen == 0 && this.clearBlank){
35953                 this.clear();
35954                 return;
35955             }
35956             value = value.toLowerCase();
35957             f = function(n){
35958                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
35959             };
35960         }else if(value.exec){ // regex?
35961             f = function(n){
35962                 return value.test(n.attributes[attr]);
35963             };
35964         }else{
35965             throw 'Illegal filter type, must be string or regex';
35966         }
35967         this.filterBy(f, null, startNode);
35968         },
35969
35970     /**
35971      * Filter by a function. The passed function will be called with each
35972      * node in the tree (or from the startNode). If the function returns true, the node is kept
35973      * otherwise it is filtered. If a node is filtered, its children are also filtered.
35974      * @param {Function} fn The filter function
35975      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
35976      */
35977     filterBy : function(fn, scope, startNode){
35978         startNode = startNode || this.tree.root;
35979         if(this.autoClear){
35980             this.clear();
35981         }
35982         var af = this.filtered, rv = this.reverse;
35983         var f = function(n){
35984             if(n == startNode){
35985                 return true;
35986             }
35987             if(af[n.id]){
35988                 return false;
35989             }
35990             var m = fn.call(scope || n, n);
35991             if(!m || rv){
35992                 af[n.id] = n;
35993                 n.ui.hide();
35994                 return false;
35995             }
35996             return true;
35997         };
35998         startNode.cascade(f);
35999         if(this.remove){
36000            for(var id in af){
36001                if(typeof id != "function"){
36002                    var n = af[id];
36003                    if(n && n.parentNode){
36004                        n.parentNode.removeChild(n);
36005                    }
36006                }
36007            }
36008         }
36009     },
36010
36011     /**
36012      * Clears the current filter. Note: with the "remove" option
36013      * set a filter cannot be cleared.
36014      */
36015     clear : function(){
36016         var t = this.tree;
36017         var af = this.filtered;
36018         for(var id in af){
36019             if(typeof id != "function"){
36020                 var n = af[id];
36021                 if(n){
36022                     n.ui.show();
36023                 }
36024             }
36025         }
36026         this.filtered = {};
36027     }
36028 };
36029 /*
36030  * Based on:
36031  * Ext JS Library 1.1.1
36032  * Copyright(c) 2006-2007, Ext JS, LLC.
36033  *
36034  * Originally Released Under LGPL - original licence link has changed is not relivant.
36035  *
36036  * Fork - LGPL
36037  * <script type="text/javascript">
36038  */
36039  
36040
36041 /**
36042  * @class Roo.tree.TreeSorter
36043  * Provides sorting of nodes in a TreePanel
36044  * 
36045  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
36046  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
36047  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
36048  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
36049  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
36050  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
36051  * @constructor
36052  * @param {TreePanel} tree
36053  * @param {Object} config
36054  */
36055 Roo.tree.TreeSorter = function(tree, config){
36056     Roo.apply(this, config);
36057     tree.on("beforechildrenrendered", this.doSort, this);
36058     tree.on("append", this.updateSort, this);
36059     tree.on("insert", this.updateSort, this);
36060     
36061     var dsc = this.dir && this.dir.toLowerCase() == "desc";
36062     var p = this.property || "text";
36063     var sortType = this.sortType;
36064     var fs = this.folderSort;
36065     var cs = this.caseSensitive === true;
36066     var leafAttr = this.leafAttr || 'leaf';
36067
36068     this.sortFn = function(n1, n2){
36069         if(fs){
36070             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
36071                 return 1;
36072             }
36073             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
36074                 return -1;
36075             }
36076         }
36077         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
36078         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
36079         if(v1 < v2){
36080                         return dsc ? +1 : -1;
36081                 }else if(v1 > v2){
36082                         return dsc ? -1 : +1;
36083         }else{
36084                 return 0;
36085         }
36086     };
36087 };
36088
36089 Roo.tree.TreeSorter.prototype = {
36090     doSort : function(node){
36091         node.sort(this.sortFn);
36092     },
36093     
36094     compareNodes : function(n1, n2){
36095         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
36096     },
36097     
36098     updateSort : function(tree, node){
36099         if(node.childrenRendered){
36100             this.doSort.defer(1, this, [node]);
36101         }
36102     }
36103 };/*
36104  * Based on:
36105  * Ext JS Library 1.1.1
36106  * Copyright(c) 2006-2007, Ext JS, LLC.
36107  *
36108  * Originally Released Under LGPL - original licence link has changed is not relivant.
36109  *
36110  * Fork - LGPL
36111  * <script type="text/javascript">
36112  */
36113
36114 if(Roo.dd.DropZone){
36115     
36116 Roo.tree.TreeDropZone = function(tree, config){
36117     this.allowParentInsert = false;
36118     this.allowContainerDrop = false;
36119     this.appendOnly = false;
36120     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
36121     this.tree = tree;
36122     this.lastInsertClass = "x-tree-no-status";
36123     this.dragOverData = {};
36124 };
36125
36126 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
36127     ddGroup : "TreeDD",
36128     scroll:  true,
36129     
36130     expandDelay : 1000,
36131     
36132     expandNode : function(node){
36133         if(node.hasChildNodes() && !node.isExpanded()){
36134             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
36135         }
36136     },
36137     
36138     queueExpand : function(node){
36139         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
36140     },
36141     
36142     cancelExpand : function(){
36143         if(this.expandProcId){
36144             clearTimeout(this.expandProcId);
36145             this.expandProcId = false;
36146         }
36147     },
36148     
36149     isValidDropPoint : function(n, pt, dd, e, data){
36150         if(!n || !data){ return false; }
36151         var targetNode = n.node;
36152         var dropNode = data.node;
36153         // default drop rules
36154         if(!(targetNode && targetNode.isTarget && pt)){
36155             return false;
36156         }
36157         if(pt == "append" && targetNode.allowChildren === false){
36158             return false;
36159         }
36160         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
36161             return false;
36162         }
36163         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
36164             return false;
36165         }
36166         // reuse the object
36167         var overEvent = this.dragOverData;
36168         overEvent.tree = this.tree;
36169         overEvent.target = targetNode;
36170         overEvent.data = data;
36171         overEvent.point = pt;
36172         overEvent.source = dd;
36173         overEvent.rawEvent = e;
36174         overEvent.dropNode = dropNode;
36175         overEvent.cancel = false;  
36176         var result = this.tree.fireEvent("nodedragover", overEvent);
36177         return overEvent.cancel === false && result !== false;
36178     },
36179     
36180     getDropPoint : function(e, n, dd)
36181     {
36182         var tn = n.node;
36183         if(tn.isRoot){
36184             return tn.allowChildren !== false ? "append" : false; // always append for root
36185         }
36186         var dragEl = n.ddel;
36187         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
36188         var y = Roo.lib.Event.getPageY(e);
36189         //var noAppend = tn.allowChildren === false || tn.isLeaf();
36190         
36191         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
36192         var noAppend = tn.allowChildren === false;
36193         if(this.appendOnly || tn.parentNode.allowChildren === false){
36194             return noAppend ? false : "append";
36195         }
36196         var noBelow = false;
36197         if(!this.allowParentInsert){
36198             noBelow = tn.hasChildNodes() && tn.isExpanded();
36199         }
36200         var q = (b - t) / (noAppend ? 2 : 3);
36201         if(y >= t && y < (t + q)){
36202             return "above";
36203         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
36204             return "below";
36205         }else{
36206             return "append";
36207         }
36208     },
36209     
36210     onNodeEnter : function(n, dd, e, data)
36211     {
36212         this.cancelExpand();
36213     },
36214     
36215     onNodeOver : function(n, dd, e, data)
36216     {
36217        
36218         var pt = this.getDropPoint(e, n, dd);
36219         var node = n.node;
36220         
36221         // auto node expand check
36222         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
36223             this.queueExpand(node);
36224         }else if(pt != "append"){
36225             this.cancelExpand();
36226         }
36227         
36228         // set the insert point style on the target node
36229         var returnCls = this.dropNotAllowed;
36230         if(this.isValidDropPoint(n, pt, dd, e, data)){
36231            if(pt){
36232                var el = n.ddel;
36233                var cls;
36234                if(pt == "above"){
36235                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
36236                    cls = "x-tree-drag-insert-above";
36237                }else if(pt == "below"){
36238                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
36239                    cls = "x-tree-drag-insert-below";
36240                }else{
36241                    returnCls = "x-tree-drop-ok-append";
36242                    cls = "x-tree-drag-append";
36243                }
36244                if(this.lastInsertClass != cls){
36245                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
36246                    this.lastInsertClass = cls;
36247                }
36248            }
36249        }
36250        return returnCls;
36251     },
36252     
36253     onNodeOut : function(n, dd, e, data){
36254         
36255         this.cancelExpand();
36256         this.removeDropIndicators(n);
36257     },
36258     
36259     onNodeDrop : function(n, dd, e, data){
36260         var point = this.getDropPoint(e, n, dd);
36261         var targetNode = n.node;
36262         targetNode.ui.startDrop();
36263         if(!this.isValidDropPoint(n, point, dd, e, data)){
36264             targetNode.ui.endDrop();
36265             return false;
36266         }
36267         // first try to find the drop node
36268         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
36269         var dropEvent = {
36270             tree : this.tree,
36271             target: targetNode,
36272             data: data,
36273             point: point,
36274             source: dd,
36275             rawEvent: e,
36276             dropNode: dropNode,
36277             cancel: !dropNode   
36278         };
36279         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
36280         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
36281             targetNode.ui.endDrop();
36282             return false;
36283         }
36284         // allow target changing
36285         targetNode = dropEvent.target;
36286         if(point == "append" && !targetNode.isExpanded()){
36287             targetNode.expand(false, null, function(){
36288                 this.completeDrop(dropEvent);
36289             }.createDelegate(this));
36290         }else{
36291             this.completeDrop(dropEvent);
36292         }
36293         return true;
36294     },
36295     
36296     completeDrop : function(de){
36297         var ns = de.dropNode, p = de.point, t = de.target;
36298         if(!(ns instanceof Array)){
36299             ns = [ns];
36300         }
36301         var n;
36302         for(var i = 0, len = ns.length; i < len; i++){
36303             n = ns[i];
36304             if(p == "above"){
36305                 t.parentNode.insertBefore(n, t);
36306             }else if(p == "below"){
36307                 t.parentNode.insertBefore(n, t.nextSibling);
36308             }else{
36309                 t.appendChild(n);
36310             }
36311         }
36312         n.ui.focus();
36313         if(this.tree.hlDrop){
36314             n.ui.highlight();
36315         }
36316         t.ui.endDrop();
36317         this.tree.fireEvent("nodedrop", de);
36318     },
36319     
36320     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
36321         if(this.tree.hlDrop){
36322             dropNode.ui.focus();
36323             dropNode.ui.highlight();
36324         }
36325         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
36326     },
36327     
36328     getTree : function(){
36329         return this.tree;
36330     },
36331     
36332     removeDropIndicators : function(n){
36333         if(n && n.ddel){
36334             var el = n.ddel;
36335             Roo.fly(el).removeClass([
36336                     "x-tree-drag-insert-above",
36337                     "x-tree-drag-insert-below",
36338                     "x-tree-drag-append"]);
36339             this.lastInsertClass = "_noclass";
36340         }
36341     },
36342     
36343     beforeDragDrop : function(target, e, id){
36344         this.cancelExpand();
36345         return true;
36346     },
36347     
36348     afterRepair : function(data){
36349         if(data && Roo.enableFx){
36350             data.node.ui.highlight();
36351         }
36352         this.hideProxy();
36353     } 
36354     
36355 });
36356
36357 }
36358 /*
36359  * Based on:
36360  * Ext JS Library 1.1.1
36361  * Copyright(c) 2006-2007, Ext JS, LLC.
36362  *
36363  * Originally Released Under LGPL - original licence link has changed is not relivant.
36364  *
36365  * Fork - LGPL
36366  * <script type="text/javascript">
36367  */
36368  
36369
36370 if(Roo.dd.DragZone){
36371 Roo.tree.TreeDragZone = function(tree, config){
36372     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
36373     this.tree = tree;
36374 };
36375
36376 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
36377     ddGroup : "TreeDD",
36378    
36379     onBeforeDrag : function(data, e){
36380         var n = data.node;
36381         return n && n.draggable && !n.disabled;
36382     },
36383      
36384     
36385     onInitDrag : function(e){
36386         var data = this.dragData;
36387         this.tree.getSelectionModel().select(data.node);
36388         this.proxy.update("");
36389         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
36390         this.tree.fireEvent("startdrag", this.tree, data.node, e);
36391     },
36392     
36393     getRepairXY : function(e, data){
36394         return data.node.ui.getDDRepairXY();
36395     },
36396     
36397     onEndDrag : function(data, e){
36398         this.tree.fireEvent("enddrag", this.tree, data.node, e);
36399         
36400         
36401     },
36402     
36403     onValidDrop : function(dd, e, id){
36404         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
36405         this.hideProxy();
36406     },
36407     
36408     beforeInvalidDrop : function(e, id){
36409         // this scrolls the original position back into view
36410         var sm = this.tree.getSelectionModel();
36411         sm.clearSelections();
36412         sm.select(this.dragData.node);
36413     }
36414 });
36415 }/*
36416  * Based on:
36417  * Ext JS Library 1.1.1
36418  * Copyright(c) 2006-2007, Ext JS, LLC.
36419  *
36420  * Originally Released Under LGPL - original licence link has changed is not relivant.
36421  *
36422  * Fork - LGPL
36423  * <script type="text/javascript">
36424  */
36425 /**
36426  * @class Roo.tree.TreeEditor
36427  * @extends Roo.Editor
36428  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
36429  * as the editor field.
36430  * @constructor
36431  * @param {Object} config (used to be the tree panel.)
36432  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
36433  * 
36434  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
36435  * @cfg {Roo.form.TextField|Object} field The field configuration
36436  *
36437  * 
36438  */
36439 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
36440     var tree = config;
36441     var field;
36442     if (oldconfig) { // old style..
36443         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
36444     } else {
36445         // new style..
36446         tree = config.tree;
36447         config.field = config.field  || {};
36448         config.field.xtype = 'TextField';
36449         field = Roo.factory(config.field, Roo.form);
36450     }
36451     config = config || {};
36452     
36453     
36454     this.addEvents({
36455         /**
36456          * @event beforenodeedit
36457          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
36458          * false from the handler of this event.
36459          * @param {Editor} this
36460          * @param {Roo.tree.Node} node 
36461          */
36462         "beforenodeedit" : true
36463     });
36464     
36465     //Roo.log(config);
36466     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
36467
36468     this.tree = tree;
36469
36470     tree.on('beforeclick', this.beforeNodeClick, this);
36471     tree.getTreeEl().on('mousedown', this.hide, this);
36472     this.on('complete', this.updateNode, this);
36473     this.on('beforestartedit', this.fitToTree, this);
36474     this.on('startedit', this.bindScroll, this, {delay:10});
36475     this.on('specialkey', this.onSpecialKey, this);
36476 };
36477
36478 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
36479     /**
36480      * @cfg {String} alignment
36481      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
36482      */
36483     alignment: "l-l",
36484     // inherit
36485     autoSize: false,
36486     /**
36487      * @cfg {Boolean} hideEl
36488      * True to hide the bound element while the editor is displayed (defaults to false)
36489      */
36490     hideEl : false,
36491     /**
36492      * @cfg {String} cls
36493      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
36494      */
36495     cls: "x-small-editor x-tree-editor",
36496     /**
36497      * @cfg {Boolean} shim
36498      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
36499      */
36500     shim:false,
36501     // inherit
36502     shadow:"frame",
36503     /**
36504      * @cfg {Number} maxWidth
36505      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
36506      * the containing tree element's size, it will be automatically limited for you to the container width, taking
36507      * scroll and client offsets into account prior to each edit.
36508      */
36509     maxWidth: 250,
36510
36511     editDelay : 350,
36512
36513     // private
36514     fitToTree : function(ed, el){
36515         var td = this.tree.getTreeEl().dom, nd = el.dom;
36516         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
36517             td.scrollLeft = nd.offsetLeft;
36518         }
36519         var w = Math.min(
36520                 this.maxWidth,
36521                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
36522         this.setSize(w, '');
36523         
36524         return this.fireEvent('beforenodeedit', this, this.editNode);
36525         
36526     },
36527
36528     // private
36529     triggerEdit : function(node){
36530         this.completeEdit();
36531         this.editNode = node;
36532         this.startEdit(node.ui.textNode, node.text);
36533     },
36534
36535     // private
36536     bindScroll : function(){
36537         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
36538     },
36539
36540     // private
36541     beforeNodeClick : function(node, e){
36542         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
36543         this.lastClick = new Date();
36544         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
36545             e.stopEvent();
36546             this.triggerEdit(node);
36547             return false;
36548         }
36549         return true;
36550     },
36551
36552     // private
36553     updateNode : function(ed, value){
36554         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
36555         this.editNode.setText(value);
36556     },
36557
36558     // private
36559     onHide : function(){
36560         Roo.tree.TreeEditor.superclass.onHide.call(this);
36561         if(this.editNode){
36562             this.editNode.ui.focus();
36563         }
36564     },
36565
36566     // private
36567     onSpecialKey : function(field, e){
36568         var k = e.getKey();
36569         if(k == e.ESC){
36570             e.stopEvent();
36571             this.cancelEdit();
36572         }else if(k == e.ENTER && !e.hasModifier()){
36573             e.stopEvent();
36574             this.completeEdit();
36575         }
36576     }
36577 });//<Script type="text/javascript">
36578 /*
36579  * Based on:
36580  * Ext JS Library 1.1.1
36581  * Copyright(c) 2006-2007, Ext JS, LLC.
36582  *
36583  * Originally Released Under LGPL - original licence link has changed is not relivant.
36584  *
36585  * Fork - LGPL
36586  * <script type="text/javascript">
36587  */
36588  
36589 /**
36590  * Not documented??? - probably should be...
36591  */
36592
36593 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
36594     //focus: Roo.emptyFn, // prevent odd scrolling behavior
36595     
36596     renderElements : function(n, a, targetNode, bulkRender){
36597         //consel.log("renderElements?");
36598         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
36599
36600         var t = n.getOwnerTree();
36601         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
36602         
36603         var cols = t.columns;
36604         var bw = t.borderWidth;
36605         var c = cols[0];
36606         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
36607          var cb = typeof a.checked == "boolean";
36608         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
36609         var colcls = 'x-t-' + tid + '-c0';
36610         var buf = [
36611             '<li class="x-tree-node">',
36612             
36613                 
36614                 '<div class="x-tree-node-el ', a.cls,'">',
36615                     // extran...
36616                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
36617                 
36618                 
36619                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
36620                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
36621                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
36622                            (a.icon ? ' x-tree-node-inline-icon' : ''),
36623                            (a.iconCls ? ' '+a.iconCls : ''),
36624                            '" unselectable="on" />',
36625                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
36626                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
36627                              
36628                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
36629                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
36630                             '<span unselectable="on" qtip="' + tx + '">',
36631                              tx,
36632                              '</span></a>' ,
36633                     '</div>',
36634                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
36635                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
36636                  ];
36637         for(var i = 1, len = cols.length; i < len; i++){
36638             c = cols[i];
36639             colcls = 'x-t-' + tid + '-c' +i;
36640             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
36641             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
36642                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
36643                       "</div>");
36644          }
36645          
36646          buf.push(
36647             '</a>',
36648             '<div class="x-clear"></div></div>',
36649             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
36650             "</li>");
36651         
36652         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
36653             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
36654                                 n.nextSibling.ui.getEl(), buf.join(""));
36655         }else{
36656             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
36657         }
36658         var el = this.wrap.firstChild;
36659         this.elRow = el;
36660         this.elNode = el.firstChild;
36661         this.ranchor = el.childNodes[1];
36662         this.ctNode = this.wrap.childNodes[1];
36663         var cs = el.firstChild.childNodes;
36664         this.indentNode = cs[0];
36665         this.ecNode = cs[1];
36666         this.iconNode = cs[2];
36667         var index = 3;
36668         if(cb){
36669             this.checkbox = cs[3];
36670             index++;
36671         }
36672         this.anchor = cs[index];
36673         
36674         this.textNode = cs[index].firstChild;
36675         
36676         //el.on("click", this.onClick, this);
36677         //el.on("dblclick", this.onDblClick, this);
36678         
36679         
36680        // console.log(this);
36681     },
36682     initEvents : function(){
36683         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
36684         
36685             
36686         var a = this.ranchor;
36687
36688         var el = Roo.get(a);
36689
36690         if(Roo.isOpera){ // opera render bug ignores the CSS
36691             el.setStyle("text-decoration", "none");
36692         }
36693
36694         el.on("click", this.onClick, this);
36695         el.on("dblclick", this.onDblClick, this);
36696         el.on("contextmenu", this.onContextMenu, this);
36697         
36698     },
36699     
36700     /*onSelectedChange : function(state){
36701         if(state){
36702             this.focus();
36703             this.addClass("x-tree-selected");
36704         }else{
36705             //this.blur();
36706             this.removeClass("x-tree-selected");
36707         }
36708     },*/
36709     addClass : function(cls){
36710         if(this.elRow){
36711             Roo.fly(this.elRow).addClass(cls);
36712         }
36713         
36714     },
36715     
36716     
36717     removeClass : function(cls){
36718         if(this.elRow){
36719             Roo.fly(this.elRow).removeClass(cls);
36720         }
36721     }
36722
36723     
36724     
36725 });//<Script type="text/javascript">
36726
36727 /*
36728  * Based on:
36729  * Ext JS Library 1.1.1
36730  * Copyright(c) 2006-2007, Ext JS, LLC.
36731  *
36732  * Originally Released Under LGPL - original licence link has changed is not relivant.
36733  *
36734  * Fork - LGPL
36735  * <script type="text/javascript">
36736  */
36737  
36738
36739 /**
36740  * @class Roo.tree.ColumnTree
36741  * @extends Roo.data.TreePanel
36742  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
36743  * @cfg {int} borderWidth  compined right/left border allowance
36744  * @constructor
36745  * @param {String/HTMLElement/Element} el The container element
36746  * @param {Object} config
36747  */
36748 Roo.tree.ColumnTree =  function(el, config)
36749 {
36750    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
36751    this.addEvents({
36752         /**
36753         * @event resize
36754         * Fire this event on a container when it resizes
36755         * @param {int} w Width
36756         * @param {int} h Height
36757         */
36758        "resize" : true
36759     });
36760     this.on('resize', this.onResize, this);
36761 };
36762
36763 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
36764     //lines:false,
36765     
36766     
36767     borderWidth: Roo.isBorderBox ? 0 : 2, 
36768     headEls : false,
36769     
36770     render : function(){
36771         // add the header.....
36772        
36773         Roo.tree.ColumnTree.superclass.render.apply(this);
36774         
36775         this.el.addClass('x-column-tree');
36776         
36777         this.headers = this.el.createChild(
36778             {cls:'x-tree-headers'},this.innerCt.dom);
36779    
36780         var cols = this.columns, c;
36781         var totalWidth = 0;
36782         this.headEls = [];
36783         var  len = cols.length;
36784         for(var i = 0; i < len; i++){
36785              c = cols[i];
36786              totalWidth += c.width;
36787             this.headEls.push(this.headers.createChild({
36788                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
36789                  cn: {
36790                      cls:'x-tree-hd-text',
36791                      html: c.header
36792                  },
36793                  style:'width:'+(c.width-this.borderWidth)+'px;'
36794              }));
36795         }
36796         this.headers.createChild({cls:'x-clear'});
36797         // prevent floats from wrapping when clipped
36798         this.headers.setWidth(totalWidth);
36799         //this.innerCt.setWidth(totalWidth);
36800         this.innerCt.setStyle({ overflow: 'auto' });
36801         this.onResize(this.width, this.height);
36802              
36803         
36804     },
36805     onResize : function(w,h)
36806     {
36807         this.height = h;
36808         this.width = w;
36809         // resize cols..
36810         this.innerCt.setWidth(this.width);
36811         this.innerCt.setHeight(this.height-20);
36812         
36813         // headers...
36814         var cols = this.columns, c;
36815         var totalWidth = 0;
36816         var expEl = false;
36817         var len = cols.length;
36818         for(var i = 0; i < len; i++){
36819             c = cols[i];
36820             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
36821                 // it's the expander..
36822                 expEl  = this.headEls[i];
36823                 continue;
36824             }
36825             totalWidth += c.width;
36826             
36827         }
36828         if (expEl) {
36829             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
36830         }
36831         this.headers.setWidth(w-20);
36832
36833         
36834         
36835         
36836     }
36837 });
36838 /*
36839  * Based on:
36840  * Ext JS Library 1.1.1
36841  * Copyright(c) 2006-2007, Ext JS, LLC.
36842  *
36843  * Originally Released Under LGPL - original licence link has changed is not relivant.
36844  *
36845  * Fork - LGPL
36846  * <script type="text/javascript">
36847  */
36848  
36849 /**
36850  * @class Roo.menu.Menu
36851  * @extends Roo.util.Observable
36852  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
36853  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
36854  * @constructor
36855  * Creates a new Menu
36856  * @param {Object} config Configuration options
36857  */
36858 Roo.menu.Menu = function(config){
36859     
36860     Roo.menu.Menu.superclass.constructor.call(this, config);
36861     
36862     this.id = this.id || Roo.id();
36863     this.addEvents({
36864         /**
36865          * @event beforeshow
36866          * Fires before this menu is displayed
36867          * @param {Roo.menu.Menu} this
36868          */
36869         beforeshow : true,
36870         /**
36871          * @event beforehide
36872          * Fires before this menu is hidden
36873          * @param {Roo.menu.Menu} this
36874          */
36875         beforehide : true,
36876         /**
36877          * @event show
36878          * Fires after this menu is displayed
36879          * @param {Roo.menu.Menu} this
36880          */
36881         show : true,
36882         /**
36883          * @event hide
36884          * Fires after this menu is hidden
36885          * @param {Roo.menu.Menu} this
36886          */
36887         hide : true,
36888         /**
36889          * @event click
36890          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
36891          * @param {Roo.menu.Menu} this
36892          * @param {Roo.menu.Item} menuItem The menu item that was clicked
36893          * @param {Roo.EventObject} e
36894          */
36895         click : true,
36896         /**
36897          * @event mouseover
36898          * Fires when the mouse is hovering over this menu
36899          * @param {Roo.menu.Menu} this
36900          * @param {Roo.EventObject} e
36901          * @param {Roo.menu.Item} menuItem The menu item that was clicked
36902          */
36903         mouseover : true,
36904         /**
36905          * @event mouseout
36906          * Fires when the mouse exits this menu
36907          * @param {Roo.menu.Menu} this
36908          * @param {Roo.EventObject} e
36909          * @param {Roo.menu.Item} menuItem The menu item that was clicked
36910          */
36911         mouseout : true,
36912         /**
36913          * @event itemclick
36914          * Fires when a menu item contained in this menu is clicked
36915          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
36916          * @param {Roo.EventObject} e
36917          */
36918         itemclick: true
36919     });
36920     if (this.registerMenu) {
36921         Roo.menu.MenuMgr.register(this);
36922     }
36923     
36924     var mis = this.items;
36925     this.items = new Roo.util.MixedCollection();
36926     if(mis){
36927         this.add.apply(this, mis);
36928     }
36929 };
36930
36931 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
36932     /**
36933      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
36934      */
36935     minWidth : 120,
36936     /**
36937      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
36938      * for bottom-right shadow (defaults to "sides")
36939      */
36940     shadow : "sides",
36941     /**
36942      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
36943      * this menu (defaults to "tl-tr?")
36944      */
36945     subMenuAlign : "tl-tr?",
36946     /**
36947      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
36948      * relative to its element of origin (defaults to "tl-bl?")
36949      */
36950     defaultAlign : "tl-bl?",
36951     /**
36952      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
36953      */
36954     allowOtherMenus : false,
36955     /**
36956      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
36957      */
36958     registerMenu : true,
36959
36960     hidden:true,
36961
36962     // private
36963     render : function(){
36964         if(this.el){
36965             return;
36966         }
36967         var el = this.el = new Roo.Layer({
36968             cls: "x-menu",
36969             shadow:this.shadow,
36970             constrain: false,
36971             parentEl: this.parentEl || document.body,
36972             zindex:15000
36973         });
36974
36975         this.keyNav = new Roo.menu.MenuNav(this);
36976
36977         if(this.plain){
36978             el.addClass("x-menu-plain");
36979         }
36980         if(this.cls){
36981             el.addClass(this.cls);
36982         }
36983         // generic focus element
36984         this.focusEl = el.createChild({
36985             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
36986         });
36987         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
36988         //disabling touch- as it's causing issues ..
36989         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
36990         ul.on('click'   , this.onClick, this);
36991         
36992         
36993         ul.on("mouseover", this.onMouseOver, this);
36994         ul.on("mouseout", this.onMouseOut, this);
36995         this.items.each(function(item){
36996             if (item.hidden) {
36997                 return;
36998             }
36999             
37000             var li = document.createElement("li");
37001             li.className = "x-menu-list-item";
37002             ul.dom.appendChild(li);
37003             item.render(li, this);
37004         }, this);
37005         this.ul = ul;
37006         this.autoWidth();
37007     },
37008
37009     // private
37010     autoWidth : function(){
37011         var el = this.el, ul = this.ul;
37012         if(!el){
37013             return;
37014         }
37015         var w = this.width;
37016         if(w){
37017             el.setWidth(w);
37018         }else if(Roo.isIE){
37019             el.setWidth(this.minWidth);
37020             var t = el.dom.offsetWidth; // force recalc
37021             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
37022         }
37023     },
37024
37025     // private
37026     delayAutoWidth : function(){
37027         if(this.rendered){
37028             if(!this.awTask){
37029                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
37030             }
37031             this.awTask.delay(20);
37032         }
37033     },
37034
37035     // private
37036     findTargetItem : function(e){
37037         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
37038         if(t && t.menuItemId){
37039             return this.items.get(t.menuItemId);
37040         }
37041     },
37042
37043     // private
37044     onClick : function(e){
37045         Roo.log("menu.onClick");
37046         var t = this.findTargetItem(e);
37047         if(!t){
37048             return;
37049         }
37050         Roo.log(e);
37051         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
37052             if(t == this.activeItem && t.shouldDeactivate(e)){
37053                 this.activeItem.deactivate();
37054                 delete this.activeItem;
37055                 return;
37056             }
37057             if(t.canActivate){
37058                 this.setActiveItem(t, true);
37059             }
37060             return;
37061             
37062             
37063         }
37064         
37065         t.onClick(e);
37066         this.fireEvent("click", this, t, e);
37067     },
37068
37069     // private
37070     setActiveItem : function(item, autoExpand){
37071         if(item != this.activeItem){
37072             if(this.activeItem){
37073                 this.activeItem.deactivate();
37074             }
37075             this.activeItem = item;
37076             item.activate(autoExpand);
37077         }else if(autoExpand){
37078             item.expandMenu();
37079         }
37080     },
37081
37082     // private
37083     tryActivate : function(start, step){
37084         var items = this.items;
37085         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
37086             var item = items.get(i);
37087             if(!item.disabled && item.canActivate){
37088                 this.setActiveItem(item, false);
37089                 return item;
37090             }
37091         }
37092         return false;
37093     },
37094
37095     // private
37096     onMouseOver : function(e){
37097         var t;
37098         if(t = this.findTargetItem(e)){
37099             if(t.canActivate && !t.disabled){
37100                 this.setActiveItem(t, true);
37101             }
37102         }
37103         this.fireEvent("mouseover", this, e, t);
37104     },
37105
37106     // private
37107     onMouseOut : function(e){
37108         var t;
37109         if(t = this.findTargetItem(e)){
37110             if(t == this.activeItem && t.shouldDeactivate(e)){
37111                 this.activeItem.deactivate();
37112                 delete this.activeItem;
37113             }
37114         }
37115         this.fireEvent("mouseout", this, e, t);
37116     },
37117
37118     /**
37119      * Read-only.  Returns true if the menu is currently displayed, else false.
37120      * @type Boolean
37121      */
37122     isVisible : function(){
37123         return this.el && !this.hidden;
37124     },
37125
37126     /**
37127      * Displays this menu relative to another element
37128      * @param {String/HTMLElement/Roo.Element} element The element to align to
37129      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
37130      * the element (defaults to this.defaultAlign)
37131      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37132      */
37133     show : function(el, pos, parentMenu){
37134         this.parentMenu = parentMenu;
37135         if(!this.el){
37136             this.render();
37137         }
37138         this.fireEvent("beforeshow", this);
37139         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
37140     },
37141
37142     /**
37143      * Displays this menu at a specific xy position
37144      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
37145      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37146      */
37147     showAt : function(xy, parentMenu, /* private: */_e){
37148         this.parentMenu = parentMenu;
37149         if(!this.el){
37150             this.render();
37151         }
37152         if(_e !== false){
37153             this.fireEvent("beforeshow", this);
37154             xy = this.el.adjustForConstraints(xy);
37155         }
37156         this.el.setXY(xy);
37157         this.el.show();
37158         this.hidden = false;
37159         this.focus();
37160         this.fireEvent("show", this);
37161     },
37162
37163     focus : function(){
37164         if(!this.hidden){
37165             this.doFocus.defer(50, this);
37166         }
37167     },
37168
37169     doFocus : function(){
37170         if(!this.hidden){
37171             this.focusEl.focus();
37172         }
37173     },
37174
37175     /**
37176      * Hides this menu and optionally all parent menus
37177      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
37178      */
37179     hide : function(deep){
37180         if(this.el && this.isVisible()){
37181             this.fireEvent("beforehide", this);
37182             if(this.activeItem){
37183                 this.activeItem.deactivate();
37184                 this.activeItem = null;
37185             }
37186             this.el.hide();
37187             this.hidden = true;
37188             this.fireEvent("hide", this);
37189         }
37190         if(deep === true && this.parentMenu){
37191             this.parentMenu.hide(true);
37192         }
37193     },
37194
37195     /**
37196      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
37197      * Any of the following are valid:
37198      * <ul>
37199      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
37200      * <li>An HTMLElement object which will be converted to a menu item</li>
37201      * <li>A menu item config object that will be created as a new menu item</li>
37202      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
37203      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
37204      * </ul>
37205      * Usage:
37206      * <pre><code>
37207 // Create the menu
37208 var menu = new Roo.menu.Menu();
37209
37210 // Create a menu item to add by reference
37211 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
37212
37213 // Add a bunch of items at once using different methods.
37214 // Only the last item added will be returned.
37215 var item = menu.add(
37216     menuItem,                // add existing item by ref
37217     'Dynamic Item',          // new TextItem
37218     '-',                     // new separator
37219     { text: 'Config Item' }  // new item by config
37220 );
37221 </code></pre>
37222      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
37223      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
37224      */
37225     add : function(){
37226         var a = arguments, l = a.length, item;
37227         for(var i = 0; i < l; i++){
37228             var el = a[i];
37229             if ((typeof(el) == "object") && el.xtype && el.xns) {
37230                 el = Roo.factory(el, Roo.menu);
37231             }
37232             
37233             if(el.render){ // some kind of Item
37234                 item = this.addItem(el);
37235             }else if(typeof el == "string"){ // string
37236                 if(el == "separator" || el == "-"){
37237                     item = this.addSeparator();
37238                 }else{
37239                     item = this.addText(el);
37240                 }
37241             }else if(el.tagName || el.el){ // element
37242                 item = this.addElement(el);
37243             }else if(typeof el == "object"){ // must be menu item config?
37244                 item = this.addMenuItem(el);
37245             }
37246         }
37247         return item;
37248     },
37249
37250     /**
37251      * Returns this menu's underlying {@link Roo.Element} object
37252      * @return {Roo.Element} The element
37253      */
37254     getEl : function(){
37255         if(!this.el){
37256             this.render();
37257         }
37258         return this.el;
37259     },
37260
37261     /**
37262      * Adds a separator bar to the menu
37263      * @return {Roo.menu.Item} The menu item that was added
37264      */
37265     addSeparator : function(){
37266         return this.addItem(new Roo.menu.Separator());
37267     },
37268
37269     /**
37270      * Adds an {@link Roo.Element} object to the menu
37271      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
37272      * @return {Roo.menu.Item} The menu item that was added
37273      */
37274     addElement : function(el){
37275         return this.addItem(new Roo.menu.BaseItem(el));
37276     },
37277
37278     /**
37279      * Adds an existing object based on {@link Roo.menu.Item} to the menu
37280      * @param {Roo.menu.Item} item The menu item to add
37281      * @return {Roo.menu.Item} The menu item that was added
37282      */
37283     addItem : function(item){
37284         this.items.add(item);
37285         if(this.ul){
37286             var li = document.createElement("li");
37287             li.className = "x-menu-list-item";
37288             this.ul.dom.appendChild(li);
37289             item.render(li, this);
37290             this.delayAutoWidth();
37291         }
37292         return item;
37293     },
37294
37295     /**
37296      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
37297      * @param {Object} config A MenuItem config object
37298      * @return {Roo.menu.Item} The menu item that was added
37299      */
37300     addMenuItem : function(config){
37301         if(!(config instanceof Roo.menu.Item)){
37302             if(typeof config.checked == "boolean"){ // must be check menu item config?
37303                 config = new Roo.menu.CheckItem(config);
37304             }else{
37305                 config = new Roo.menu.Item(config);
37306             }
37307         }
37308         return this.addItem(config);
37309     },
37310
37311     /**
37312      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
37313      * @param {String} text The text to display in the menu item
37314      * @return {Roo.menu.Item} The menu item that was added
37315      */
37316     addText : function(text){
37317         return this.addItem(new Roo.menu.TextItem({ text : text }));
37318     },
37319
37320     /**
37321      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
37322      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
37323      * @param {Roo.menu.Item} item The menu item to add
37324      * @return {Roo.menu.Item} The menu item that was added
37325      */
37326     insert : function(index, item){
37327         this.items.insert(index, item);
37328         if(this.ul){
37329             var li = document.createElement("li");
37330             li.className = "x-menu-list-item";
37331             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
37332             item.render(li, this);
37333             this.delayAutoWidth();
37334         }
37335         return item;
37336     },
37337
37338     /**
37339      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
37340      * @param {Roo.menu.Item} item The menu item to remove
37341      */
37342     remove : function(item){
37343         this.items.removeKey(item.id);
37344         item.destroy();
37345     },
37346
37347     /**
37348      * Removes and destroys all items in the menu
37349      */
37350     removeAll : function(){
37351         var f;
37352         while(f = this.items.first()){
37353             this.remove(f);
37354         }
37355     }
37356 });
37357
37358 // MenuNav is a private utility class used internally by the Menu
37359 Roo.menu.MenuNav = function(menu){
37360     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
37361     this.scope = this.menu = menu;
37362 };
37363
37364 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
37365     doRelay : function(e, h){
37366         var k = e.getKey();
37367         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
37368             this.menu.tryActivate(0, 1);
37369             return false;
37370         }
37371         return h.call(this.scope || this, e, this.menu);
37372     },
37373
37374     up : function(e, m){
37375         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
37376             m.tryActivate(m.items.length-1, -1);
37377         }
37378     },
37379
37380     down : function(e, m){
37381         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
37382             m.tryActivate(0, 1);
37383         }
37384     },
37385
37386     right : function(e, m){
37387         if(m.activeItem){
37388             m.activeItem.expandMenu(true);
37389         }
37390     },
37391
37392     left : function(e, m){
37393         m.hide();
37394         if(m.parentMenu && m.parentMenu.activeItem){
37395             m.parentMenu.activeItem.activate();
37396         }
37397     },
37398
37399     enter : function(e, m){
37400         if(m.activeItem){
37401             e.stopPropagation();
37402             m.activeItem.onClick(e);
37403             m.fireEvent("click", this, m.activeItem);
37404             return true;
37405         }
37406     }
37407 });/*
37408  * Based on:
37409  * Ext JS Library 1.1.1
37410  * Copyright(c) 2006-2007, Ext JS, LLC.
37411  *
37412  * Originally Released Under LGPL - original licence link has changed is not relivant.
37413  *
37414  * Fork - LGPL
37415  * <script type="text/javascript">
37416  */
37417  
37418 /**
37419  * @class Roo.menu.MenuMgr
37420  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
37421  * @singleton
37422  */
37423 Roo.menu.MenuMgr = function(){
37424    var menus, active, groups = {}, attached = false, lastShow = new Date();
37425
37426    // private - called when first menu is created
37427    function init(){
37428        menus = {};
37429        active = new Roo.util.MixedCollection();
37430        Roo.get(document).addKeyListener(27, function(){
37431            if(active.length > 0){
37432                hideAll();
37433            }
37434        });
37435    }
37436
37437    // private
37438    function hideAll(){
37439        if(active && active.length > 0){
37440            var c = active.clone();
37441            c.each(function(m){
37442                m.hide();
37443            });
37444        }
37445    }
37446
37447    // private
37448    function onHide(m){
37449        active.remove(m);
37450        if(active.length < 1){
37451            Roo.get(document).un("mousedown", onMouseDown);
37452            attached = false;
37453        }
37454    }
37455
37456    // private
37457    function onShow(m){
37458        var last = active.last();
37459        lastShow = new Date();
37460        active.add(m);
37461        if(!attached){
37462            Roo.get(document).on("mousedown", onMouseDown);
37463            attached = true;
37464        }
37465        if(m.parentMenu){
37466           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
37467           m.parentMenu.activeChild = m;
37468        }else if(last && last.isVisible()){
37469           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
37470        }
37471    }
37472
37473    // private
37474    function onBeforeHide(m){
37475        if(m.activeChild){
37476            m.activeChild.hide();
37477        }
37478        if(m.autoHideTimer){
37479            clearTimeout(m.autoHideTimer);
37480            delete m.autoHideTimer;
37481        }
37482    }
37483
37484    // private
37485    function onBeforeShow(m){
37486        var pm = m.parentMenu;
37487        if(!pm && !m.allowOtherMenus){
37488            hideAll();
37489        }else if(pm && pm.activeChild && active != m){
37490            pm.activeChild.hide();
37491        }
37492    }
37493
37494    // private
37495    function onMouseDown(e){
37496        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
37497            hideAll();
37498        }
37499    }
37500
37501    // private
37502    function onBeforeCheck(mi, state){
37503        if(state){
37504            var g = groups[mi.group];
37505            for(var i = 0, l = g.length; i < l; i++){
37506                if(g[i] != mi){
37507                    g[i].setChecked(false);
37508                }
37509            }
37510        }
37511    }
37512
37513    return {
37514
37515        /**
37516         * Hides all menus that are currently visible
37517         */
37518        hideAll : function(){
37519             hideAll();  
37520        },
37521
37522        // private
37523        register : function(menu){
37524            if(!menus){
37525                init();
37526            }
37527            menus[menu.id] = menu;
37528            menu.on("beforehide", onBeforeHide);
37529            menu.on("hide", onHide);
37530            menu.on("beforeshow", onBeforeShow);
37531            menu.on("show", onShow);
37532            var g = menu.group;
37533            if(g && menu.events["checkchange"]){
37534                if(!groups[g]){
37535                    groups[g] = [];
37536                }
37537                groups[g].push(menu);
37538                menu.on("checkchange", onCheck);
37539            }
37540        },
37541
37542         /**
37543          * Returns a {@link Roo.menu.Menu} object
37544          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
37545          * be used to generate and return a new Menu instance.
37546          */
37547        get : function(menu){
37548            if(typeof menu == "string"){ // menu id
37549                return menus[menu];
37550            }else if(menu.events){  // menu instance
37551                return menu;
37552            }else if(typeof menu.length == 'number'){ // array of menu items?
37553                return new Roo.menu.Menu({items:menu});
37554            }else{ // otherwise, must be a config
37555                return new Roo.menu.Menu(menu);
37556            }
37557        },
37558
37559        // private
37560        unregister : function(menu){
37561            delete menus[menu.id];
37562            menu.un("beforehide", onBeforeHide);
37563            menu.un("hide", onHide);
37564            menu.un("beforeshow", onBeforeShow);
37565            menu.un("show", onShow);
37566            var g = menu.group;
37567            if(g && menu.events["checkchange"]){
37568                groups[g].remove(menu);
37569                menu.un("checkchange", onCheck);
37570            }
37571        },
37572
37573        // private
37574        registerCheckable : function(menuItem){
37575            var g = menuItem.group;
37576            if(g){
37577                if(!groups[g]){
37578                    groups[g] = [];
37579                }
37580                groups[g].push(menuItem);
37581                menuItem.on("beforecheckchange", onBeforeCheck);
37582            }
37583        },
37584
37585        // private
37586        unregisterCheckable : function(menuItem){
37587            var g = menuItem.group;
37588            if(g){
37589                groups[g].remove(menuItem);
37590                menuItem.un("beforecheckchange", onBeforeCheck);
37591            }
37592        }
37593    };
37594 }();/*
37595  * Based on:
37596  * Ext JS Library 1.1.1
37597  * Copyright(c) 2006-2007, Ext JS, LLC.
37598  *
37599  * Originally Released Under LGPL - original licence link has changed is not relivant.
37600  *
37601  * Fork - LGPL
37602  * <script type="text/javascript">
37603  */
37604  
37605
37606 /**
37607  * @class Roo.menu.BaseItem
37608  * @extends Roo.Component
37609  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
37610  * management and base configuration options shared by all menu components.
37611  * @constructor
37612  * Creates a new BaseItem
37613  * @param {Object} config Configuration options
37614  */
37615 Roo.menu.BaseItem = function(config){
37616     Roo.menu.BaseItem.superclass.constructor.call(this, config);
37617
37618     this.addEvents({
37619         /**
37620          * @event click
37621          * Fires when this item is clicked
37622          * @param {Roo.menu.BaseItem} this
37623          * @param {Roo.EventObject} e
37624          */
37625         click: true,
37626         /**
37627          * @event activate
37628          * Fires when this item is activated
37629          * @param {Roo.menu.BaseItem} this
37630          */
37631         activate : true,
37632         /**
37633          * @event deactivate
37634          * Fires when this item is deactivated
37635          * @param {Roo.menu.BaseItem} this
37636          */
37637         deactivate : true
37638     });
37639
37640     if(this.handler){
37641         this.on("click", this.handler, this.scope, true);
37642     }
37643 };
37644
37645 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
37646     /**
37647      * @cfg {Function} handler
37648      * A function that will handle the click event of this menu item (defaults to undefined)
37649      */
37650     /**
37651      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
37652      */
37653     canActivate : false,
37654     
37655      /**
37656      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
37657      */
37658     hidden: false,
37659     
37660     /**
37661      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
37662      */
37663     activeClass : "x-menu-item-active",
37664     /**
37665      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
37666      */
37667     hideOnClick : true,
37668     /**
37669      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
37670      */
37671     hideDelay : 100,
37672
37673     // private
37674     ctype: "Roo.menu.BaseItem",
37675
37676     // private
37677     actionMode : "container",
37678
37679     // private
37680     render : function(container, parentMenu){
37681         this.parentMenu = parentMenu;
37682         Roo.menu.BaseItem.superclass.render.call(this, container);
37683         this.container.menuItemId = this.id;
37684     },
37685
37686     // private
37687     onRender : function(container, position){
37688         this.el = Roo.get(this.el);
37689         container.dom.appendChild(this.el.dom);
37690     },
37691
37692     // private
37693     onClick : function(e){
37694         if(!this.disabled && this.fireEvent("click", this, e) !== false
37695                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
37696             this.handleClick(e);
37697         }else{
37698             e.stopEvent();
37699         }
37700     },
37701
37702     // private
37703     activate : function(){
37704         if(this.disabled){
37705             return false;
37706         }
37707         var li = this.container;
37708         li.addClass(this.activeClass);
37709         this.region = li.getRegion().adjust(2, 2, -2, -2);
37710         this.fireEvent("activate", this);
37711         return true;
37712     },
37713
37714     // private
37715     deactivate : function(){
37716         this.container.removeClass(this.activeClass);
37717         this.fireEvent("deactivate", this);
37718     },
37719
37720     // private
37721     shouldDeactivate : function(e){
37722         return !this.region || !this.region.contains(e.getPoint());
37723     },
37724
37725     // private
37726     handleClick : function(e){
37727         if(this.hideOnClick){
37728             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
37729         }
37730     },
37731
37732     // private
37733     expandMenu : function(autoActivate){
37734         // do nothing
37735     },
37736
37737     // private
37738     hideMenu : function(){
37739         // do nothing
37740     }
37741 });/*
37742  * Based on:
37743  * Ext JS Library 1.1.1
37744  * Copyright(c) 2006-2007, Ext JS, LLC.
37745  *
37746  * Originally Released Under LGPL - original licence link has changed is not relivant.
37747  *
37748  * Fork - LGPL
37749  * <script type="text/javascript">
37750  */
37751  
37752 /**
37753  * @class Roo.menu.Adapter
37754  * @extends Roo.menu.BaseItem
37755  * 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.
37756  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
37757  * @constructor
37758  * Creates a new Adapter
37759  * @param {Object} config Configuration options
37760  */
37761 Roo.menu.Adapter = function(component, config){
37762     Roo.menu.Adapter.superclass.constructor.call(this, config);
37763     this.component = component;
37764 };
37765 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
37766     // private
37767     canActivate : true,
37768
37769     // private
37770     onRender : function(container, position){
37771         this.component.render(container);
37772         this.el = this.component.getEl();
37773     },
37774
37775     // private
37776     activate : function(){
37777         if(this.disabled){
37778             return false;
37779         }
37780         this.component.focus();
37781         this.fireEvent("activate", this);
37782         return true;
37783     },
37784
37785     // private
37786     deactivate : function(){
37787         this.fireEvent("deactivate", this);
37788     },
37789
37790     // private
37791     disable : function(){
37792         this.component.disable();
37793         Roo.menu.Adapter.superclass.disable.call(this);
37794     },
37795
37796     // private
37797     enable : function(){
37798         this.component.enable();
37799         Roo.menu.Adapter.superclass.enable.call(this);
37800     }
37801 });/*
37802  * Based on:
37803  * Ext JS Library 1.1.1
37804  * Copyright(c) 2006-2007, Ext JS, LLC.
37805  *
37806  * Originally Released Under LGPL - original licence link has changed is not relivant.
37807  *
37808  * Fork - LGPL
37809  * <script type="text/javascript">
37810  */
37811
37812 /**
37813  * @class Roo.menu.TextItem
37814  * @extends Roo.menu.BaseItem
37815  * Adds a static text string to a menu, usually used as either a heading or group separator.
37816  * Note: old style constructor with text is still supported.
37817  * 
37818  * @constructor
37819  * Creates a new TextItem
37820  * @param {Object} cfg Configuration
37821  */
37822 Roo.menu.TextItem = function(cfg){
37823     if (typeof(cfg) == 'string') {
37824         this.text = cfg;
37825     } else {
37826         Roo.apply(this,cfg);
37827     }
37828     
37829     Roo.menu.TextItem.superclass.constructor.call(this);
37830 };
37831
37832 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
37833     /**
37834      * @cfg {Boolean} text Text to show on item.
37835      */
37836     text : '',
37837     
37838     /**
37839      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
37840      */
37841     hideOnClick : false,
37842     /**
37843      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
37844      */
37845     itemCls : "x-menu-text",
37846
37847     // private
37848     onRender : function(){
37849         var s = document.createElement("span");
37850         s.className = this.itemCls;
37851         s.innerHTML = this.text;
37852         this.el = s;
37853         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
37854     }
37855 });/*
37856  * Based on:
37857  * Ext JS Library 1.1.1
37858  * Copyright(c) 2006-2007, Ext JS, LLC.
37859  *
37860  * Originally Released Under LGPL - original licence link has changed is not relivant.
37861  *
37862  * Fork - LGPL
37863  * <script type="text/javascript">
37864  */
37865
37866 /**
37867  * @class Roo.menu.Separator
37868  * @extends Roo.menu.BaseItem
37869  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
37870  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
37871  * @constructor
37872  * @param {Object} config Configuration options
37873  */
37874 Roo.menu.Separator = function(config){
37875     Roo.menu.Separator.superclass.constructor.call(this, config);
37876 };
37877
37878 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
37879     /**
37880      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
37881      */
37882     itemCls : "x-menu-sep",
37883     /**
37884      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
37885      */
37886     hideOnClick : false,
37887
37888     // private
37889     onRender : function(li){
37890         var s = document.createElement("span");
37891         s.className = this.itemCls;
37892         s.innerHTML = "&#160;";
37893         this.el = s;
37894         li.addClass("x-menu-sep-li");
37895         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
37896     }
37897 });/*
37898  * Based on:
37899  * Ext JS Library 1.1.1
37900  * Copyright(c) 2006-2007, Ext JS, LLC.
37901  *
37902  * Originally Released Under LGPL - original licence link has changed is not relivant.
37903  *
37904  * Fork - LGPL
37905  * <script type="text/javascript">
37906  */
37907 /**
37908  * @class Roo.menu.Item
37909  * @extends Roo.menu.BaseItem
37910  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
37911  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
37912  * activation and click handling.
37913  * @constructor
37914  * Creates a new Item
37915  * @param {Object} config Configuration options
37916  */
37917 Roo.menu.Item = function(config){
37918     Roo.menu.Item.superclass.constructor.call(this, config);
37919     if(this.menu){
37920         this.menu = Roo.menu.MenuMgr.get(this.menu);
37921     }
37922 };
37923 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
37924     
37925     /**
37926      * @cfg {String} text
37927      * The text to show on the menu item.
37928      */
37929     text: '',
37930      /**
37931      * @cfg {String} HTML to render in menu
37932      * The text to show on the menu item (HTML version).
37933      */
37934     html: '',
37935     /**
37936      * @cfg {String} icon
37937      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
37938      */
37939     icon: undefined,
37940     /**
37941      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
37942      */
37943     itemCls : "x-menu-item",
37944     /**
37945      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
37946      */
37947     canActivate : true,
37948     /**
37949      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
37950      */
37951     showDelay: 200,
37952     // doc'd in BaseItem
37953     hideDelay: 200,
37954
37955     // private
37956     ctype: "Roo.menu.Item",
37957     
37958     // private
37959     onRender : function(container, position){
37960         var el = document.createElement("a");
37961         el.hideFocus = true;
37962         el.unselectable = "on";
37963         el.href = this.href || "#";
37964         if(this.hrefTarget){
37965             el.target = this.hrefTarget;
37966         }
37967         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
37968         
37969         var html = this.html.length ? this.html  : String.format('{0}',this.text);
37970         
37971         el.innerHTML = String.format(
37972                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
37973                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
37974         this.el = el;
37975         Roo.menu.Item.superclass.onRender.call(this, container, position);
37976     },
37977
37978     /**
37979      * Sets the text to display in this menu item
37980      * @param {String} text The text to display
37981      * @param {Boolean} isHTML true to indicate text is pure html.
37982      */
37983     setText : function(text, isHTML){
37984         if (isHTML) {
37985             this.html = text;
37986         } else {
37987             this.text = text;
37988             this.html = '';
37989         }
37990         if(this.rendered){
37991             var html = this.html.length ? this.html  : String.format('{0}',this.text);
37992      
37993             this.el.update(String.format(
37994                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
37995                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
37996             this.parentMenu.autoWidth();
37997         }
37998     },
37999
38000     // private
38001     handleClick : function(e){
38002         if(!this.href){ // if no link defined, stop the event automatically
38003             e.stopEvent();
38004         }
38005         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
38006     },
38007
38008     // private
38009     activate : function(autoExpand){
38010         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
38011             this.focus();
38012             if(autoExpand){
38013                 this.expandMenu();
38014             }
38015         }
38016         return true;
38017     },
38018
38019     // private
38020     shouldDeactivate : function(e){
38021         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
38022             if(this.menu && this.menu.isVisible()){
38023                 return !this.menu.getEl().getRegion().contains(e.getPoint());
38024             }
38025             return true;
38026         }
38027         return false;
38028     },
38029
38030     // private
38031     deactivate : function(){
38032         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
38033         this.hideMenu();
38034     },
38035
38036     // private
38037     expandMenu : function(autoActivate){
38038         if(!this.disabled && this.menu){
38039             clearTimeout(this.hideTimer);
38040             delete this.hideTimer;
38041             if(!this.menu.isVisible() && !this.showTimer){
38042                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
38043             }else if (this.menu.isVisible() && autoActivate){
38044                 this.menu.tryActivate(0, 1);
38045             }
38046         }
38047     },
38048
38049     // private
38050     deferExpand : function(autoActivate){
38051         delete this.showTimer;
38052         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
38053         if(autoActivate){
38054             this.menu.tryActivate(0, 1);
38055         }
38056     },
38057
38058     // private
38059     hideMenu : function(){
38060         clearTimeout(this.showTimer);
38061         delete this.showTimer;
38062         if(!this.hideTimer && this.menu && this.menu.isVisible()){
38063             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
38064         }
38065     },
38066
38067     // private
38068     deferHide : function(){
38069         delete this.hideTimer;
38070         this.menu.hide();
38071     }
38072 });/*
38073  * Based on:
38074  * Ext JS Library 1.1.1
38075  * Copyright(c) 2006-2007, Ext JS, LLC.
38076  *
38077  * Originally Released Under LGPL - original licence link has changed is not relivant.
38078  *
38079  * Fork - LGPL
38080  * <script type="text/javascript">
38081  */
38082  
38083 /**
38084  * @class Roo.menu.CheckItem
38085  * @extends Roo.menu.Item
38086  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
38087  * @constructor
38088  * Creates a new CheckItem
38089  * @param {Object} config Configuration options
38090  */
38091 Roo.menu.CheckItem = function(config){
38092     Roo.menu.CheckItem.superclass.constructor.call(this, config);
38093     this.addEvents({
38094         /**
38095          * @event beforecheckchange
38096          * Fires before the checked value is set, providing an opportunity to cancel if needed
38097          * @param {Roo.menu.CheckItem} this
38098          * @param {Boolean} checked The new checked value that will be set
38099          */
38100         "beforecheckchange" : true,
38101         /**
38102          * @event checkchange
38103          * Fires after the checked value has been set
38104          * @param {Roo.menu.CheckItem} this
38105          * @param {Boolean} checked The checked value that was set
38106          */
38107         "checkchange" : true
38108     });
38109     if(this.checkHandler){
38110         this.on('checkchange', this.checkHandler, this.scope);
38111     }
38112 };
38113 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
38114     /**
38115      * @cfg {String} group
38116      * All check items with the same group name will automatically be grouped into a single-select
38117      * radio button group (defaults to '')
38118      */
38119     /**
38120      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
38121      */
38122     itemCls : "x-menu-item x-menu-check-item",
38123     /**
38124      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
38125      */
38126     groupClass : "x-menu-group-item",
38127
38128     /**
38129      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
38130      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
38131      * initialized with checked = true will be rendered as checked.
38132      */
38133     checked: false,
38134
38135     // private
38136     ctype: "Roo.menu.CheckItem",
38137
38138     // private
38139     onRender : function(c){
38140         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
38141         if(this.group){
38142             this.el.addClass(this.groupClass);
38143         }
38144         Roo.menu.MenuMgr.registerCheckable(this);
38145         if(this.checked){
38146             this.checked = false;
38147             this.setChecked(true, true);
38148         }
38149     },
38150
38151     // private
38152     destroy : function(){
38153         if(this.rendered){
38154             Roo.menu.MenuMgr.unregisterCheckable(this);
38155         }
38156         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
38157     },
38158
38159     /**
38160      * Set the checked state of this item
38161      * @param {Boolean} checked The new checked value
38162      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
38163      */
38164     setChecked : function(state, suppressEvent){
38165         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
38166             if(this.container){
38167                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
38168             }
38169             this.checked = state;
38170             if(suppressEvent !== true){
38171                 this.fireEvent("checkchange", this, state);
38172             }
38173         }
38174     },
38175
38176     // private
38177     handleClick : function(e){
38178        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
38179            this.setChecked(!this.checked);
38180        }
38181        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
38182     }
38183 });/*
38184  * Based on:
38185  * Ext JS Library 1.1.1
38186  * Copyright(c) 2006-2007, Ext JS, LLC.
38187  *
38188  * Originally Released Under LGPL - original licence link has changed is not relivant.
38189  *
38190  * Fork - LGPL
38191  * <script type="text/javascript">
38192  */
38193  
38194 /**
38195  * @class Roo.menu.DateItem
38196  * @extends Roo.menu.Adapter
38197  * A menu item that wraps the {@link Roo.DatPicker} component.
38198  * @constructor
38199  * Creates a new DateItem
38200  * @param {Object} config Configuration options
38201  */
38202 Roo.menu.DateItem = function(config){
38203     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
38204     /** The Roo.DatePicker object @type Roo.DatePicker */
38205     this.picker = this.component;
38206     this.addEvents({select: true});
38207     
38208     this.picker.on("render", function(picker){
38209         picker.getEl().swallowEvent("click");
38210         picker.container.addClass("x-menu-date-item");
38211     });
38212
38213     this.picker.on("select", this.onSelect, this);
38214 };
38215
38216 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
38217     // private
38218     onSelect : function(picker, date){
38219         this.fireEvent("select", this, date, picker);
38220         Roo.menu.DateItem.superclass.handleClick.call(this);
38221     }
38222 });/*
38223  * Based on:
38224  * Ext JS Library 1.1.1
38225  * Copyright(c) 2006-2007, Ext JS, LLC.
38226  *
38227  * Originally Released Under LGPL - original licence link has changed is not relivant.
38228  *
38229  * Fork - LGPL
38230  * <script type="text/javascript">
38231  */
38232  
38233 /**
38234  * @class Roo.menu.ColorItem
38235  * @extends Roo.menu.Adapter
38236  * A menu item that wraps the {@link Roo.ColorPalette} component.
38237  * @constructor
38238  * Creates a new ColorItem
38239  * @param {Object} config Configuration options
38240  */
38241 Roo.menu.ColorItem = function(config){
38242     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
38243     /** The Roo.ColorPalette object @type Roo.ColorPalette */
38244     this.palette = this.component;
38245     this.relayEvents(this.palette, ["select"]);
38246     if(this.selectHandler){
38247         this.on('select', this.selectHandler, this.scope);
38248     }
38249 };
38250 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
38251  * Based on:
38252  * Ext JS Library 1.1.1
38253  * Copyright(c) 2006-2007, Ext JS, LLC.
38254  *
38255  * Originally Released Under LGPL - original licence link has changed is not relivant.
38256  *
38257  * Fork - LGPL
38258  * <script type="text/javascript">
38259  */
38260  
38261
38262 /**
38263  * @class Roo.menu.DateMenu
38264  * @extends Roo.menu.Menu
38265  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
38266  * @constructor
38267  * Creates a new DateMenu
38268  * @param {Object} config Configuration options
38269  */
38270 Roo.menu.DateMenu = function(config){
38271     Roo.menu.DateMenu.superclass.constructor.call(this, config);
38272     this.plain = true;
38273     var di = new Roo.menu.DateItem(config);
38274     this.add(di);
38275     /**
38276      * The {@link Roo.DatePicker} instance for this DateMenu
38277      * @type DatePicker
38278      */
38279     this.picker = di.picker;
38280     /**
38281      * @event select
38282      * @param {DatePicker} picker
38283      * @param {Date} date
38284      */
38285     this.relayEvents(di, ["select"]);
38286     this.on('beforeshow', function(){
38287         if(this.picker){
38288             this.picker.hideMonthPicker(false);
38289         }
38290     }, this);
38291 };
38292 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
38293     cls:'x-date-menu'
38294 });/*
38295  * Based on:
38296  * Ext JS Library 1.1.1
38297  * Copyright(c) 2006-2007, Ext JS, LLC.
38298  *
38299  * Originally Released Under LGPL - original licence link has changed is not relivant.
38300  *
38301  * Fork - LGPL
38302  * <script type="text/javascript">
38303  */
38304  
38305
38306 /**
38307  * @class Roo.menu.ColorMenu
38308  * @extends Roo.menu.Menu
38309  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
38310  * @constructor
38311  * Creates a new ColorMenu
38312  * @param {Object} config Configuration options
38313  */
38314 Roo.menu.ColorMenu = function(config){
38315     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
38316     this.plain = true;
38317     var ci = new Roo.menu.ColorItem(config);
38318     this.add(ci);
38319     /**
38320      * The {@link Roo.ColorPalette} instance for this ColorMenu
38321      * @type ColorPalette
38322      */
38323     this.palette = ci.palette;
38324     /**
38325      * @event select
38326      * @param {ColorPalette} palette
38327      * @param {String} color
38328      */
38329     this.relayEvents(ci, ["select"]);
38330 };
38331 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
38332  * Based on:
38333  * Ext JS Library 1.1.1
38334  * Copyright(c) 2006-2007, Ext JS, LLC.
38335  *
38336  * Originally Released Under LGPL - original licence link has changed is not relivant.
38337  *
38338  * Fork - LGPL
38339  * <script type="text/javascript">
38340  */
38341  
38342 /**
38343  * @class Roo.form.TextItem
38344  * @extends Roo.BoxComponent
38345  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
38346  * @constructor
38347  * Creates a new TextItem
38348  * @param {Object} config Configuration options
38349  */
38350 Roo.form.TextItem = function(config){
38351     Roo.form.TextItem.superclass.constructor.call(this, config);
38352 };
38353
38354 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
38355     
38356     /**
38357      * @cfg {String} tag the tag for this item (default div)
38358      */
38359     tag : 'div',
38360     /**
38361      * @cfg {String} html the content for this item
38362      */
38363     html : '',
38364     
38365     getAutoCreate : function()
38366     {
38367         var cfg = {
38368             id: this.id,
38369             tag: this.tag,
38370             html: this.html,
38371             cls: 'x-form-item'
38372         };
38373         
38374         return cfg;
38375         
38376     },
38377     
38378     onRender : function(ct, position)
38379     {
38380         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
38381         
38382         if(!this.el){
38383             var cfg = this.getAutoCreate();
38384             if(!cfg.name){
38385                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
38386             }
38387             if (!cfg.name.length) {
38388                 delete cfg.name;
38389             }
38390             this.el = ct.createChild(cfg, position);
38391         }
38392     }
38393     
38394 });/*
38395  * Based on:
38396  * Ext JS Library 1.1.1
38397  * Copyright(c) 2006-2007, Ext JS, LLC.
38398  *
38399  * Originally Released Under LGPL - original licence link has changed is not relivant.
38400  *
38401  * Fork - LGPL
38402  * <script type="text/javascript">
38403  */
38404  
38405 /**
38406  * @class Roo.form.Field
38407  * @extends Roo.BoxComponent
38408  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
38409  * @constructor
38410  * Creates a new Field
38411  * @param {Object} config Configuration options
38412  */
38413 Roo.form.Field = function(config){
38414     Roo.form.Field.superclass.constructor.call(this, config);
38415 };
38416
38417 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
38418     /**
38419      * @cfg {String} fieldLabel Label to use when rendering a form.
38420      */
38421        /**
38422      * @cfg {String} qtip Mouse over tip
38423      */
38424      
38425     /**
38426      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
38427      */
38428     invalidClass : "x-form-invalid",
38429     /**
38430      * @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")
38431      */
38432     invalidText : "The value in this field is invalid",
38433     /**
38434      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
38435      */
38436     focusClass : "x-form-focus",
38437     /**
38438      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
38439       automatic validation (defaults to "keyup").
38440      */
38441     validationEvent : "keyup",
38442     /**
38443      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
38444      */
38445     validateOnBlur : true,
38446     /**
38447      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
38448      */
38449     validationDelay : 250,
38450     /**
38451      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38452      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
38453      */
38454     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
38455     /**
38456      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
38457      */
38458     fieldClass : "x-form-field",
38459     /**
38460      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
38461      *<pre>
38462 Value         Description
38463 -----------   ----------------------------------------------------------------------
38464 qtip          Display a quick tip when the user hovers over the field
38465 title         Display a default browser title attribute popup
38466 under         Add a block div beneath the field containing the error text
38467 side          Add an error icon to the right of the field with a popup on hover
38468 [element id]  Add the error text directly to the innerHTML of the specified element
38469 </pre>
38470      */
38471     msgTarget : 'qtip',
38472     /**
38473      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
38474      */
38475     msgFx : 'normal',
38476
38477     /**
38478      * @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.
38479      */
38480     readOnly : false,
38481
38482     /**
38483      * @cfg {Boolean} disabled True to disable the field (defaults to false).
38484      */
38485     disabled : false,
38486
38487     /**
38488      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
38489      */
38490     inputType : undefined,
38491     
38492     /**
38493      * @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).
38494          */
38495         tabIndex : undefined,
38496         
38497     // private
38498     isFormField : true,
38499
38500     // private
38501     hasFocus : false,
38502     /**
38503      * @property {Roo.Element} fieldEl
38504      * Element Containing the rendered Field (with label etc.)
38505      */
38506     /**
38507      * @cfg {Mixed} value A value to initialize this field with.
38508      */
38509     value : undefined,
38510
38511     /**
38512      * @cfg {String} name The field's HTML name attribute.
38513      */
38514     /**
38515      * @cfg {String} cls A CSS class to apply to the field's underlying element.
38516      */
38517     // private
38518     loadedValue : false,
38519      
38520      
38521         // private ??
38522         initComponent : function(){
38523         Roo.form.Field.superclass.initComponent.call(this);
38524         this.addEvents({
38525             /**
38526              * @event focus
38527              * Fires when this field receives input focus.
38528              * @param {Roo.form.Field} this
38529              */
38530             focus : true,
38531             /**
38532              * @event blur
38533              * Fires when this field loses input focus.
38534              * @param {Roo.form.Field} this
38535              */
38536             blur : true,
38537             /**
38538              * @event specialkey
38539              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
38540              * {@link Roo.EventObject#getKey} to determine which key was pressed.
38541              * @param {Roo.form.Field} this
38542              * @param {Roo.EventObject} e The event object
38543              */
38544             specialkey : true,
38545             /**
38546              * @event change
38547              * Fires just before the field blurs if the field value has changed.
38548              * @param {Roo.form.Field} this
38549              * @param {Mixed} newValue The new value
38550              * @param {Mixed} oldValue The original value
38551              */
38552             change : true,
38553             /**
38554              * @event invalid
38555              * Fires after the field has been marked as invalid.
38556              * @param {Roo.form.Field} this
38557              * @param {String} msg The validation message
38558              */
38559             invalid : true,
38560             /**
38561              * @event valid
38562              * Fires after the field has been validated with no errors.
38563              * @param {Roo.form.Field} this
38564              */
38565             valid : true,
38566              /**
38567              * @event keyup
38568              * Fires after the key up
38569              * @param {Roo.form.Field} this
38570              * @param {Roo.EventObject}  e The event Object
38571              */
38572             keyup : true
38573         });
38574     },
38575
38576     /**
38577      * Returns the name attribute of the field if available
38578      * @return {String} name The field name
38579      */
38580     getName: function(){
38581          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
38582     },
38583
38584     // private
38585     onRender : function(ct, position){
38586         Roo.form.Field.superclass.onRender.call(this, ct, position);
38587         if(!this.el){
38588             var cfg = this.getAutoCreate();
38589             if(!cfg.name){
38590                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
38591             }
38592             if (!cfg.name.length) {
38593                 delete cfg.name;
38594             }
38595             if(this.inputType){
38596                 cfg.type = this.inputType;
38597             }
38598             this.el = ct.createChild(cfg, position);
38599         }
38600         var type = this.el.dom.type;
38601         if(type){
38602             if(type == 'password'){
38603                 type = 'text';
38604             }
38605             this.el.addClass('x-form-'+type);
38606         }
38607         if(this.readOnly){
38608             this.el.dom.readOnly = true;
38609         }
38610         if(this.tabIndex !== undefined){
38611             this.el.dom.setAttribute('tabIndex', this.tabIndex);
38612         }
38613
38614         this.el.addClass([this.fieldClass, this.cls]);
38615         this.initValue();
38616     },
38617
38618     /**
38619      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
38620      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
38621      * @return {Roo.form.Field} this
38622      */
38623     applyTo : function(target){
38624         this.allowDomMove = false;
38625         this.el = Roo.get(target);
38626         this.render(this.el.dom.parentNode);
38627         return this;
38628     },
38629
38630     // private
38631     initValue : function(){
38632         if(this.value !== undefined){
38633             this.setValue(this.value);
38634         }else if(this.el.dom.value.length > 0){
38635             this.setValue(this.el.dom.value);
38636         }
38637     },
38638
38639     /**
38640      * Returns true if this field has been changed since it was originally loaded and is not disabled.
38641      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
38642      */
38643     isDirty : function() {
38644         if(this.disabled) {
38645             return false;
38646         }
38647         return String(this.getValue()) !== String(this.originalValue);
38648     },
38649
38650     /**
38651      * stores the current value in loadedValue
38652      */
38653     resetHasChanged : function()
38654     {
38655         this.loadedValue = String(this.getValue());
38656     },
38657     /**
38658      * checks the current value against the 'loaded' value.
38659      * Note - will return false if 'resetHasChanged' has not been called first.
38660      */
38661     hasChanged : function()
38662     {
38663         if(this.disabled || this.readOnly) {
38664             return false;
38665         }
38666         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
38667     },
38668     
38669     
38670     
38671     // private
38672     afterRender : function(){
38673         Roo.form.Field.superclass.afterRender.call(this);
38674         this.initEvents();
38675     },
38676
38677     // private
38678     fireKey : function(e){
38679         //Roo.log('field ' + e.getKey());
38680         if(e.isNavKeyPress()){
38681             this.fireEvent("specialkey", this, e);
38682         }
38683     },
38684
38685     /**
38686      * Resets the current field value to the originally loaded value and clears any validation messages
38687      */
38688     reset : function(){
38689         this.setValue(this.resetValue);
38690         this.originalValue = this.getValue();
38691         this.clearInvalid();
38692     },
38693
38694     // private
38695     initEvents : function(){
38696         // safari killled keypress - so keydown is now used..
38697         this.el.on("keydown" , this.fireKey,  this);
38698         this.el.on("focus", this.onFocus,  this);
38699         this.el.on("blur", this.onBlur,  this);
38700         this.el.relayEvent('keyup', this);
38701
38702         // reference to original value for reset
38703         this.originalValue = this.getValue();
38704         this.resetValue =  this.getValue();
38705     },
38706
38707     // private
38708     onFocus : function(){
38709         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
38710             this.el.addClass(this.focusClass);
38711         }
38712         if(!this.hasFocus){
38713             this.hasFocus = true;
38714             this.startValue = this.getValue();
38715             this.fireEvent("focus", this);
38716         }
38717     },
38718
38719     beforeBlur : Roo.emptyFn,
38720
38721     // private
38722     onBlur : function(){
38723         this.beforeBlur();
38724         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
38725             this.el.removeClass(this.focusClass);
38726         }
38727         this.hasFocus = false;
38728         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
38729             this.validate();
38730         }
38731         var v = this.getValue();
38732         if(String(v) !== String(this.startValue)){
38733             this.fireEvent('change', this, v, this.startValue);
38734         }
38735         this.fireEvent("blur", this);
38736     },
38737
38738     /**
38739      * Returns whether or not the field value is currently valid
38740      * @param {Boolean} preventMark True to disable marking the field invalid
38741      * @return {Boolean} True if the value is valid, else false
38742      */
38743     isValid : function(preventMark){
38744         if(this.disabled){
38745             return true;
38746         }
38747         var restore = this.preventMark;
38748         this.preventMark = preventMark === true;
38749         var v = this.validateValue(this.processValue(this.getRawValue()));
38750         this.preventMark = restore;
38751         return v;
38752     },
38753
38754     /**
38755      * Validates the field value
38756      * @return {Boolean} True if the value is valid, else false
38757      */
38758     validate : function(){
38759         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
38760             this.clearInvalid();
38761             return true;
38762         }
38763         return false;
38764     },
38765
38766     processValue : function(value){
38767         return value;
38768     },
38769
38770     // private
38771     // Subclasses should provide the validation implementation by overriding this
38772     validateValue : function(value){
38773         return true;
38774     },
38775
38776     /**
38777      * Mark this field as invalid
38778      * @param {String} msg The validation message
38779      */
38780     markInvalid : function(msg){
38781         if(!this.rendered || this.preventMark){ // not rendered
38782             return;
38783         }
38784         
38785         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
38786         
38787         obj.el.addClass(this.invalidClass);
38788         msg = msg || this.invalidText;
38789         switch(this.msgTarget){
38790             case 'qtip':
38791                 obj.el.dom.qtip = msg;
38792                 obj.el.dom.qclass = 'x-form-invalid-tip';
38793                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
38794                     Roo.QuickTips.enable();
38795                 }
38796                 break;
38797             case 'title':
38798                 this.el.dom.title = msg;
38799                 break;
38800             case 'under':
38801                 if(!this.errorEl){
38802                     var elp = this.el.findParent('.x-form-element', 5, true);
38803                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
38804                     this.errorEl.setWidth(elp.getWidth(true)-20);
38805                 }
38806                 this.errorEl.update(msg);
38807                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
38808                 break;
38809             case 'side':
38810                 if(!this.errorIcon){
38811                     var elp = this.el.findParent('.x-form-element', 5, true);
38812                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
38813                 }
38814                 this.alignErrorIcon();
38815                 this.errorIcon.dom.qtip = msg;
38816                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
38817                 this.errorIcon.show();
38818                 this.on('resize', this.alignErrorIcon, this);
38819                 break;
38820             default:
38821                 var t = Roo.getDom(this.msgTarget);
38822                 t.innerHTML = msg;
38823                 t.style.display = this.msgDisplay;
38824                 break;
38825         }
38826         this.fireEvent('invalid', this, msg);
38827     },
38828
38829     // private
38830     alignErrorIcon : function(){
38831         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
38832     },
38833
38834     /**
38835      * Clear any invalid styles/messages for this field
38836      */
38837     clearInvalid : function(){
38838         if(!this.rendered || this.preventMark){ // not rendered
38839             return;
38840         }
38841         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
38842         
38843         obj.el.removeClass(this.invalidClass);
38844         switch(this.msgTarget){
38845             case 'qtip':
38846                 obj.el.dom.qtip = '';
38847                 break;
38848             case 'title':
38849                 this.el.dom.title = '';
38850                 break;
38851             case 'under':
38852                 if(this.errorEl){
38853                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
38854                 }
38855                 break;
38856             case 'side':
38857                 if(this.errorIcon){
38858                     this.errorIcon.dom.qtip = '';
38859                     this.errorIcon.hide();
38860                     this.un('resize', this.alignErrorIcon, this);
38861                 }
38862                 break;
38863             default:
38864                 var t = Roo.getDom(this.msgTarget);
38865                 t.innerHTML = '';
38866                 t.style.display = 'none';
38867                 break;
38868         }
38869         this.fireEvent('valid', this);
38870     },
38871
38872     /**
38873      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
38874      * @return {Mixed} value The field value
38875      */
38876     getRawValue : function(){
38877         var v = this.el.getValue();
38878         
38879         return v;
38880     },
38881
38882     /**
38883      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
38884      * @return {Mixed} value The field value
38885      */
38886     getValue : function(){
38887         var v = this.el.getValue();
38888          
38889         return v;
38890     },
38891
38892     /**
38893      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
38894      * @param {Mixed} value The value to set
38895      */
38896     setRawValue : function(v){
38897         return this.el.dom.value = (v === null || v === undefined ? '' : v);
38898     },
38899
38900     /**
38901      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
38902      * @param {Mixed} value The value to set
38903      */
38904     setValue : function(v){
38905         this.value = v;
38906         if(this.rendered){
38907             this.el.dom.value = (v === null || v === undefined ? '' : v);
38908              this.validate();
38909         }
38910     },
38911
38912     adjustSize : function(w, h){
38913         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
38914         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
38915         return s;
38916     },
38917
38918     adjustWidth : function(tag, w){
38919         tag = tag.toLowerCase();
38920         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
38921             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
38922                 if(tag == 'input'){
38923                     return w + 2;
38924                 }
38925                 if(tag == 'textarea'){
38926                     return w-2;
38927                 }
38928             }else if(Roo.isOpera){
38929                 if(tag == 'input'){
38930                     return w + 2;
38931                 }
38932                 if(tag == 'textarea'){
38933                     return w-2;
38934                 }
38935             }
38936         }
38937         return w;
38938     }
38939 });
38940
38941
38942 // anything other than normal should be considered experimental
38943 Roo.form.Field.msgFx = {
38944     normal : {
38945         show: function(msgEl, f){
38946             msgEl.setDisplayed('block');
38947         },
38948
38949         hide : function(msgEl, f){
38950             msgEl.setDisplayed(false).update('');
38951         }
38952     },
38953
38954     slide : {
38955         show: function(msgEl, f){
38956             msgEl.slideIn('t', {stopFx:true});
38957         },
38958
38959         hide : function(msgEl, f){
38960             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
38961         }
38962     },
38963
38964     slideRight : {
38965         show: function(msgEl, f){
38966             msgEl.fixDisplay();
38967             msgEl.alignTo(f.el, 'tl-tr');
38968             msgEl.slideIn('l', {stopFx:true});
38969         },
38970
38971         hide : function(msgEl, f){
38972             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
38973         }
38974     }
38975 };/*
38976  * Based on:
38977  * Ext JS Library 1.1.1
38978  * Copyright(c) 2006-2007, Ext JS, LLC.
38979  *
38980  * Originally Released Under LGPL - original licence link has changed is not relivant.
38981  *
38982  * Fork - LGPL
38983  * <script type="text/javascript">
38984  */
38985  
38986
38987 /**
38988  * @class Roo.form.TextField
38989  * @extends Roo.form.Field
38990  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
38991  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
38992  * @constructor
38993  * Creates a new TextField
38994  * @param {Object} config Configuration options
38995  */
38996 Roo.form.TextField = function(config){
38997     Roo.form.TextField.superclass.constructor.call(this, config);
38998     this.addEvents({
38999         /**
39000          * @event autosize
39001          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
39002          * according to the default logic, but this event provides a hook for the developer to apply additional
39003          * logic at runtime to resize the field if needed.
39004              * @param {Roo.form.Field} this This text field
39005              * @param {Number} width The new field width
39006              */
39007         autosize : true
39008     });
39009 };
39010
39011 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
39012     /**
39013      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
39014      */
39015     grow : false,
39016     /**
39017      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
39018      */
39019     growMin : 30,
39020     /**
39021      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
39022      */
39023     growMax : 800,
39024     /**
39025      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
39026      */
39027     vtype : null,
39028     /**
39029      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
39030      */
39031     maskRe : null,
39032     /**
39033      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
39034      */
39035     disableKeyFilter : false,
39036     /**
39037      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
39038      */
39039     allowBlank : true,
39040     /**
39041      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
39042      */
39043     minLength : 0,
39044     /**
39045      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
39046      */
39047     maxLength : Number.MAX_VALUE,
39048     /**
39049      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
39050      */
39051     minLengthText : "The minimum length for this field is {0}",
39052     /**
39053      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
39054      */
39055     maxLengthText : "The maximum length for this field is {0}",
39056     /**
39057      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
39058      */
39059     selectOnFocus : false,
39060     /**
39061      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
39062      */    
39063     allowLeadingSpace : false,
39064     /**
39065      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
39066      */
39067     blankText : "This field is required",
39068     /**
39069      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
39070      * If available, this function will be called only after the basic validators all return true, and will be passed the
39071      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
39072      */
39073     validator : null,
39074     /**
39075      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
39076      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
39077      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
39078      */
39079     regex : null,
39080     /**
39081      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
39082      */
39083     regexText : "",
39084     /**
39085      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
39086      */
39087     emptyText : null,
39088    
39089
39090     // private
39091     initEvents : function()
39092     {
39093         if (this.emptyText) {
39094             this.el.attr('placeholder', this.emptyText);
39095         }
39096         
39097         Roo.form.TextField.superclass.initEvents.call(this);
39098         if(this.validationEvent == 'keyup'){
39099             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
39100             this.el.on('keyup', this.filterValidation, this);
39101         }
39102         else if(this.validationEvent !== false){
39103             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
39104         }
39105         
39106         if(this.selectOnFocus){
39107             this.on("focus", this.preFocus, this);
39108         }
39109         if (!this.allowLeadingSpace) {
39110             this.on('blur', this.cleanLeadingSpace, this);
39111         }
39112         
39113         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
39114             this.el.on("keypress", this.filterKeys, this);
39115         }
39116         if(this.grow){
39117             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
39118             this.el.on("click", this.autoSize,  this);
39119         }
39120         if(this.el.is('input[type=password]') && Roo.isSafari){
39121             this.el.on('keydown', this.SafariOnKeyDown, this);
39122         }
39123     },
39124
39125     processValue : function(value){
39126         if(this.stripCharsRe){
39127             var newValue = value.replace(this.stripCharsRe, '');
39128             if(newValue !== value){
39129                 this.setRawValue(newValue);
39130                 return newValue;
39131             }
39132         }
39133         return value;
39134     },
39135
39136     filterValidation : function(e){
39137         if(!e.isNavKeyPress()){
39138             this.validationTask.delay(this.validationDelay);
39139         }
39140     },
39141
39142     // private
39143     onKeyUp : function(e){
39144         if(!e.isNavKeyPress()){
39145             this.autoSize();
39146         }
39147     },
39148     // private - clean the leading white space
39149     cleanLeadingSpace : function(e)
39150     {
39151         if ( this.inputType == 'file') {
39152             return;
39153         }
39154         
39155         this.setValue((this.getValue() + '').replace(/^\s+/,''));
39156     },
39157     /**
39158      * Resets the current field value to the originally-loaded value and clears any validation messages.
39159      *  
39160      */
39161     reset : function(){
39162         Roo.form.TextField.superclass.reset.call(this);
39163        
39164     }, 
39165     // private
39166     preFocus : function(){
39167         
39168         if(this.selectOnFocus){
39169             this.el.dom.select();
39170         }
39171     },
39172
39173     
39174     // private
39175     filterKeys : function(e){
39176         var k = e.getKey();
39177         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
39178             return;
39179         }
39180         var c = e.getCharCode(), cc = String.fromCharCode(c);
39181         if(Roo.isIE && (e.isSpecialKey() || !cc)){
39182             return;
39183         }
39184         if(!this.maskRe.test(cc)){
39185             e.stopEvent();
39186         }
39187     },
39188
39189     setValue : function(v){
39190         
39191         Roo.form.TextField.superclass.setValue.apply(this, arguments);
39192         
39193         this.autoSize();
39194     },
39195
39196     /**
39197      * Validates a value according to the field's validation rules and marks the field as invalid
39198      * if the validation fails
39199      * @param {Mixed} value The value to validate
39200      * @return {Boolean} True if the value is valid, else false
39201      */
39202     validateValue : function(value){
39203         if(value.length < 1)  { // if it's blank
39204              if(this.allowBlank){
39205                 this.clearInvalid();
39206                 return true;
39207              }else{
39208                 this.markInvalid(this.blankText);
39209                 return false;
39210              }
39211         }
39212         if(value.length < this.minLength){
39213             this.markInvalid(String.format(this.minLengthText, this.minLength));
39214             return false;
39215         }
39216         if(value.length > this.maxLength){
39217             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
39218             return false;
39219         }
39220         if(this.vtype){
39221             var vt = Roo.form.VTypes;
39222             if(!vt[this.vtype](value, this)){
39223                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
39224                 return false;
39225             }
39226         }
39227         if(typeof this.validator == "function"){
39228             var msg = this.validator(value);
39229             if(msg !== true){
39230                 this.markInvalid(msg);
39231                 return false;
39232             }
39233         }
39234         if(this.regex && !this.regex.test(value)){
39235             this.markInvalid(this.regexText);
39236             return false;
39237         }
39238         return true;
39239     },
39240
39241     /**
39242      * Selects text in this field
39243      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
39244      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
39245      */
39246     selectText : function(start, end){
39247         var v = this.getRawValue();
39248         if(v.length > 0){
39249             start = start === undefined ? 0 : start;
39250             end = end === undefined ? v.length : end;
39251             var d = this.el.dom;
39252             if(d.setSelectionRange){
39253                 d.setSelectionRange(start, end);
39254             }else if(d.createTextRange){
39255                 var range = d.createTextRange();
39256                 range.moveStart("character", start);
39257                 range.moveEnd("character", v.length-end);
39258                 range.select();
39259             }
39260         }
39261     },
39262
39263     /**
39264      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
39265      * This only takes effect if grow = true, and fires the autosize event.
39266      */
39267     autoSize : function(){
39268         if(!this.grow || !this.rendered){
39269             return;
39270         }
39271         if(!this.metrics){
39272             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
39273         }
39274         var el = this.el;
39275         var v = el.dom.value;
39276         var d = document.createElement('div');
39277         d.appendChild(document.createTextNode(v));
39278         v = d.innerHTML;
39279         d = null;
39280         v += "&#160;";
39281         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
39282         this.el.setWidth(w);
39283         this.fireEvent("autosize", this, w);
39284     },
39285     
39286     // private
39287     SafariOnKeyDown : function(event)
39288     {
39289         // this is a workaround for a password hang bug on chrome/ webkit.
39290         
39291         var isSelectAll = false;
39292         
39293         if(this.el.dom.selectionEnd > 0){
39294             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
39295         }
39296         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
39297             event.preventDefault();
39298             this.setValue('');
39299             return;
39300         }
39301         
39302         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
39303             
39304             event.preventDefault();
39305             // this is very hacky as keydown always get's upper case.
39306             
39307             var cc = String.fromCharCode(event.getCharCode());
39308             
39309             
39310             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
39311             
39312         }
39313         
39314         
39315     }
39316 });/*
39317  * Based on:
39318  * Ext JS Library 1.1.1
39319  * Copyright(c) 2006-2007, Ext JS, LLC.
39320  *
39321  * Originally Released Under LGPL - original licence link has changed is not relivant.
39322  *
39323  * Fork - LGPL
39324  * <script type="text/javascript">
39325  */
39326  
39327 /**
39328  * @class Roo.form.Hidden
39329  * @extends Roo.form.TextField
39330  * Simple Hidden element used on forms 
39331  * 
39332  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
39333  * 
39334  * @constructor
39335  * Creates a new Hidden form element.
39336  * @param {Object} config Configuration options
39337  */
39338
39339
39340
39341 // easy hidden field...
39342 Roo.form.Hidden = function(config){
39343     Roo.form.Hidden.superclass.constructor.call(this, config);
39344 };
39345   
39346 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
39347     fieldLabel:      '',
39348     inputType:      'hidden',
39349     width:          50,
39350     allowBlank:     true,
39351     labelSeparator: '',
39352     hidden:         true,
39353     itemCls :       'x-form-item-display-none'
39354
39355
39356 });
39357
39358
39359 /*
39360  * Based on:
39361  * Ext JS Library 1.1.1
39362  * Copyright(c) 2006-2007, Ext JS, LLC.
39363  *
39364  * Originally Released Under LGPL - original licence link has changed is not relivant.
39365  *
39366  * Fork - LGPL
39367  * <script type="text/javascript">
39368  */
39369  
39370 /**
39371  * @class Roo.form.TriggerField
39372  * @extends Roo.form.TextField
39373  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
39374  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
39375  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
39376  * for which you can provide a custom implementation.  For example:
39377  * <pre><code>
39378 var trigger = new Roo.form.TriggerField();
39379 trigger.onTriggerClick = myTriggerFn;
39380 trigger.applyTo('my-field');
39381 </code></pre>
39382  *
39383  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
39384  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
39385  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39386  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
39387  * @constructor
39388  * Create a new TriggerField.
39389  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
39390  * to the base TextField)
39391  */
39392 Roo.form.TriggerField = function(config){
39393     this.mimicing = false;
39394     Roo.form.TriggerField.superclass.constructor.call(this, config);
39395 };
39396
39397 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
39398     /**
39399      * @cfg {String} triggerClass A CSS class to apply to the trigger
39400      */
39401     /**
39402      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39403      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
39404      */
39405     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
39406     /**
39407      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
39408      */
39409     hideTrigger:false,
39410
39411     /** @cfg {Boolean} grow @hide */
39412     /** @cfg {Number} growMin @hide */
39413     /** @cfg {Number} growMax @hide */
39414
39415     /**
39416      * @hide 
39417      * @method
39418      */
39419     autoSize: Roo.emptyFn,
39420     // private
39421     monitorTab : true,
39422     // private
39423     deferHeight : true,
39424
39425     
39426     actionMode : 'wrap',
39427     // private
39428     onResize : function(w, h){
39429         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
39430         if(typeof w == 'number'){
39431             var x = w - this.trigger.getWidth();
39432             this.el.setWidth(this.adjustWidth('input', x));
39433             this.trigger.setStyle('left', x+'px');
39434         }
39435     },
39436
39437     // private
39438     adjustSize : Roo.BoxComponent.prototype.adjustSize,
39439
39440     // private
39441     getResizeEl : function(){
39442         return this.wrap;
39443     },
39444
39445     // private
39446     getPositionEl : function(){
39447         return this.wrap;
39448     },
39449
39450     // private
39451     alignErrorIcon : function(){
39452         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
39453     },
39454
39455     // private
39456     onRender : function(ct, position){
39457         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
39458         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
39459         this.trigger = this.wrap.createChild(this.triggerConfig ||
39460                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
39461         if(this.hideTrigger){
39462             this.trigger.setDisplayed(false);
39463         }
39464         this.initTrigger();
39465         if(!this.width){
39466             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
39467         }
39468     },
39469
39470     // private
39471     initTrigger : function(){
39472         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39473         this.trigger.addClassOnOver('x-form-trigger-over');
39474         this.trigger.addClassOnClick('x-form-trigger-click');
39475     },
39476
39477     // private
39478     onDestroy : function(){
39479         if(this.trigger){
39480             this.trigger.removeAllListeners();
39481             this.trigger.remove();
39482         }
39483         if(this.wrap){
39484             this.wrap.remove();
39485         }
39486         Roo.form.TriggerField.superclass.onDestroy.call(this);
39487     },
39488
39489     // private
39490     onFocus : function(){
39491         Roo.form.TriggerField.superclass.onFocus.call(this);
39492         if(!this.mimicing){
39493             this.wrap.addClass('x-trigger-wrap-focus');
39494             this.mimicing = true;
39495             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
39496             if(this.monitorTab){
39497                 this.el.on("keydown", this.checkTab, this);
39498             }
39499         }
39500     },
39501
39502     // private
39503     checkTab : function(e){
39504         if(e.getKey() == e.TAB){
39505             this.triggerBlur();
39506         }
39507     },
39508
39509     // private
39510     onBlur : function(){
39511         // do nothing
39512     },
39513
39514     // private
39515     mimicBlur : function(e, t){
39516         if(!this.wrap.contains(t) && this.validateBlur()){
39517             this.triggerBlur();
39518         }
39519     },
39520
39521     // private
39522     triggerBlur : function(){
39523         this.mimicing = false;
39524         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
39525         if(this.monitorTab){
39526             this.el.un("keydown", this.checkTab, this);
39527         }
39528         this.wrap.removeClass('x-trigger-wrap-focus');
39529         Roo.form.TriggerField.superclass.onBlur.call(this);
39530     },
39531
39532     // private
39533     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
39534     validateBlur : function(e, t){
39535         return true;
39536     },
39537
39538     // private
39539     onDisable : function(){
39540         Roo.form.TriggerField.superclass.onDisable.call(this);
39541         if(this.wrap){
39542             this.wrap.addClass('x-item-disabled');
39543         }
39544     },
39545
39546     // private
39547     onEnable : function(){
39548         Roo.form.TriggerField.superclass.onEnable.call(this);
39549         if(this.wrap){
39550             this.wrap.removeClass('x-item-disabled');
39551         }
39552     },
39553
39554     // private
39555     onShow : function(){
39556         var ae = this.getActionEl();
39557         
39558         if(ae){
39559             ae.dom.style.display = '';
39560             ae.dom.style.visibility = 'visible';
39561         }
39562     },
39563
39564     // private
39565     
39566     onHide : function(){
39567         var ae = this.getActionEl();
39568         ae.dom.style.display = 'none';
39569     },
39570
39571     /**
39572      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
39573      * by an implementing function.
39574      * @method
39575      * @param {EventObject} e
39576      */
39577     onTriggerClick : Roo.emptyFn
39578 });
39579
39580 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
39581 // to be extended by an implementing class.  For an example of implementing this class, see the custom
39582 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
39583 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
39584     initComponent : function(){
39585         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
39586
39587         this.triggerConfig = {
39588             tag:'span', cls:'x-form-twin-triggers', cn:[
39589             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
39590             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
39591         ]};
39592     },
39593
39594     getTrigger : function(index){
39595         return this.triggers[index];
39596     },
39597
39598     initTrigger : function(){
39599         var ts = this.trigger.select('.x-form-trigger', true);
39600         this.wrap.setStyle('overflow', 'hidden');
39601         var triggerField = this;
39602         ts.each(function(t, all, index){
39603             t.hide = function(){
39604                 var w = triggerField.wrap.getWidth();
39605                 this.dom.style.display = 'none';
39606                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
39607             };
39608             t.show = function(){
39609                 var w = triggerField.wrap.getWidth();
39610                 this.dom.style.display = '';
39611                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
39612             };
39613             var triggerIndex = 'Trigger'+(index+1);
39614
39615             if(this['hide'+triggerIndex]){
39616                 t.dom.style.display = 'none';
39617             }
39618             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
39619             t.addClassOnOver('x-form-trigger-over');
39620             t.addClassOnClick('x-form-trigger-click');
39621         }, this);
39622         this.triggers = ts.elements;
39623     },
39624
39625     onTrigger1Click : Roo.emptyFn,
39626     onTrigger2Click : Roo.emptyFn
39627 });/*
39628  * Based on:
39629  * Ext JS Library 1.1.1
39630  * Copyright(c) 2006-2007, Ext JS, LLC.
39631  *
39632  * Originally Released Under LGPL - original licence link has changed is not relivant.
39633  *
39634  * Fork - LGPL
39635  * <script type="text/javascript">
39636  */
39637  
39638 /**
39639  * @class Roo.form.TextArea
39640  * @extends Roo.form.TextField
39641  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
39642  * support for auto-sizing.
39643  * @constructor
39644  * Creates a new TextArea
39645  * @param {Object} config Configuration options
39646  */
39647 Roo.form.TextArea = function(config){
39648     Roo.form.TextArea.superclass.constructor.call(this, config);
39649     // these are provided exchanges for backwards compat
39650     // minHeight/maxHeight were replaced by growMin/growMax to be
39651     // compatible with TextField growing config values
39652     if(this.minHeight !== undefined){
39653         this.growMin = this.minHeight;
39654     }
39655     if(this.maxHeight !== undefined){
39656         this.growMax = this.maxHeight;
39657     }
39658 };
39659
39660 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
39661     /**
39662      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
39663      */
39664     growMin : 60,
39665     /**
39666      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
39667      */
39668     growMax: 1000,
39669     /**
39670      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
39671      * in the field (equivalent to setting overflow: hidden, defaults to false)
39672      */
39673     preventScrollbars: false,
39674     /**
39675      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39676      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
39677      */
39678
39679     // private
39680     onRender : function(ct, position){
39681         if(!this.el){
39682             this.defaultAutoCreate = {
39683                 tag: "textarea",
39684                 style:"width:300px;height:60px;",
39685                 autocomplete: "new-password"
39686             };
39687         }
39688         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
39689         if(this.grow){
39690             this.textSizeEl = Roo.DomHelper.append(document.body, {
39691                 tag: "pre", cls: "x-form-grow-sizer"
39692             });
39693             if(this.preventScrollbars){
39694                 this.el.setStyle("overflow", "hidden");
39695             }
39696             this.el.setHeight(this.growMin);
39697         }
39698     },
39699
39700     onDestroy : function(){
39701         if(this.textSizeEl){
39702             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
39703         }
39704         Roo.form.TextArea.superclass.onDestroy.call(this);
39705     },
39706
39707     // private
39708     onKeyUp : function(e){
39709         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
39710             this.autoSize();
39711         }
39712     },
39713
39714     /**
39715      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
39716      * This only takes effect if grow = true, and fires the autosize event if the height changes.
39717      */
39718     autoSize : function(){
39719         if(!this.grow || !this.textSizeEl){
39720             return;
39721         }
39722         var el = this.el;
39723         var v = el.dom.value;
39724         var ts = this.textSizeEl;
39725
39726         ts.innerHTML = '';
39727         ts.appendChild(document.createTextNode(v));
39728         v = ts.innerHTML;
39729
39730         Roo.fly(ts).setWidth(this.el.getWidth());
39731         if(v.length < 1){
39732             v = "&#160;&#160;";
39733         }else{
39734             if(Roo.isIE){
39735                 v = v.replace(/\n/g, '<p>&#160;</p>');
39736             }
39737             v += "&#160;\n&#160;";
39738         }
39739         ts.innerHTML = v;
39740         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
39741         if(h != this.lastHeight){
39742             this.lastHeight = h;
39743             this.el.setHeight(h);
39744             this.fireEvent("autosize", this, h);
39745         }
39746     }
39747 });/*
39748  * Based on:
39749  * Ext JS Library 1.1.1
39750  * Copyright(c) 2006-2007, Ext JS, LLC.
39751  *
39752  * Originally Released Under LGPL - original licence link has changed is not relivant.
39753  *
39754  * Fork - LGPL
39755  * <script type="text/javascript">
39756  */
39757  
39758
39759 /**
39760  * @class Roo.form.NumberField
39761  * @extends Roo.form.TextField
39762  * Numeric text field that provides automatic keystroke filtering and numeric validation.
39763  * @constructor
39764  * Creates a new NumberField
39765  * @param {Object} config Configuration options
39766  */
39767 Roo.form.NumberField = function(config){
39768     Roo.form.NumberField.superclass.constructor.call(this, config);
39769 };
39770
39771 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
39772     /**
39773      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
39774      */
39775     fieldClass: "x-form-field x-form-num-field",
39776     /**
39777      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
39778      */
39779     allowDecimals : true,
39780     /**
39781      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
39782      */
39783     decimalSeparator : ".",
39784     /**
39785      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
39786      */
39787     decimalPrecision : 2,
39788     /**
39789      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
39790      */
39791     allowNegative : true,
39792     /**
39793      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
39794      */
39795     minValue : Number.NEGATIVE_INFINITY,
39796     /**
39797      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
39798      */
39799     maxValue : Number.MAX_VALUE,
39800     /**
39801      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
39802      */
39803     minText : "The minimum value for this field is {0}",
39804     /**
39805      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
39806      */
39807     maxText : "The maximum value for this field is {0}",
39808     /**
39809      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
39810      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
39811      */
39812     nanText : "{0} is not a valid number",
39813
39814     // private
39815     initEvents : function(){
39816         Roo.form.NumberField.superclass.initEvents.call(this);
39817         var allowed = "0123456789";
39818         if(this.allowDecimals){
39819             allowed += this.decimalSeparator;
39820         }
39821         if(this.allowNegative){
39822             allowed += "-";
39823         }
39824         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
39825         var keyPress = function(e){
39826             var k = e.getKey();
39827             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
39828                 return;
39829             }
39830             var c = e.getCharCode();
39831             if(allowed.indexOf(String.fromCharCode(c)) === -1){
39832                 e.stopEvent();
39833             }
39834         };
39835         this.el.on("keypress", keyPress, this);
39836     },
39837
39838     // private
39839     validateValue : function(value){
39840         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
39841             return false;
39842         }
39843         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
39844              return true;
39845         }
39846         var num = this.parseValue(value);
39847         if(isNaN(num)){
39848             this.markInvalid(String.format(this.nanText, value));
39849             return false;
39850         }
39851         if(num < this.minValue){
39852             this.markInvalid(String.format(this.minText, this.minValue));
39853             return false;
39854         }
39855         if(num > this.maxValue){
39856             this.markInvalid(String.format(this.maxText, this.maxValue));
39857             return false;
39858         }
39859         return true;
39860     },
39861
39862     getValue : function(){
39863         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
39864     },
39865
39866     // private
39867     parseValue : function(value){
39868         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
39869         return isNaN(value) ? '' : value;
39870     },
39871
39872     // private
39873     fixPrecision : function(value){
39874         var nan = isNaN(value);
39875         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
39876             return nan ? '' : value;
39877         }
39878         return parseFloat(value).toFixed(this.decimalPrecision);
39879     },
39880
39881     setValue : function(v){
39882         v = this.fixPrecision(v);
39883         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
39884     },
39885
39886     // private
39887     decimalPrecisionFcn : function(v){
39888         return Math.floor(v);
39889     },
39890
39891     beforeBlur : function(){
39892         var v = this.parseValue(this.getRawValue());
39893         if(v){
39894             this.setValue(v);
39895         }
39896     }
39897 });/*
39898  * Based on:
39899  * Ext JS Library 1.1.1
39900  * Copyright(c) 2006-2007, Ext JS, LLC.
39901  *
39902  * Originally Released Under LGPL - original licence link has changed is not relivant.
39903  *
39904  * Fork - LGPL
39905  * <script type="text/javascript">
39906  */
39907  
39908 /**
39909  * @class Roo.form.DateField
39910  * @extends Roo.form.TriggerField
39911  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
39912 * @constructor
39913 * Create a new DateField
39914 * @param {Object} config
39915  */
39916 Roo.form.DateField = function(config)
39917 {
39918     Roo.form.DateField.superclass.constructor.call(this, config);
39919     
39920       this.addEvents({
39921          
39922         /**
39923          * @event select
39924          * Fires when a date is selected
39925              * @param {Roo.form.DateField} combo This combo box
39926              * @param {Date} date The date selected
39927              */
39928         'select' : true
39929          
39930     });
39931     
39932     
39933     if(typeof this.minValue == "string") {
39934         this.minValue = this.parseDate(this.minValue);
39935     }
39936     if(typeof this.maxValue == "string") {
39937         this.maxValue = this.parseDate(this.maxValue);
39938     }
39939     this.ddMatch = null;
39940     if(this.disabledDates){
39941         var dd = this.disabledDates;
39942         var re = "(?:";
39943         for(var i = 0; i < dd.length; i++){
39944             re += dd[i];
39945             if(i != dd.length-1) {
39946                 re += "|";
39947             }
39948         }
39949         this.ddMatch = new RegExp(re + ")");
39950     }
39951 };
39952
39953 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
39954     /**
39955      * @cfg {String} format
39956      * The default date format string which can be overriden for localization support.  The format must be
39957      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
39958      */
39959     format : "m/d/y",
39960     /**
39961      * @cfg {String} altFormats
39962      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
39963      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
39964      */
39965     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
39966     /**
39967      * @cfg {Array} disabledDays
39968      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
39969      */
39970     disabledDays : null,
39971     /**
39972      * @cfg {String} disabledDaysText
39973      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
39974      */
39975     disabledDaysText : "Disabled",
39976     /**
39977      * @cfg {Array} disabledDates
39978      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
39979      * expression so they are very powerful. Some examples:
39980      * <ul>
39981      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
39982      * <li>["03/08", "09/16"] would disable those days for every year</li>
39983      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
39984      * <li>["03/../2006"] would disable every day in March 2006</li>
39985      * <li>["^03"] would disable every day in every March</li>
39986      * </ul>
39987      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
39988      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
39989      */
39990     disabledDates : null,
39991     /**
39992      * @cfg {String} disabledDatesText
39993      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
39994      */
39995     disabledDatesText : "Disabled",
39996     /**
39997      * @cfg {Date/String} minValue
39998      * The minimum allowed date. Can be either a Javascript date object or a string date in a
39999      * valid format (defaults to null).
40000      */
40001     minValue : null,
40002     /**
40003      * @cfg {Date/String} maxValue
40004      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40005      * valid format (defaults to null).
40006      */
40007     maxValue : null,
40008     /**
40009      * @cfg {String} minText
40010      * The error text to display when the date in the cell is before minValue (defaults to
40011      * 'The date in this field must be after {minValue}').
40012      */
40013     minText : "The date in this field must be equal to or after {0}",
40014     /**
40015      * @cfg {String} maxText
40016      * The error text to display when the date in the cell is after maxValue (defaults to
40017      * 'The date in this field must be before {maxValue}').
40018      */
40019     maxText : "The date in this field must be equal to or before {0}",
40020     /**
40021      * @cfg {String} invalidText
40022      * The error text to display when the date in the field is invalid (defaults to
40023      * '{value} is not a valid date - it must be in the format {format}').
40024      */
40025     invalidText : "{0} is not a valid date - it must be in the format {1}",
40026     /**
40027      * @cfg {String} triggerClass
40028      * An additional CSS class used to style the trigger button.  The trigger will always get the
40029      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40030      * which displays a calendar icon).
40031      */
40032     triggerClass : 'x-form-date-trigger',
40033     
40034
40035     /**
40036      * @cfg {Boolean} useIso
40037      * if enabled, then the date field will use a hidden field to store the 
40038      * real value as iso formated date. default (false)
40039      */ 
40040     useIso : false,
40041     /**
40042      * @cfg {String/Object} autoCreate
40043      * A DomHelper element spec, or true for a default element spec (defaults to
40044      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40045      */ 
40046     // private
40047     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
40048     
40049     // private
40050     hiddenField: false,
40051     
40052     onRender : function(ct, position)
40053     {
40054         Roo.form.DateField.superclass.onRender.call(this, ct, position);
40055         if (this.useIso) {
40056             //this.el.dom.removeAttribute('name'); 
40057             Roo.log("Changing name?");
40058             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
40059             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40060                     'before', true);
40061             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40062             // prevent input submission
40063             this.hiddenName = this.name;
40064         }
40065             
40066             
40067     },
40068     
40069     // private
40070     validateValue : function(value)
40071     {
40072         value = this.formatDate(value);
40073         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
40074             Roo.log('super failed');
40075             return false;
40076         }
40077         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40078              return true;
40079         }
40080         var svalue = value;
40081         value = this.parseDate(value);
40082         if(!value){
40083             Roo.log('parse date failed' + svalue);
40084             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40085             return false;
40086         }
40087         var time = value.getTime();
40088         if(this.minValue && time < this.minValue.getTime()){
40089             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40090             return false;
40091         }
40092         if(this.maxValue && time > this.maxValue.getTime()){
40093             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40094             return false;
40095         }
40096         if(this.disabledDays){
40097             var day = value.getDay();
40098             for(var i = 0; i < this.disabledDays.length; i++) {
40099                 if(day === this.disabledDays[i]){
40100                     this.markInvalid(this.disabledDaysText);
40101                     return false;
40102                 }
40103             }
40104         }
40105         var fvalue = this.formatDate(value);
40106         if(this.ddMatch && this.ddMatch.test(fvalue)){
40107             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40108             return false;
40109         }
40110         return true;
40111     },
40112
40113     // private
40114     // Provides logic to override the default TriggerField.validateBlur which just returns true
40115     validateBlur : function(){
40116         return !this.menu || !this.menu.isVisible();
40117     },
40118     
40119     getName: function()
40120     {
40121         // returns hidden if it's set..
40122         if (!this.rendered) {return ''};
40123         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40124         
40125     },
40126
40127     /**
40128      * Returns the current date value of the date field.
40129      * @return {Date} The date value
40130      */
40131     getValue : function(){
40132         
40133         return  this.hiddenField ?
40134                 this.hiddenField.value :
40135                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
40136     },
40137
40138     /**
40139      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40140      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
40141      * (the default format used is "m/d/y").
40142      * <br />Usage:
40143      * <pre><code>
40144 //All of these calls set the same date value (May 4, 2006)
40145
40146 //Pass a date object:
40147 var dt = new Date('5/4/06');
40148 dateField.setValue(dt);
40149
40150 //Pass a date string (default format):
40151 dateField.setValue('5/4/06');
40152
40153 //Pass a date string (custom format):
40154 dateField.format = 'Y-m-d';
40155 dateField.setValue('2006-5-4');
40156 </code></pre>
40157      * @param {String/Date} date The date or valid date string
40158      */
40159     setValue : function(date){
40160         if (this.hiddenField) {
40161             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40162         }
40163         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40164         // make sure the value field is always stored as a date..
40165         this.value = this.parseDate(date);
40166         
40167         
40168     },
40169
40170     // private
40171     parseDate : function(value){
40172         if(!value || value instanceof Date){
40173             return value;
40174         }
40175         var v = Date.parseDate(value, this.format);
40176          if (!v && this.useIso) {
40177             v = Date.parseDate(value, 'Y-m-d');
40178         }
40179         if(!v && this.altFormats){
40180             if(!this.altFormatsArray){
40181                 this.altFormatsArray = this.altFormats.split("|");
40182             }
40183             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40184                 v = Date.parseDate(value, this.altFormatsArray[i]);
40185             }
40186         }
40187         return v;
40188     },
40189
40190     // private
40191     formatDate : function(date, fmt){
40192         return (!date || !(date instanceof Date)) ?
40193                date : date.dateFormat(fmt || this.format);
40194     },
40195
40196     // private
40197     menuListeners : {
40198         select: function(m, d){
40199             
40200             this.setValue(d);
40201             this.fireEvent('select', this, d);
40202         },
40203         show : function(){ // retain focus styling
40204             this.onFocus();
40205         },
40206         hide : function(){
40207             this.focus.defer(10, this);
40208             var ml = this.menuListeners;
40209             this.menu.un("select", ml.select,  this);
40210             this.menu.un("show", ml.show,  this);
40211             this.menu.un("hide", ml.hide,  this);
40212         }
40213     },
40214
40215     // private
40216     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40217     onTriggerClick : function(){
40218         if(this.disabled){
40219             return;
40220         }
40221         if(this.menu == null){
40222             this.menu = new Roo.menu.DateMenu();
40223         }
40224         Roo.apply(this.menu.picker,  {
40225             showClear: this.allowBlank,
40226             minDate : this.minValue,
40227             maxDate : this.maxValue,
40228             disabledDatesRE : this.ddMatch,
40229             disabledDatesText : this.disabledDatesText,
40230             disabledDays : this.disabledDays,
40231             disabledDaysText : this.disabledDaysText,
40232             format : this.useIso ? 'Y-m-d' : this.format,
40233             minText : String.format(this.minText, this.formatDate(this.minValue)),
40234             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40235         });
40236         this.menu.on(Roo.apply({}, this.menuListeners, {
40237             scope:this
40238         }));
40239         this.menu.picker.setValue(this.getValue() || new Date());
40240         this.menu.show(this.el, "tl-bl?");
40241     },
40242
40243     beforeBlur : function(){
40244         var v = this.parseDate(this.getRawValue());
40245         if(v){
40246             this.setValue(v);
40247         }
40248     },
40249
40250     /*@
40251      * overide
40252      * 
40253      */
40254     isDirty : function() {
40255         if(this.disabled) {
40256             return false;
40257         }
40258         
40259         if(typeof(this.startValue) === 'undefined'){
40260             return false;
40261         }
40262         
40263         return String(this.getValue()) !== String(this.startValue);
40264         
40265     },
40266     // @overide
40267     cleanLeadingSpace : function(e)
40268     {
40269        return;
40270     }
40271     
40272 });/*
40273  * Based on:
40274  * Ext JS Library 1.1.1
40275  * Copyright(c) 2006-2007, Ext JS, LLC.
40276  *
40277  * Originally Released Under LGPL - original licence link has changed is not relivant.
40278  *
40279  * Fork - LGPL
40280  * <script type="text/javascript">
40281  */
40282  
40283 /**
40284  * @class Roo.form.MonthField
40285  * @extends Roo.form.TriggerField
40286  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40287 * @constructor
40288 * Create a new MonthField
40289 * @param {Object} config
40290  */
40291 Roo.form.MonthField = function(config){
40292     
40293     Roo.form.MonthField.superclass.constructor.call(this, config);
40294     
40295       this.addEvents({
40296          
40297         /**
40298          * @event select
40299          * Fires when a date is selected
40300              * @param {Roo.form.MonthFieeld} combo This combo box
40301              * @param {Date} date The date selected
40302              */
40303         'select' : true
40304          
40305     });
40306     
40307     
40308     if(typeof this.minValue == "string") {
40309         this.minValue = this.parseDate(this.minValue);
40310     }
40311     if(typeof this.maxValue == "string") {
40312         this.maxValue = this.parseDate(this.maxValue);
40313     }
40314     this.ddMatch = null;
40315     if(this.disabledDates){
40316         var dd = this.disabledDates;
40317         var re = "(?:";
40318         for(var i = 0; i < dd.length; i++){
40319             re += dd[i];
40320             if(i != dd.length-1) {
40321                 re += "|";
40322             }
40323         }
40324         this.ddMatch = new RegExp(re + ")");
40325     }
40326 };
40327
40328 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
40329     /**
40330      * @cfg {String} format
40331      * The default date format string which can be overriden for localization support.  The format must be
40332      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40333      */
40334     format : "M Y",
40335     /**
40336      * @cfg {String} altFormats
40337      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40338      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40339      */
40340     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
40341     /**
40342      * @cfg {Array} disabledDays
40343      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40344      */
40345     disabledDays : [0,1,2,3,4,5,6],
40346     /**
40347      * @cfg {String} disabledDaysText
40348      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40349      */
40350     disabledDaysText : "Disabled",
40351     /**
40352      * @cfg {Array} disabledDates
40353      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40354      * expression so they are very powerful. Some examples:
40355      * <ul>
40356      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40357      * <li>["03/08", "09/16"] would disable those days for every year</li>
40358      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40359      * <li>["03/../2006"] would disable every day in March 2006</li>
40360      * <li>["^03"] would disable every day in every March</li>
40361      * </ul>
40362      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40363      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40364      */
40365     disabledDates : null,
40366     /**
40367      * @cfg {String} disabledDatesText
40368      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40369      */
40370     disabledDatesText : "Disabled",
40371     /**
40372      * @cfg {Date/String} minValue
40373      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40374      * valid format (defaults to null).
40375      */
40376     minValue : null,
40377     /**
40378      * @cfg {Date/String} maxValue
40379      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40380      * valid format (defaults to null).
40381      */
40382     maxValue : null,
40383     /**
40384      * @cfg {String} minText
40385      * The error text to display when the date in the cell is before minValue (defaults to
40386      * 'The date in this field must be after {minValue}').
40387      */
40388     minText : "The date in this field must be equal to or after {0}",
40389     /**
40390      * @cfg {String} maxTextf
40391      * The error text to display when the date in the cell is after maxValue (defaults to
40392      * 'The date in this field must be before {maxValue}').
40393      */
40394     maxText : "The date in this field must be equal to or before {0}",
40395     /**
40396      * @cfg {String} invalidText
40397      * The error text to display when the date in the field is invalid (defaults to
40398      * '{value} is not a valid date - it must be in the format {format}').
40399      */
40400     invalidText : "{0} is not a valid date - it must be in the format {1}",
40401     /**
40402      * @cfg {String} triggerClass
40403      * An additional CSS class used to style the trigger button.  The trigger will always get the
40404      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40405      * which displays a calendar icon).
40406      */
40407     triggerClass : 'x-form-date-trigger',
40408     
40409
40410     /**
40411      * @cfg {Boolean} useIso
40412      * if enabled, then the date field will use a hidden field to store the 
40413      * real value as iso formated date. default (true)
40414      */ 
40415     useIso : true,
40416     /**
40417      * @cfg {String/Object} autoCreate
40418      * A DomHelper element spec, or true for a default element spec (defaults to
40419      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40420      */ 
40421     // private
40422     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
40423     
40424     // private
40425     hiddenField: false,
40426     
40427     hideMonthPicker : false,
40428     
40429     onRender : function(ct, position)
40430     {
40431         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
40432         if (this.useIso) {
40433             this.el.dom.removeAttribute('name'); 
40434             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40435                     'before', true);
40436             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40437             // prevent input submission
40438             this.hiddenName = this.name;
40439         }
40440             
40441             
40442     },
40443     
40444     // private
40445     validateValue : function(value)
40446     {
40447         value = this.formatDate(value);
40448         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
40449             return false;
40450         }
40451         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40452              return true;
40453         }
40454         var svalue = value;
40455         value = this.parseDate(value);
40456         if(!value){
40457             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40458             return false;
40459         }
40460         var time = value.getTime();
40461         if(this.minValue && time < this.minValue.getTime()){
40462             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40463             return false;
40464         }
40465         if(this.maxValue && time > this.maxValue.getTime()){
40466             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40467             return false;
40468         }
40469         /*if(this.disabledDays){
40470             var day = value.getDay();
40471             for(var i = 0; i < this.disabledDays.length; i++) {
40472                 if(day === this.disabledDays[i]){
40473                     this.markInvalid(this.disabledDaysText);
40474                     return false;
40475                 }
40476             }
40477         }
40478         */
40479         var fvalue = this.formatDate(value);
40480         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
40481             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40482             return false;
40483         }
40484         */
40485         return true;
40486     },
40487
40488     // private
40489     // Provides logic to override the default TriggerField.validateBlur which just returns true
40490     validateBlur : function(){
40491         return !this.menu || !this.menu.isVisible();
40492     },
40493
40494     /**
40495      * Returns the current date value of the date field.
40496      * @return {Date} The date value
40497      */
40498     getValue : function(){
40499         
40500         
40501         
40502         return  this.hiddenField ?
40503                 this.hiddenField.value :
40504                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
40505     },
40506
40507     /**
40508      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40509      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
40510      * (the default format used is "m/d/y").
40511      * <br />Usage:
40512      * <pre><code>
40513 //All of these calls set the same date value (May 4, 2006)
40514
40515 //Pass a date object:
40516 var dt = new Date('5/4/06');
40517 monthField.setValue(dt);
40518
40519 //Pass a date string (default format):
40520 monthField.setValue('5/4/06');
40521
40522 //Pass a date string (custom format):
40523 monthField.format = 'Y-m-d';
40524 monthField.setValue('2006-5-4');
40525 </code></pre>
40526      * @param {String/Date} date The date or valid date string
40527      */
40528     setValue : function(date){
40529         Roo.log('month setValue' + date);
40530         // can only be first of month..
40531         
40532         var val = this.parseDate(date);
40533         
40534         if (this.hiddenField) {
40535             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40536         }
40537         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40538         this.value = this.parseDate(date);
40539     },
40540
40541     // private
40542     parseDate : function(value){
40543         if(!value || value instanceof Date){
40544             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
40545             return value;
40546         }
40547         var v = Date.parseDate(value, this.format);
40548         if (!v && this.useIso) {
40549             v = Date.parseDate(value, 'Y-m-d');
40550         }
40551         if (v) {
40552             // 
40553             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
40554         }
40555         
40556         
40557         if(!v && this.altFormats){
40558             if(!this.altFormatsArray){
40559                 this.altFormatsArray = this.altFormats.split("|");
40560             }
40561             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40562                 v = Date.parseDate(value, this.altFormatsArray[i]);
40563             }
40564         }
40565         return v;
40566     },
40567
40568     // private
40569     formatDate : function(date, fmt){
40570         return (!date || !(date instanceof Date)) ?
40571                date : date.dateFormat(fmt || this.format);
40572     },
40573
40574     // private
40575     menuListeners : {
40576         select: function(m, d){
40577             this.setValue(d);
40578             this.fireEvent('select', this, d);
40579         },
40580         show : function(){ // retain focus styling
40581             this.onFocus();
40582         },
40583         hide : function(){
40584             this.focus.defer(10, this);
40585             var ml = this.menuListeners;
40586             this.menu.un("select", ml.select,  this);
40587             this.menu.un("show", ml.show,  this);
40588             this.menu.un("hide", ml.hide,  this);
40589         }
40590     },
40591     // private
40592     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40593     onTriggerClick : function(){
40594         if(this.disabled){
40595             return;
40596         }
40597         if(this.menu == null){
40598             this.menu = new Roo.menu.DateMenu();
40599            
40600         }
40601         
40602         Roo.apply(this.menu.picker,  {
40603             
40604             showClear: this.allowBlank,
40605             minDate : this.minValue,
40606             maxDate : this.maxValue,
40607             disabledDatesRE : this.ddMatch,
40608             disabledDatesText : this.disabledDatesText,
40609             
40610             format : this.useIso ? 'Y-m-d' : this.format,
40611             minText : String.format(this.minText, this.formatDate(this.minValue)),
40612             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40613             
40614         });
40615          this.menu.on(Roo.apply({}, this.menuListeners, {
40616             scope:this
40617         }));
40618        
40619         
40620         var m = this.menu;
40621         var p = m.picker;
40622         
40623         // hide month picker get's called when we called by 'before hide';
40624         
40625         var ignorehide = true;
40626         p.hideMonthPicker  = function(disableAnim){
40627             if (ignorehide) {
40628                 return;
40629             }
40630              if(this.monthPicker){
40631                 Roo.log("hideMonthPicker called");
40632                 if(disableAnim === true){
40633                     this.monthPicker.hide();
40634                 }else{
40635                     this.monthPicker.slideOut('t', {duration:.2});
40636                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
40637                     p.fireEvent("select", this, this.value);
40638                     m.hide();
40639                 }
40640             }
40641         }
40642         
40643         Roo.log('picker set value');
40644         Roo.log(this.getValue());
40645         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
40646         m.show(this.el, 'tl-bl?');
40647         ignorehide  = false;
40648         // this will trigger hideMonthPicker..
40649         
40650         
40651         // hidden the day picker
40652         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
40653         
40654         
40655         
40656       
40657         
40658         p.showMonthPicker.defer(100, p);
40659     
40660         
40661        
40662     },
40663
40664     beforeBlur : function(){
40665         var v = this.parseDate(this.getRawValue());
40666         if(v){
40667             this.setValue(v);
40668         }
40669     }
40670
40671     /** @cfg {Boolean} grow @hide */
40672     /** @cfg {Number} growMin @hide */
40673     /** @cfg {Number} growMax @hide */
40674     /**
40675      * @hide
40676      * @method autoSize
40677      */
40678 });/*
40679  * Based on:
40680  * Ext JS Library 1.1.1
40681  * Copyright(c) 2006-2007, Ext JS, LLC.
40682  *
40683  * Originally Released Under LGPL - original licence link has changed is not relivant.
40684  *
40685  * Fork - LGPL
40686  * <script type="text/javascript">
40687  */
40688  
40689
40690 /**
40691  * @class Roo.form.ComboBox
40692  * @extends Roo.form.TriggerField
40693  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
40694  * @constructor
40695  * Create a new ComboBox.
40696  * @param {Object} config Configuration options
40697  */
40698 Roo.form.ComboBox = function(config){
40699     Roo.form.ComboBox.superclass.constructor.call(this, config);
40700     this.addEvents({
40701         /**
40702          * @event expand
40703          * Fires when the dropdown list is expanded
40704              * @param {Roo.form.ComboBox} combo This combo box
40705              */
40706         'expand' : true,
40707         /**
40708          * @event collapse
40709          * Fires when the dropdown list is collapsed
40710              * @param {Roo.form.ComboBox} combo This combo box
40711              */
40712         'collapse' : true,
40713         /**
40714          * @event beforeselect
40715          * Fires before a list item is selected. Return false to cancel the selection.
40716              * @param {Roo.form.ComboBox} combo This combo box
40717              * @param {Roo.data.Record} record The data record returned from the underlying store
40718              * @param {Number} index The index of the selected item in the dropdown list
40719              */
40720         'beforeselect' : true,
40721         /**
40722          * @event select
40723          * Fires when a list item is selected
40724              * @param {Roo.form.ComboBox} combo This combo box
40725              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
40726              * @param {Number} index The index of the selected item in the dropdown list
40727              */
40728         'select' : true,
40729         /**
40730          * @event beforequery
40731          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
40732          * The event object passed has these properties:
40733              * @param {Roo.form.ComboBox} combo This combo box
40734              * @param {String} query The query
40735              * @param {Boolean} forceAll true to force "all" query
40736              * @param {Boolean} cancel true to cancel the query
40737              * @param {Object} e The query event object
40738              */
40739         'beforequery': true,
40740          /**
40741          * @event add
40742          * Fires when the 'add' icon is pressed (add a listener to enable add button)
40743              * @param {Roo.form.ComboBox} combo This combo box
40744              */
40745         'add' : true,
40746         /**
40747          * @event edit
40748          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
40749              * @param {Roo.form.ComboBox} combo This combo box
40750              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
40751              */
40752         'edit' : true
40753         
40754         
40755     });
40756     if(this.transform){
40757         this.allowDomMove = false;
40758         var s = Roo.getDom(this.transform);
40759         if(!this.hiddenName){
40760             this.hiddenName = s.name;
40761         }
40762         if(!this.store){
40763             this.mode = 'local';
40764             var d = [], opts = s.options;
40765             for(var i = 0, len = opts.length;i < len; i++){
40766                 var o = opts[i];
40767                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
40768                 if(o.selected) {
40769                     this.value = value;
40770                 }
40771                 d.push([value, o.text]);
40772             }
40773             this.store = new Roo.data.SimpleStore({
40774                 'id': 0,
40775                 fields: ['value', 'text'],
40776                 data : d
40777             });
40778             this.valueField = 'value';
40779             this.displayField = 'text';
40780         }
40781         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
40782         if(!this.lazyRender){
40783             this.target = true;
40784             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
40785             s.parentNode.removeChild(s); // remove it
40786             this.render(this.el.parentNode);
40787         }else{
40788             s.parentNode.removeChild(s); // remove it
40789         }
40790
40791     }
40792     if (this.store) {
40793         this.store = Roo.factory(this.store, Roo.data);
40794     }
40795     
40796     this.selectedIndex = -1;
40797     if(this.mode == 'local'){
40798         if(config.queryDelay === undefined){
40799             this.queryDelay = 10;
40800         }
40801         if(config.minChars === undefined){
40802             this.minChars = 0;
40803         }
40804     }
40805 };
40806
40807 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
40808     /**
40809      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
40810      */
40811     /**
40812      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
40813      * rendering into an Roo.Editor, defaults to false)
40814      */
40815     /**
40816      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
40817      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
40818      */
40819     /**
40820      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
40821      */
40822     /**
40823      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
40824      * the dropdown list (defaults to undefined, with no header element)
40825      */
40826
40827      /**
40828      * @cfg {String/Roo.Template} tpl The template to use to render the output
40829      */
40830      
40831     // private
40832     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
40833     /**
40834      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
40835      */
40836     listWidth: undefined,
40837     /**
40838      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
40839      * mode = 'remote' or 'text' if mode = 'local')
40840      */
40841     displayField: undefined,
40842     /**
40843      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
40844      * mode = 'remote' or 'value' if mode = 'local'). 
40845      * Note: use of a valueField requires the user make a selection
40846      * in order for a value to be mapped.
40847      */
40848     valueField: undefined,
40849     
40850     
40851     /**
40852      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
40853      * field's data value (defaults to the underlying DOM element's name)
40854      */
40855     hiddenName: undefined,
40856     /**
40857      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
40858      */
40859     listClass: '',
40860     /**
40861      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
40862      */
40863     selectedClass: 'x-combo-selected',
40864     /**
40865      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
40866      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
40867      * which displays a downward arrow icon).
40868      */
40869     triggerClass : 'x-form-arrow-trigger',
40870     /**
40871      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
40872      */
40873     shadow:'sides',
40874     /**
40875      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
40876      * anchor positions (defaults to 'tl-bl')
40877      */
40878     listAlign: 'tl-bl?',
40879     /**
40880      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
40881      */
40882     maxHeight: 300,
40883     /**
40884      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
40885      * query specified by the allQuery config option (defaults to 'query')
40886      */
40887     triggerAction: 'query',
40888     /**
40889      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
40890      * (defaults to 4, does not apply if editable = false)
40891      */
40892     minChars : 4,
40893     /**
40894      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
40895      * delay (typeAheadDelay) if it matches a known value (defaults to false)
40896      */
40897     typeAhead: false,
40898     /**
40899      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
40900      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
40901      */
40902     queryDelay: 500,
40903     /**
40904      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
40905      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
40906      */
40907     pageSize: 0,
40908     /**
40909      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
40910      * when editable = true (defaults to false)
40911      */
40912     selectOnFocus:false,
40913     /**
40914      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
40915      */
40916     queryParam: 'query',
40917     /**
40918      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
40919      * when mode = 'remote' (defaults to 'Loading...')
40920      */
40921     loadingText: 'Loading...',
40922     /**
40923      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
40924      */
40925     resizable: false,
40926     /**
40927      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
40928      */
40929     handleHeight : 8,
40930     /**
40931      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
40932      * traditional select (defaults to true)
40933      */
40934     editable: true,
40935     /**
40936      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
40937      */
40938     allQuery: '',
40939     /**
40940      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
40941      */
40942     mode: 'remote',
40943     /**
40944      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
40945      * listWidth has a higher value)
40946      */
40947     minListWidth : 70,
40948     /**
40949      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
40950      * allow the user to set arbitrary text into the field (defaults to false)
40951      */
40952     forceSelection:false,
40953     /**
40954      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
40955      * if typeAhead = true (defaults to 250)
40956      */
40957     typeAheadDelay : 250,
40958     /**
40959      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
40960      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
40961      */
40962     valueNotFoundText : undefined,
40963     /**
40964      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
40965      */
40966     blockFocus : false,
40967     
40968     /**
40969      * @cfg {Boolean} disableClear Disable showing of clear button.
40970      */
40971     disableClear : false,
40972     /**
40973      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
40974      */
40975     alwaysQuery : false,
40976     
40977     //private
40978     addicon : false,
40979     editicon: false,
40980     
40981     // element that contains real text value.. (when hidden is used..)
40982      
40983     // private
40984     onRender : function(ct, position)
40985     {
40986         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
40987         
40988         if(this.hiddenName){
40989             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
40990                     'before', true);
40991             this.hiddenField.value =
40992                 this.hiddenValue !== undefined ? this.hiddenValue :
40993                 this.value !== undefined ? this.value : '';
40994
40995             // prevent input submission
40996             this.el.dom.removeAttribute('name');
40997              
40998              
40999         }
41000         
41001         if(Roo.isGecko){
41002             this.el.dom.setAttribute('autocomplete', 'off');
41003         }
41004
41005         var cls = 'x-combo-list';
41006
41007         this.list = new Roo.Layer({
41008             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
41009         });
41010
41011         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
41012         this.list.setWidth(lw);
41013         this.list.swallowEvent('mousewheel');
41014         this.assetHeight = 0;
41015
41016         if(this.title){
41017             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
41018             this.assetHeight += this.header.getHeight();
41019         }
41020
41021         this.innerList = this.list.createChild({cls:cls+'-inner'});
41022         this.innerList.on('mouseover', this.onViewOver, this);
41023         this.innerList.on('mousemove', this.onViewMove, this);
41024         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41025         
41026         if(this.allowBlank && !this.pageSize && !this.disableClear){
41027             this.footer = this.list.createChild({cls:cls+'-ft'});
41028             this.pageTb = new Roo.Toolbar(this.footer);
41029            
41030         }
41031         if(this.pageSize){
41032             this.footer = this.list.createChild({cls:cls+'-ft'});
41033             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
41034                     {pageSize: this.pageSize});
41035             
41036         }
41037         
41038         if (this.pageTb && this.allowBlank && !this.disableClear) {
41039             var _this = this;
41040             this.pageTb.add(new Roo.Toolbar.Fill(), {
41041                 cls: 'x-btn-icon x-btn-clear',
41042                 text: '&#160;',
41043                 handler: function()
41044                 {
41045                     _this.collapse();
41046                     _this.clearValue();
41047                     _this.onSelect(false, -1);
41048                 }
41049             });
41050         }
41051         if (this.footer) {
41052             this.assetHeight += this.footer.getHeight();
41053         }
41054         
41055
41056         if(!this.tpl){
41057             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
41058         }
41059
41060         this.view = new Roo.View(this.innerList, this.tpl, {
41061             singleSelect:true,
41062             store: this.store,
41063             selectedClass: this.selectedClass
41064         });
41065
41066         this.view.on('click', this.onViewClick, this);
41067
41068         this.store.on('beforeload', this.onBeforeLoad, this);
41069         this.store.on('load', this.onLoad, this);
41070         this.store.on('loadexception', this.onLoadException, this);
41071
41072         if(this.resizable){
41073             this.resizer = new Roo.Resizable(this.list,  {
41074                pinned:true, handles:'se'
41075             });
41076             this.resizer.on('resize', function(r, w, h){
41077                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
41078                 this.listWidth = w;
41079                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
41080                 this.restrictHeight();
41081             }, this);
41082             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
41083         }
41084         if(!this.editable){
41085             this.editable = true;
41086             this.setEditable(false);
41087         }  
41088         
41089         
41090         if (typeof(this.events.add.listeners) != 'undefined') {
41091             
41092             this.addicon = this.wrap.createChild(
41093                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
41094        
41095             this.addicon.on('click', function(e) {
41096                 this.fireEvent('add', this);
41097             }, this);
41098         }
41099         if (typeof(this.events.edit.listeners) != 'undefined') {
41100             
41101             this.editicon = this.wrap.createChild(
41102                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
41103             if (this.addicon) {
41104                 this.editicon.setStyle('margin-left', '40px');
41105             }
41106             this.editicon.on('click', function(e) {
41107                 
41108                 // we fire even  if inothing is selected..
41109                 this.fireEvent('edit', this, this.lastData );
41110                 
41111             }, this);
41112         }
41113         
41114         
41115         
41116     },
41117
41118     // private
41119     initEvents : function(){
41120         Roo.form.ComboBox.superclass.initEvents.call(this);
41121
41122         this.keyNav = new Roo.KeyNav(this.el, {
41123             "up" : function(e){
41124                 this.inKeyMode = true;
41125                 this.selectPrev();
41126             },
41127
41128             "down" : function(e){
41129                 if(!this.isExpanded()){
41130                     this.onTriggerClick();
41131                 }else{
41132                     this.inKeyMode = true;
41133                     this.selectNext();
41134                 }
41135             },
41136
41137             "enter" : function(e){
41138                 this.onViewClick();
41139                 //return true;
41140             },
41141
41142             "esc" : function(e){
41143                 this.collapse();
41144             },
41145
41146             "tab" : function(e){
41147                 this.onViewClick(false);
41148                 this.fireEvent("specialkey", this, e);
41149                 return true;
41150             },
41151
41152             scope : this,
41153
41154             doRelay : function(foo, bar, hname){
41155                 if(hname == 'down' || this.scope.isExpanded()){
41156                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41157                 }
41158                 return true;
41159             },
41160
41161             forceKeyDown: true
41162         });
41163         this.queryDelay = Math.max(this.queryDelay || 10,
41164                 this.mode == 'local' ? 10 : 250);
41165         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
41166         if(this.typeAhead){
41167             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
41168         }
41169         if(this.editable !== false){
41170             this.el.on("keyup", this.onKeyUp, this);
41171         }
41172         if(this.forceSelection){
41173             this.on('blur', this.doForce, this);
41174         }
41175     },
41176
41177     onDestroy : function(){
41178         if(this.view){
41179             this.view.setStore(null);
41180             this.view.el.removeAllListeners();
41181             this.view.el.remove();
41182             this.view.purgeListeners();
41183         }
41184         if(this.list){
41185             this.list.destroy();
41186         }
41187         if(this.store){
41188             this.store.un('beforeload', this.onBeforeLoad, this);
41189             this.store.un('load', this.onLoad, this);
41190             this.store.un('loadexception', this.onLoadException, this);
41191         }
41192         Roo.form.ComboBox.superclass.onDestroy.call(this);
41193     },
41194
41195     // private
41196     fireKey : function(e){
41197         if(e.isNavKeyPress() && !this.list.isVisible()){
41198             this.fireEvent("specialkey", this, e);
41199         }
41200     },
41201
41202     // private
41203     onResize: function(w, h){
41204         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
41205         
41206         if(typeof w != 'number'){
41207             // we do not handle it!?!?
41208             return;
41209         }
41210         var tw = this.trigger.getWidth();
41211         tw += this.addicon ? this.addicon.getWidth() : 0;
41212         tw += this.editicon ? this.editicon.getWidth() : 0;
41213         var x = w - tw;
41214         this.el.setWidth( this.adjustWidth('input', x));
41215             
41216         this.trigger.setStyle('left', x+'px');
41217         
41218         if(this.list && this.listWidth === undefined){
41219             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
41220             this.list.setWidth(lw);
41221             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41222         }
41223         
41224     
41225         
41226     },
41227
41228     /**
41229      * Allow or prevent the user from directly editing the field text.  If false is passed,
41230      * the user will only be able to select from the items defined in the dropdown list.  This method
41231      * is the runtime equivalent of setting the 'editable' config option at config time.
41232      * @param {Boolean} value True to allow the user to directly edit the field text
41233      */
41234     setEditable : function(value){
41235         if(value == this.editable){
41236             return;
41237         }
41238         this.editable = value;
41239         if(!value){
41240             this.el.dom.setAttribute('readOnly', true);
41241             this.el.on('mousedown', this.onTriggerClick,  this);
41242             this.el.addClass('x-combo-noedit');
41243         }else{
41244             this.el.dom.setAttribute('readOnly', false);
41245             this.el.un('mousedown', this.onTriggerClick,  this);
41246             this.el.removeClass('x-combo-noedit');
41247         }
41248     },
41249
41250     // private
41251     onBeforeLoad : function(){
41252         if(!this.hasFocus){
41253             return;
41254         }
41255         this.innerList.update(this.loadingText ?
41256                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
41257         this.restrictHeight();
41258         this.selectedIndex = -1;
41259     },
41260
41261     // private
41262     onLoad : function(){
41263         if(!this.hasFocus){
41264             return;
41265         }
41266         if(this.store.getCount() > 0){
41267             this.expand();
41268             this.restrictHeight();
41269             if(this.lastQuery == this.allQuery){
41270                 if(this.editable){
41271                     this.el.dom.select();
41272                 }
41273                 if(!this.selectByValue(this.value, true)){
41274                     this.select(0, true);
41275                 }
41276             }else{
41277                 this.selectNext();
41278                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
41279                     this.taTask.delay(this.typeAheadDelay);
41280                 }
41281             }
41282         }else{
41283             this.onEmptyResults();
41284         }
41285         //this.el.focus();
41286     },
41287     // private
41288     onLoadException : function()
41289     {
41290         this.collapse();
41291         Roo.log(this.store.reader.jsonData);
41292         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
41293             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
41294         }
41295         
41296         
41297     },
41298     // private
41299     onTypeAhead : function(){
41300         if(this.store.getCount() > 0){
41301             var r = this.store.getAt(0);
41302             var newValue = r.data[this.displayField];
41303             var len = newValue.length;
41304             var selStart = this.getRawValue().length;
41305             if(selStart != len){
41306                 this.setRawValue(newValue);
41307                 this.selectText(selStart, newValue.length);
41308             }
41309         }
41310     },
41311
41312     // private
41313     onSelect : function(record, index){
41314         if(this.fireEvent('beforeselect', this, record, index) !== false){
41315             this.setFromData(index > -1 ? record.data : false);
41316             this.collapse();
41317             this.fireEvent('select', this, record, index);
41318         }
41319     },
41320
41321     /**
41322      * Returns the currently selected field value or empty string if no value is set.
41323      * @return {String} value The selected value
41324      */
41325     getValue : function(){
41326         if(this.valueField){
41327             return typeof this.value != 'undefined' ? this.value : '';
41328         }
41329         return Roo.form.ComboBox.superclass.getValue.call(this);
41330     },
41331
41332     /**
41333      * Clears any text/value currently set in the field
41334      */
41335     clearValue : function(){
41336         if(this.hiddenField){
41337             this.hiddenField.value = '';
41338         }
41339         this.value = '';
41340         this.setRawValue('');
41341         this.lastSelectionText = '';
41342         
41343     },
41344
41345     /**
41346      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
41347      * will be displayed in the field.  If the value does not match the data value of an existing item,
41348      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
41349      * Otherwise the field will be blank (although the value will still be set).
41350      * @param {String} value The value to match
41351      */
41352     setValue : function(v){
41353         var text = v;
41354         if(this.valueField){
41355             var r = this.findRecord(this.valueField, v);
41356             if(r){
41357                 text = r.data[this.displayField];
41358             }else if(this.valueNotFoundText !== undefined){
41359                 text = this.valueNotFoundText;
41360             }
41361         }
41362         this.lastSelectionText = text;
41363         if(this.hiddenField){
41364             this.hiddenField.value = v;
41365         }
41366         Roo.form.ComboBox.superclass.setValue.call(this, text);
41367         this.value = v;
41368     },
41369     /**
41370      * @property {Object} the last set data for the element
41371      */
41372     
41373     lastData : false,
41374     /**
41375      * Sets the value of the field based on a object which is related to the record format for the store.
41376      * @param {Object} value the value to set as. or false on reset?
41377      */
41378     setFromData : function(o){
41379         var dv = ''; // display value
41380         var vv = ''; // value value..
41381         this.lastData = o;
41382         if (this.displayField) {
41383             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
41384         } else {
41385             // this is an error condition!!!
41386             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
41387         }
41388         
41389         if(this.valueField){
41390             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
41391         }
41392         if(this.hiddenField){
41393             this.hiddenField.value = vv;
41394             
41395             this.lastSelectionText = dv;
41396             Roo.form.ComboBox.superclass.setValue.call(this, dv);
41397             this.value = vv;
41398             return;
41399         }
41400         // no hidden field.. - we store the value in 'value', but still display
41401         // display field!!!!
41402         this.lastSelectionText = dv;
41403         Roo.form.ComboBox.superclass.setValue.call(this, dv);
41404         this.value = vv;
41405         
41406         
41407     },
41408     // private
41409     reset : function(){
41410         // overridden so that last data is reset..
41411         this.setValue(this.resetValue);
41412         this.originalValue = this.getValue();
41413         this.clearInvalid();
41414         this.lastData = false;
41415         if (this.view) {
41416             this.view.clearSelections();
41417         }
41418     },
41419     // private
41420     findRecord : function(prop, value){
41421         var record;
41422         if(this.store.getCount() > 0){
41423             this.store.each(function(r){
41424                 if(r.data[prop] == value){
41425                     record = r;
41426                     return false;
41427                 }
41428                 return true;
41429             });
41430         }
41431         return record;
41432     },
41433     
41434     getName: function()
41435     {
41436         // returns hidden if it's set..
41437         if (!this.rendered) {return ''};
41438         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
41439         
41440     },
41441     // private
41442     onViewMove : function(e, t){
41443         this.inKeyMode = false;
41444     },
41445
41446     // private
41447     onViewOver : function(e, t){
41448         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
41449             return;
41450         }
41451         var item = this.view.findItemFromChild(t);
41452         if(item){
41453             var index = this.view.indexOf(item);
41454             this.select(index, false);
41455         }
41456     },
41457
41458     // private
41459     onViewClick : function(doFocus)
41460     {
41461         var index = this.view.getSelectedIndexes()[0];
41462         var r = this.store.getAt(index);
41463         if(r){
41464             this.onSelect(r, index);
41465         }
41466         if(doFocus !== false && !this.blockFocus){
41467             this.el.focus();
41468         }
41469     },
41470
41471     // private
41472     restrictHeight : function(){
41473         this.innerList.dom.style.height = '';
41474         var inner = this.innerList.dom;
41475         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
41476         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
41477         this.list.beginUpdate();
41478         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
41479         this.list.alignTo(this.el, this.listAlign);
41480         this.list.endUpdate();
41481     },
41482
41483     // private
41484     onEmptyResults : function(){
41485         this.collapse();
41486     },
41487
41488     /**
41489      * Returns true if the dropdown list is expanded, else false.
41490      */
41491     isExpanded : function(){
41492         return this.list.isVisible();
41493     },
41494
41495     /**
41496      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
41497      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
41498      * @param {String} value The data value of the item to select
41499      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
41500      * selected item if it is not currently in view (defaults to true)
41501      * @return {Boolean} True if the value matched an item in the list, else false
41502      */
41503     selectByValue : function(v, scrollIntoView){
41504         if(v !== undefined && v !== null){
41505             var r = this.findRecord(this.valueField || this.displayField, v);
41506             if(r){
41507                 this.select(this.store.indexOf(r), scrollIntoView);
41508                 return true;
41509             }
41510         }
41511         return false;
41512     },
41513
41514     /**
41515      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
41516      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
41517      * @param {Number} index The zero-based index of the list item to select
41518      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
41519      * selected item if it is not currently in view (defaults to true)
41520      */
41521     select : function(index, scrollIntoView){
41522         this.selectedIndex = index;
41523         this.view.select(index);
41524         if(scrollIntoView !== false){
41525             var el = this.view.getNode(index);
41526             if(el){
41527                 this.innerList.scrollChildIntoView(el, false);
41528             }
41529         }
41530     },
41531
41532     // private
41533     selectNext : function(){
41534         var ct = this.store.getCount();
41535         if(ct > 0){
41536             if(this.selectedIndex == -1){
41537                 this.select(0);
41538             }else if(this.selectedIndex < ct-1){
41539                 this.select(this.selectedIndex+1);
41540             }
41541         }
41542     },
41543
41544     // private
41545     selectPrev : function(){
41546         var ct = this.store.getCount();
41547         if(ct > 0){
41548             if(this.selectedIndex == -1){
41549                 this.select(0);
41550             }else if(this.selectedIndex != 0){
41551                 this.select(this.selectedIndex-1);
41552             }
41553         }
41554     },
41555
41556     // private
41557     onKeyUp : function(e){
41558         if(this.editable !== false && !e.isSpecialKey()){
41559             this.lastKey = e.getKey();
41560             this.dqTask.delay(this.queryDelay);
41561         }
41562     },
41563
41564     // private
41565     validateBlur : function(){
41566         return !this.list || !this.list.isVisible();   
41567     },
41568
41569     // private
41570     initQuery : function(){
41571         this.doQuery(this.getRawValue());
41572     },
41573
41574     // private
41575     doForce : function(){
41576         if(this.el.dom.value.length > 0){
41577             this.el.dom.value =
41578                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
41579              
41580         }
41581     },
41582
41583     /**
41584      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
41585      * query allowing the query action to be canceled if needed.
41586      * @param {String} query The SQL query to execute
41587      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
41588      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
41589      * saved in the current store (defaults to false)
41590      */
41591     doQuery : function(q, forceAll){
41592         if(q === undefined || q === null){
41593             q = '';
41594         }
41595         var qe = {
41596             query: q,
41597             forceAll: forceAll,
41598             combo: this,
41599             cancel:false
41600         };
41601         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
41602             return false;
41603         }
41604         q = qe.query;
41605         forceAll = qe.forceAll;
41606         if(forceAll === true || (q.length >= this.minChars)){
41607             if(this.lastQuery != q || this.alwaysQuery){
41608                 this.lastQuery = q;
41609                 if(this.mode == 'local'){
41610                     this.selectedIndex = -1;
41611                     if(forceAll){
41612                         this.store.clearFilter();
41613                     }else{
41614                         this.store.filter(this.displayField, q);
41615                     }
41616                     this.onLoad();
41617                 }else{
41618                     this.store.baseParams[this.queryParam] = q;
41619                     this.store.load({
41620                         params: this.getParams(q)
41621                     });
41622                     this.expand();
41623                 }
41624             }else{
41625                 this.selectedIndex = -1;
41626                 this.onLoad();   
41627             }
41628         }
41629     },
41630
41631     // private
41632     getParams : function(q){
41633         var p = {};
41634         //p[this.queryParam] = q;
41635         if(this.pageSize){
41636             p.start = 0;
41637             p.limit = this.pageSize;
41638         }
41639         return p;
41640     },
41641
41642     /**
41643      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
41644      */
41645     collapse : function(){
41646         if(!this.isExpanded()){
41647             return;
41648         }
41649         this.list.hide();
41650         Roo.get(document).un('mousedown', this.collapseIf, this);
41651         Roo.get(document).un('mousewheel', this.collapseIf, this);
41652         if (!this.editable) {
41653             Roo.get(document).un('keydown', this.listKeyPress, this);
41654         }
41655         this.fireEvent('collapse', this);
41656     },
41657
41658     // private
41659     collapseIf : function(e){
41660         if(!e.within(this.wrap) && !e.within(this.list)){
41661             this.collapse();
41662         }
41663     },
41664
41665     /**
41666      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
41667      */
41668     expand : function(){
41669         if(this.isExpanded() || !this.hasFocus){
41670             return;
41671         }
41672         this.list.alignTo(this.el, this.listAlign);
41673         this.list.show();
41674         Roo.get(document).on('mousedown', this.collapseIf, this);
41675         Roo.get(document).on('mousewheel', this.collapseIf, this);
41676         if (!this.editable) {
41677             Roo.get(document).on('keydown', this.listKeyPress, this);
41678         }
41679         
41680         this.fireEvent('expand', this);
41681     },
41682
41683     // private
41684     // Implements the default empty TriggerField.onTriggerClick function
41685     onTriggerClick : function(){
41686         if(this.disabled){
41687             return;
41688         }
41689         if(this.isExpanded()){
41690             this.collapse();
41691             if (!this.blockFocus) {
41692                 this.el.focus();
41693             }
41694             
41695         }else {
41696             this.hasFocus = true;
41697             if(this.triggerAction == 'all') {
41698                 this.doQuery(this.allQuery, true);
41699             } else {
41700                 this.doQuery(this.getRawValue());
41701             }
41702             if (!this.blockFocus) {
41703                 this.el.focus();
41704             }
41705         }
41706     },
41707     listKeyPress : function(e)
41708     {
41709         //Roo.log('listkeypress');
41710         // scroll to first matching element based on key pres..
41711         if (e.isSpecialKey()) {
41712             return false;
41713         }
41714         var k = String.fromCharCode(e.getKey()).toUpperCase();
41715         //Roo.log(k);
41716         var match  = false;
41717         var csel = this.view.getSelectedNodes();
41718         var cselitem = false;
41719         if (csel.length) {
41720             var ix = this.view.indexOf(csel[0]);
41721             cselitem  = this.store.getAt(ix);
41722             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
41723                 cselitem = false;
41724             }
41725             
41726         }
41727         
41728         this.store.each(function(v) { 
41729             if (cselitem) {
41730                 // start at existing selection.
41731                 if (cselitem.id == v.id) {
41732                     cselitem = false;
41733                 }
41734                 return;
41735             }
41736                 
41737             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
41738                 match = this.store.indexOf(v);
41739                 return false;
41740             }
41741         }, this);
41742         
41743         if (match === false) {
41744             return true; // no more action?
41745         }
41746         // scroll to?
41747         this.view.select(match);
41748         var sn = Roo.get(this.view.getSelectedNodes()[0]);
41749         sn.scrollIntoView(sn.dom.parentNode, false);
41750     } 
41751
41752     /** 
41753     * @cfg {Boolean} grow 
41754     * @hide 
41755     */
41756     /** 
41757     * @cfg {Number} growMin 
41758     * @hide 
41759     */
41760     /** 
41761     * @cfg {Number} growMax 
41762     * @hide 
41763     */
41764     /**
41765      * @hide
41766      * @method autoSize
41767      */
41768 });/*
41769  * Copyright(c) 2010-2012, Roo J Solutions Limited
41770  *
41771  * Licence LGPL
41772  *
41773  */
41774
41775 /**
41776  * @class Roo.form.ComboBoxArray
41777  * @extends Roo.form.TextField
41778  * A facebook style adder... for lists of email / people / countries  etc...
41779  * pick multiple items from a combo box, and shows each one.
41780  *
41781  *  Fred [x]  Brian [x]  [Pick another |v]
41782  *
41783  *
41784  *  For this to work: it needs various extra information
41785  *    - normal combo problay has
41786  *      name, hiddenName
41787  *    + displayField, valueField
41788  *
41789  *    For our purpose...
41790  *
41791  *
41792  *   If we change from 'extends' to wrapping...
41793  *   
41794  *  
41795  *
41796  
41797  
41798  * @constructor
41799  * Create a new ComboBoxArray.
41800  * @param {Object} config Configuration options
41801  */
41802  
41803
41804 Roo.form.ComboBoxArray = function(config)
41805 {
41806     this.addEvents({
41807         /**
41808          * @event beforeremove
41809          * Fires before remove the value from the list
41810              * @param {Roo.form.ComboBoxArray} _self This combo box array
41811              * @param {Roo.form.ComboBoxArray.Item} item removed item
41812              */
41813         'beforeremove' : true,
41814         /**
41815          * @event remove
41816          * Fires when remove the value from the list
41817              * @param {Roo.form.ComboBoxArray} _self This combo box array
41818              * @param {Roo.form.ComboBoxArray.Item} item removed item
41819              */
41820         'remove' : true
41821         
41822         
41823     });
41824     
41825     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
41826     
41827     this.items = new Roo.util.MixedCollection(false);
41828     
41829     // construct the child combo...
41830     
41831     
41832     
41833     
41834    
41835     
41836 }
41837
41838  
41839 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
41840
41841     /**
41842      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
41843      */
41844     
41845     lastData : false,
41846     
41847     // behavies liek a hiddne field
41848     inputType:      'hidden',
41849     /**
41850      * @cfg {Number} width The width of the box that displays the selected element
41851      */ 
41852     width:          300,
41853
41854     
41855     
41856     /**
41857      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
41858      */
41859     name : false,
41860     /**
41861      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
41862      */
41863     hiddenName : false,
41864       /**
41865      * @cfg {String} seperator    The value seperator normally ',' 
41866      */
41867     seperator : ',',
41868     
41869     // private the array of items that are displayed..
41870     items  : false,
41871     // private - the hidden field el.
41872     hiddenEl : false,
41873     // private - the filed el..
41874     el : false,
41875     
41876     //validateValue : function() { return true; }, // all values are ok!
41877     //onAddClick: function() { },
41878     
41879     onRender : function(ct, position) 
41880     {
41881         
41882         // create the standard hidden element
41883         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
41884         
41885         
41886         // give fake names to child combo;
41887         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
41888         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
41889         
41890         this.combo = Roo.factory(this.combo, Roo.form);
41891         this.combo.onRender(ct, position);
41892         if (typeof(this.combo.width) != 'undefined') {
41893             this.combo.onResize(this.combo.width,0);
41894         }
41895         
41896         this.combo.initEvents();
41897         
41898         // assigned so form know we need to do this..
41899         this.store          = this.combo.store;
41900         this.valueField     = this.combo.valueField;
41901         this.displayField   = this.combo.displayField ;
41902         
41903         
41904         this.combo.wrap.addClass('x-cbarray-grp');
41905         
41906         var cbwrap = this.combo.wrap.createChild(
41907             {tag: 'div', cls: 'x-cbarray-cb'},
41908             this.combo.el.dom
41909         );
41910         
41911              
41912         this.hiddenEl = this.combo.wrap.createChild({
41913             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
41914         });
41915         this.el = this.combo.wrap.createChild({
41916             tag: 'input',  type:'hidden' , name: this.name, value : ''
41917         });
41918          //   this.el.dom.removeAttribute("name");
41919         
41920         
41921         this.outerWrap = this.combo.wrap;
41922         this.wrap = cbwrap;
41923         
41924         this.outerWrap.setWidth(this.width);
41925         this.outerWrap.dom.removeChild(this.el.dom);
41926         
41927         this.wrap.dom.appendChild(this.el.dom);
41928         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
41929         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
41930         
41931         this.combo.trigger.setStyle('position','relative');
41932         this.combo.trigger.setStyle('left', '0px');
41933         this.combo.trigger.setStyle('top', '2px');
41934         
41935         this.combo.el.setStyle('vertical-align', 'text-bottom');
41936         
41937         //this.trigger.setStyle('vertical-align', 'top');
41938         
41939         // this should use the code from combo really... on('add' ....)
41940         if (this.adder) {
41941             
41942         
41943             this.adder = this.outerWrap.createChild(
41944                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
41945             var _t = this;
41946             this.adder.on('click', function(e) {
41947                 _t.fireEvent('adderclick', this, e);
41948             }, _t);
41949         }
41950         //var _t = this;
41951         //this.adder.on('click', this.onAddClick, _t);
41952         
41953         
41954         this.combo.on('select', function(cb, rec, ix) {
41955             this.addItem(rec.data);
41956             
41957             cb.setValue('');
41958             cb.el.dom.value = '';
41959             //cb.lastData = rec.data;
41960             // add to list
41961             
41962         }, this);
41963         
41964         
41965     },
41966     
41967     
41968     getName: function()
41969     {
41970         // returns hidden if it's set..
41971         if (!this.rendered) {return ''};
41972         return  this.hiddenName ? this.hiddenName : this.name;
41973         
41974     },
41975     
41976     
41977     onResize: function(w, h){
41978         
41979         return;
41980         // not sure if this is needed..
41981         //this.combo.onResize(w,h);
41982         
41983         if(typeof w != 'number'){
41984             // we do not handle it!?!?
41985             return;
41986         }
41987         var tw = this.combo.trigger.getWidth();
41988         tw += this.addicon ? this.addicon.getWidth() : 0;
41989         tw += this.editicon ? this.editicon.getWidth() : 0;
41990         var x = w - tw;
41991         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
41992             
41993         this.combo.trigger.setStyle('left', '0px');
41994         
41995         if(this.list && this.listWidth === undefined){
41996             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
41997             this.list.setWidth(lw);
41998             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41999         }
42000         
42001     
42002         
42003     },
42004     
42005     addItem: function(rec)
42006     {
42007         var valueField = this.combo.valueField;
42008         var displayField = this.combo.displayField;
42009         
42010         if (this.items.indexOfKey(rec[valueField]) > -1) {
42011             //console.log("GOT " + rec.data.id);
42012             return;
42013         }
42014         
42015         var x = new Roo.form.ComboBoxArray.Item({
42016             //id : rec[this.idField],
42017             data : rec,
42018             displayField : displayField ,
42019             tipField : displayField ,
42020             cb : this
42021         });
42022         // use the 
42023         this.items.add(rec[valueField],x);
42024         // add it before the element..
42025         this.updateHiddenEl();
42026         x.render(this.outerWrap, this.wrap.dom);
42027         // add the image handler..
42028     },
42029     
42030     updateHiddenEl : function()
42031     {
42032         this.validate();
42033         if (!this.hiddenEl) {
42034             return;
42035         }
42036         var ar = [];
42037         var idField = this.combo.valueField;
42038         
42039         this.items.each(function(f) {
42040             ar.push(f.data[idField]);
42041         });
42042         this.hiddenEl.dom.value = ar.join(this.seperator);
42043         this.validate();
42044     },
42045     
42046     reset : function()
42047     {
42048         this.items.clear();
42049         
42050         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
42051            el.remove();
42052         });
42053         
42054         this.el.dom.value = '';
42055         if (this.hiddenEl) {
42056             this.hiddenEl.dom.value = '';
42057         }
42058         
42059     },
42060     getValue: function()
42061     {
42062         return this.hiddenEl ? this.hiddenEl.dom.value : '';
42063     },
42064     setValue: function(v) // not a valid action - must use addItems..
42065     {
42066         
42067         this.reset();
42068          
42069         if (this.store.isLocal && (typeof(v) == 'string')) {
42070             // then we can use the store to find the values..
42071             // comma seperated at present.. this needs to allow JSON based encoding..
42072             this.hiddenEl.value  = v;
42073             var v_ar = [];
42074             Roo.each(v.split(this.seperator), function(k) {
42075                 Roo.log("CHECK " + this.valueField + ',' + k);
42076                 var li = this.store.query(this.valueField, k);
42077                 if (!li.length) {
42078                     return;
42079                 }
42080                 var add = {};
42081                 add[this.valueField] = k;
42082                 add[this.displayField] = li.item(0).data[this.displayField];
42083                 
42084                 this.addItem(add);
42085             }, this) 
42086              
42087         }
42088         if (typeof(v) == 'object' ) {
42089             // then let's assume it's an array of objects..
42090             Roo.each(v, function(l) {
42091                 var add = l;
42092                 if (typeof(l) == 'string') {
42093                     add = {};
42094                     add[this.valueField] = l;
42095                     add[this.displayField] = l
42096                 }
42097                 this.addItem(add);
42098             }, this);
42099              
42100         }
42101         
42102         
42103     },
42104     setFromData: function(v)
42105     {
42106         // this recieves an object, if setValues is called.
42107         this.reset();
42108         this.el.dom.value = v[this.displayField];
42109         this.hiddenEl.dom.value = v[this.valueField];
42110         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
42111             return;
42112         }
42113         var kv = v[this.valueField];
42114         var dv = v[this.displayField];
42115         kv = typeof(kv) != 'string' ? '' : kv;
42116         dv = typeof(dv) != 'string' ? '' : dv;
42117         
42118         
42119         var keys = kv.split(this.seperator);
42120         var display = dv.split(this.seperator);
42121         for (var i = 0 ; i < keys.length; i++) {
42122             add = {};
42123             add[this.valueField] = keys[i];
42124             add[this.displayField] = display[i];
42125             this.addItem(add);
42126         }
42127       
42128         
42129     },
42130     
42131     /**
42132      * Validates the combox array value
42133      * @return {Boolean} True if the value is valid, else false
42134      */
42135     validate : function(){
42136         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
42137             this.clearInvalid();
42138             return true;
42139         }
42140         return false;
42141     },
42142     
42143     validateValue : function(value){
42144         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
42145         
42146     },
42147     
42148     /*@
42149      * overide
42150      * 
42151      */
42152     isDirty : function() {
42153         if(this.disabled) {
42154             return false;
42155         }
42156         
42157         try {
42158             var d = Roo.decode(String(this.originalValue));
42159         } catch (e) {
42160             return String(this.getValue()) !== String(this.originalValue);
42161         }
42162         
42163         var originalValue = [];
42164         
42165         for (var i = 0; i < d.length; i++){
42166             originalValue.push(d[i][this.valueField]);
42167         }
42168         
42169         return String(this.getValue()) !== String(originalValue.join(this.seperator));
42170         
42171     }
42172     
42173 });
42174
42175
42176
42177 /**
42178  * @class Roo.form.ComboBoxArray.Item
42179  * @extends Roo.BoxComponent
42180  * A selected item in the list
42181  *  Fred [x]  Brian [x]  [Pick another |v]
42182  * 
42183  * @constructor
42184  * Create a new item.
42185  * @param {Object} config Configuration options
42186  */
42187  
42188 Roo.form.ComboBoxArray.Item = function(config) {
42189     config.id = Roo.id();
42190     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
42191 }
42192
42193 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
42194     data : {},
42195     cb: false,
42196     displayField : false,
42197     tipField : false,
42198     
42199     
42200     defaultAutoCreate : {
42201         tag: 'div',
42202         cls: 'x-cbarray-item',
42203         cn : [ 
42204             { tag: 'div' },
42205             {
42206                 tag: 'img',
42207                 width:16,
42208                 height : 16,
42209                 src : Roo.BLANK_IMAGE_URL ,
42210                 align: 'center'
42211             }
42212         ]
42213         
42214     },
42215     
42216  
42217     onRender : function(ct, position)
42218     {
42219         Roo.form.Field.superclass.onRender.call(this, ct, position);
42220         
42221         if(!this.el){
42222             var cfg = this.getAutoCreate();
42223             this.el = ct.createChild(cfg, position);
42224         }
42225         
42226         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
42227         
42228         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
42229             this.cb.renderer(this.data) :
42230             String.format('{0}',this.data[this.displayField]);
42231         
42232             
42233         this.el.child('div').dom.setAttribute('qtip',
42234                         String.format('{0}',this.data[this.tipField])
42235         );
42236         
42237         this.el.child('img').on('click', this.remove, this);
42238         
42239     },
42240    
42241     remove : function()
42242     {
42243         if(this.cb.disabled){
42244             return;
42245         }
42246         
42247         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
42248             this.cb.items.remove(this);
42249             this.el.child('img').un('click', this.remove, this);
42250             this.el.remove();
42251             this.cb.updateHiddenEl();
42252
42253             this.cb.fireEvent('remove', this.cb, this);
42254         }
42255         
42256     }
42257 });/*
42258  * RooJS Library 1.1.1
42259  * Copyright(c) 2008-2011  Alan Knowles
42260  *
42261  * License - LGPL
42262  */
42263  
42264
42265 /**
42266  * @class Roo.form.ComboNested
42267  * @extends Roo.form.ComboBox
42268  * A combobox for that allows selection of nested items in a list,
42269  * eg.
42270  *
42271  *  Book
42272  *    -> red
42273  *    -> green
42274  *  Table
42275  *    -> square
42276  *      ->red
42277  *      ->green
42278  *    -> rectangle
42279  *      ->green
42280  *      
42281  * 
42282  * @constructor
42283  * Create a new ComboNested
42284  * @param {Object} config Configuration options
42285  */
42286 Roo.form.ComboNested = function(config){
42287     Roo.form.ComboCheck.superclass.constructor.call(this, config);
42288     // should verify some data...
42289     // like
42290     // hiddenName = required..
42291     // displayField = required
42292     // valudField == required
42293     var req= [ 'hiddenName', 'displayField', 'valueField' ];
42294     var _t = this;
42295     Roo.each(req, function(e) {
42296         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
42297             throw "Roo.form.ComboNested : missing value for: " + e;
42298         }
42299     });
42300      
42301     
42302 };
42303
42304 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
42305    
42306     /*
42307      * @config {Number} max Number of columns to show
42308      */
42309     
42310     maxColumns : 3,
42311    
42312     list : null, // the outermost div..
42313     innerLists : null, // the
42314     views : null,
42315     stores : null,
42316     // private
42317     loadingChildren : false,
42318     
42319     onRender : function(ct, position)
42320     {
42321         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
42322         
42323         if(this.hiddenName){
42324             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
42325                     'before', true);
42326             this.hiddenField.value =
42327                 this.hiddenValue !== undefined ? this.hiddenValue :
42328                 this.value !== undefined ? this.value : '';
42329
42330             // prevent input submission
42331             this.el.dom.removeAttribute('name');
42332              
42333              
42334         }
42335         
42336         if(Roo.isGecko){
42337             this.el.dom.setAttribute('autocomplete', 'off');
42338         }
42339
42340         var cls = 'x-combo-list';
42341
42342         this.list = new Roo.Layer({
42343             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
42344         });
42345
42346         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
42347         this.list.setWidth(lw);
42348         this.list.swallowEvent('mousewheel');
42349         this.assetHeight = 0;
42350
42351         if(this.title){
42352             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
42353             this.assetHeight += this.header.getHeight();
42354         }
42355         this.innerLists = [];
42356         this.views = [];
42357         this.stores = [];
42358         for (var i =0 ; i < this.maxColumns; i++) {
42359             this.onRenderList( cls, i);
42360         }
42361         
42362         // always needs footer, as we are going to have an 'OK' button.
42363         this.footer = this.list.createChild({cls:cls+'-ft'});
42364         this.pageTb = new Roo.Toolbar(this.footer);  
42365         var _this = this;
42366         this.pageTb.add(  {
42367             
42368             text: 'Done',
42369             handler: function()
42370             {
42371                 _this.collapse();
42372             }
42373         });
42374         
42375         if ( this.allowBlank && !this.disableClear) {
42376             
42377             this.pageTb.add(new Roo.Toolbar.Fill(), {
42378                 cls: 'x-btn-icon x-btn-clear',
42379                 text: '&#160;',
42380                 handler: function()
42381                 {
42382                     _this.collapse();
42383                     _this.clearValue();
42384                     _this.onSelect(false, -1);
42385                 }
42386             });
42387         }
42388         if (this.footer) {
42389             this.assetHeight += this.footer.getHeight();
42390         }
42391         
42392     },
42393     onRenderList : function (  cls, i)
42394     {
42395         
42396         var lw = Math.floor(
42397                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
42398         );
42399         
42400         this.list.setWidth(lw); // default to '1'
42401
42402         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
42403         //il.on('mouseover', this.onViewOver, this, { list:  i });
42404         //il.on('mousemove', this.onViewMove, this, { list:  i });
42405         il.setWidth(lw);
42406         il.setStyle({ 'overflow-x' : 'hidden'});
42407
42408         if(!this.tpl){
42409             this.tpl = new Roo.Template({
42410                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
42411                 isEmpty: function (value, allValues) {
42412                     //Roo.log(value);
42413                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
42414                     return dl ? 'has-children' : 'no-children'
42415                 }
42416             });
42417         }
42418         
42419         var store  = this.store;
42420         if (i > 0) {
42421             store  = new Roo.data.SimpleStore({
42422                 //fields : this.store.reader.meta.fields,
42423                 reader : this.store.reader,
42424                 data : [ ]
42425             });
42426         }
42427         this.stores[i]  = store;
42428                   
42429         var view = this.views[i] = new Roo.View(
42430             il,
42431             this.tpl,
42432             {
42433                 singleSelect:true,
42434                 store: store,
42435                 selectedClass: this.selectedClass
42436             }
42437         );
42438         view.getEl().setWidth(lw);
42439         view.getEl().setStyle({
42440             position: i < 1 ? 'relative' : 'absolute',
42441             top: 0,
42442             left: (i * lw ) + 'px',
42443             display : i > 0 ? 'none' : 'block'
42444         });
42445         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
42446         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
42447         //view.on('click', this.onViewClick, this, { list : i });
42448
42449         store.on('beforeload', this.onBeforeLoad, this);
42450         store.on('load',  this.onLoad, this, { list  : i});
42451         store.on('loadexception', this.onLoadException, this);
42452
42453         // hide the other vies..
42454         
42455         
42456         
42457     },
42458       
42459     restrictHeight : function()
42460     {
42461         var mh = 0;
42462         Roo.each(this.innerLists, function(il,i) {
42463             var el = this.views[i].getEl();
42464             el.dom.style.height = '';
42465             var inner = el.dom;
42466             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
42467             // only adjust heights on other ones..
42468             mh = Math.max(h, mh);
42469             if (i < 1) {
42470                 
42471                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
42472                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
42473                
42474             }
42475             
42476             
42477         }, this);
42478         
42479         this.list.beginUpdate();
42480         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
42481         this.list.alignTo(this.el, this.listAlign);
42482         this.list.endUpdate();
42483         
42484     },
42485      
42486     
42487     // -- store handlers..
42488     // private
42489     onBeforeLoad : function()
42490     {
42491         if(!this.hasFocus){
42492             return;
42493         }
42494         this.innerLists[0].update(this.loadingText ?
42495                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
42496         this.restrictHeight();
42497         this.selectedIndex = -1;
42498     },
42499     // private
42500     onLoad : function(a,b,c,d)
42501     {
42502         if (!this.loadingChildren) {
42503             // then we are loading the top level. - hide the children
42504             for (var i = 1;i < this.views.length; i++) {
42505                 this.views[i].getEl().setStyle({ display : 'none' });
42506             }
42507             var lw = Math.floor(
42508                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
42509             );
42510         
42511              this.list.setWidth(lw); // default to '1'
42512
42513             
42514         }
42515         if(!this.hasFocus){
42516             return;
42517         }
42518         
42519         if(this.store.getCount() > 0) {
42520             this.expand();
42521             this.restrictHeight();   
42522         } else {
42523             this.onEmptyResults();
42524         }
42525         
42526         if (!this.loadingChildren) {
42527             this.selectActive();
42528         }
42529         /*
42530         this.stores[1].loadData([]);
42531         this.stores[2].loadData([]);
42532         this.views
42533         */    
42534     
42535         //this.el.focus();
42536     },
42537     
42538     
42539     // private
42540     onLoadException : function()
42541     {
42542         this.collapse();
42543         Roo.log(this.store.reader.jsonData);
42544         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
42545             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
42546         }
42547         
42548         
42549     },
42550     // no cleaning of leading spaces on blur here.
42551     cleanLeadingSpace : function(e) { },
42552     
42553
42554     onSelectChange : function (view, sels, opts )
42555     {
42556         var ix = view.getSelectedIndexes();
42557          
42558         if (opts.list > this.maxColumns - 2) {
42559             if (view.store.getCount()<  1) {
42560                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
42561
42562             } else  {
42563                 if (ix.length) {
42564                     // used to clear ?? but if we are loading unselected 
42565                     this.setFromData(view.store.getAt(ix[0]).data);
42566                 }
42567                 
42568             }
42569             
42570             return;
42571         }
42572         
42573         if (!ix.length) {
42574             // this get's fired when trigger opens..
42575            // this.setFromData({});
42576             var str = this.stores[opts.list+1];
42577             str.data.clear(); // removeall wihtout the fire events..
42578             return;
42579         }
42580         
42581         var rec = view.store.getAt(ix[0]);
42582          
42583         this.setFromData(rec.data);
42584         this.fireEvent('select', this, rec, ix[0]);
42585         
42586         var lw = Math.floor(
42587              (
42588                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
42589              ) / this.maxColumns
42590         );
42591         this.loadingChildren = true;
42592         this.stores[opts.list+1].loadDataFromChildren( rec );
42593         this.loadingChildren = false;
42594         var dl = this.stores[opts.list+1]. getTotalCount();
42595         
42596         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
42597         
42598         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
42599         for (var i = opts.list+2; i < this.views.length;i++) {
42600             this.views[i].getEl().setStyle({ display : 'none' });
42601         }
42602         
42603         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
42604         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
42605         
42606         if (this.isLoading) {
42607            // this.selectActive(opts.list);
42608         }
42609          
42610     },
42611     
42612     
42613     
42614     
42615     onDoubleClick : function()
42616     {
42617         this.collapse(); //??
42618     },
42619     
42620      
42621     
42622     
42623     
42624     // private
42625     recordToStack : function(store, prop, value, stack)
42626     {
42627         var cstore = new Roo.data.SimpleStore({
42628             //fields : this.store.reader.meta.fields, // we need array reader.. for
42629             reader : this.store.reader,
42630             data : [ ]
42631         });
42632         var _this = this;
42633         var record  = false;
42634         var srec = false;
42635         if(store.getCount() < 1){
42636             return false;
42637         }
42638         store.each(function(r){
42639             if(r.data[prop] == value){
42640                 record = r;
42641             srec = r;
42642                 return false;
42643             }
42644             if (r.data.cn && r.data.cn.length) {
42645                 cstore.loadDataFromChildren( r);
42646                 var cret = _this.recordToStack(cstore, prop, value, stack);
42647                 if (cret !== false) {
42648                     record = cret;
42649                     srec = r;
42650                     return false;
42651                 }
42652             }
42653              
42654             return true;
42655         });
42656         if (record == false) {
42657             return false
42658         }
42659         stack.unshift(srec);
42660         return record;
42661     },
42662     
42663     /*
42664      * find the stack of stores that match our value.
42665      *
42666      * 
42667      */
42668     
42669     selectActive : function ()
42670     {
42671         // if store is not loaded, then we will need to wait for that to happen first.
42672         var stack = [];
42673         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
42674         for (var i = 0; i < stack.length; i++ ) {
42675             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
42676         }
42677         
42678     }
42679         
42680          
42681     
42682     
42683     
42684     
42685 });/*
42686  * Based on:
42687  * Ext JS Library 1.1.1
42688  * Copyright(c) 2006-2007, Ext JS, LLC.
42689  *
42690  * Originally Released Under LGPL - original licence link has changed is not relivant.
42691  *
42692  * Fork - LGPL
42693  * <script type="text/javascript">
42694  */
42695 /**
42696  * @class Roo.form.Checkbox
42697  * @extends Roo.form.Field
42698  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
42699  * @constructor
42700  * Creates a new Checkbox
42701  * @param {Object} config Configuration options
42702  */
42703 Roo.form.Checkbox = function(config){
42704     Roo.form.Checkbox.superclass.constructor.call(this, config);
42705     this.addEvents({
42706         /**
42707          * @event check
42708          * Fires when the checkbox is checked or unchecked.
42709              * @param {Roo.form.Checkbox} this This checkbox
42710              * @param {Boolean} checked The new checked value
42711              */
42712         check : true
42713     });
42714 };
42715
42716 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
42717     /**
42718      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
42719      */
42720     focusClass : undefined,
42721     /**
42722      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
42723      */
42724     fieldClass: "x-form-field",
42725     /**
42726      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
42727      */
42728     checked: false,
42729     /**
42730      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42731      * {tag: "input", type: "checkbox", autocomplete: "off"})
42732      */
42733     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
42734     /**
42735      * @cfg {String} boxLabel The text that appears beside the checkbox
42736      */
42737     boxLabel : "",
42738     /**
42739      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
42740      */  
42741     inputValue : '1',
42742     /**
42743      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
42744      */
42745      valueOff: '0', // value when not checked..
42746
42747     actionMode : 'viewEl', 
42748     //
42749     // private
42750     itemCls : 'x-menu-check-item x-form-item',
42751     groupClass : 'x-menu-group-item',
42752     inputType : 'hidden',
42753     
42754     
42755     inSetChecked: false, // check that we are not calling self...
42756     
42757     inputElement: false, // real input element?
42758     basedOn: false, // ????
42759     
42760     isFormField: true, // not sure where this is needed!!!!
42761
42762     onResize : function(){
42763         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
42764         if(!this.boxLabel){
42765             this.el.alignTo(this.wrap, 'c-c');
42766         }
42767     },
42768
42769     initEvents : function(){
42770         Roo.form.Checkbox.superclass.initEvents.call(this);
42771         this.el.on("click", this.onClick,  this);
42772         this.el.on("change", this.onClick,  this);
42773     },
42774
42775
42776     getResizeEl : function(){
42777         return this.wrap;
42778     },
42779
42780     getPositionEl : function(){
42781         return this.wrap;
42782     },
42783
42784     // private
42785     onRender : function(ct, position){
42786         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
42787         /*
42788         if(this.inputValue !== undefined){
42789             this.el.dom.value = this.inputValue;
42790         }
42791         */
42792         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
42793         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
42794         var viewEl = this.wrap.createChild({ 
42795             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
42796         this.viewEl = viewEl;   
42797         this.wrap.on('click', this.onClick,  this); 
42798         
42799         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
42800         this.el.on('propertychange', this.setFromHidden,  this);  //ie
42801         
42802         
42803         
42804         if(this.boxLabel){
42805             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
42806         //    viewEl.on('click', this.onClick,  this); 
42807         }
42808         //if(this.checked){
42809             this.setChecked(this.checked);
42810         //}else{
42811             //this.checked = this.el.dom;
42812         //}
42813
42814     },
42815
42816     // private
42817     initValue : Roo.emptyFn,
42818
42819     /**
42820      * Returns the checked state of the checkbox.
42821      * @return {Boolean} True if checked, else false
42822      */
42823     getValue : function(){
42824         if(this.el){
42825             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
42826         }
42827         return this.valueOff;
42828         
42829     },
42830
42831         // private
42832     onClick : function(){ 
42833         if (this.disabled) {
42834             return;
42835         }
42836         this.setChecked(!this.checked);
42837
42838         //if(this.el.dom.checked != this.checked){
42839         //    this.setValue(this.el.dom.checked);
42840        // }
42841     },
42842
42843     /**
42844      * Sets the checked state of the checkbox.
42845      * On is always based on a string comparison between inputValue and the param.
42846      * @param {Boolean/String} value - the value to set 
42847      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
42848      */
42849     setValue : function(v,suppressEvent){
42850         
42851         
42852         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
42853         //if(this.el && this.el.dom){
42854         //    this.el.dom.checked = this.checked;
42855         //    this.el.dom.defaultChecked = this.checked;
42856         //}
42857         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
42858         //this.fireEvent("check", this, this.checked);
42859     },
42860     // private..
42861     setChecked : function(state,suppressEvent)
42862     {
42863         if (this.inSetChecked) {
42864             this.checked = state;
42865             return;
42866         }
42867         
42868     
42869         if(this.wrap){
42870             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
42871         }
42872         this.checked = state;
42873         if(suppressEvent !== true){
42874             this.fireEvent('check', this, state);
42875         }
42876         this.inSetChecked = true;
42877         this.el.dom.value = state ? this.inputValue : this.valueOff;
42878         this.inSetChecked = false;
42879         
42880     },
42881     // handle setting of hidden value by some other method!!?!?
42882     setFromHidden: function()
42883     {
42884         if(!this.el){
42885             return;
42886         }
42887         //console.log("SET FROM HIDDEN");
42888         //alert('setFrom hidden');
42889         this.setValue(this.el.dom.value);
42890     },
42891     
42892     onDestroy : function()
42893     {
42894         if(this.viewEl){
42895             Roo.get(this.viewEl).remove();
42896         }
42897          
42898         Roo.form.Checkbox.superclass.onDestroy.call(this);
42899     },
42900     
42901     setBoxLabel : function(str)
42902     {
42903         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
42904     }
42905
42906 });/*
42907  * Based on:
42908  * Ext JS Library 1.1.1
42909  * Copyright(c) 2006-2007, Ext JS, LLC.
42910  *
42911  * Originally Released Under LGPL - original licence link has changed is not relivant.
42912  *
42913  * Fork - LGPL
42914  * <script type="text/javascript">
42915  */
42916  
42917 /**
42918  * @class Roo.form.Radio
42919  * @extends Roo.form.Checkbox
42920  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
42921  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
42922  * @constructor
42923  * Creates a new Radio
42924  * @param {Object} config Configuration options
42925  */
42926 Roo.form.Radio = function(){
42927     Roo.form.Radio.superclass.constructor.apply(this, arguments);
42928 };
42929 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
42930     inputType: 'radio',
42931
42932     /**
42933      * If this radio is part of a group, it will return the selected value
42934      * @return {String}
42935      */
42936     getGroupValue : function(){
42937         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
42938     },
42939     
42940     
42941     onRender : function(ct, position){
42942         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
42943         
42944         if(this.inputValue !== undefined){
42945             this.el.dom.value = this.inputValue;
42946         }
42947          
42948         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
42949         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
42950         //var viewEl = this.wrap.createChild({ 
42951         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
42952         //this.viewEl = viewEl;   
42953         //this.wrap.on('click', this.onClick,  this); 
42954         
42955         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
42956         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
42957         
42958         
42959         
42960         if(this.boxLabel){
42961             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
42962         //    viewEl.on('click', this.onClick,  this); 
42963         }
42964          if(this.checked){
42965             this.el.dom.checked =   'checked' ;
42966         }
42967          
42968     } 
42969     
42970     
42971 });//<script type="text/javascript">
42972
42973 /*
42974  * Based  Ext JS Library 1.1.1
42975  * Copyright(c) 2006-2007, Ext JS, LLC.
42976  * LGPL
42977  *
42978  */
42979  
42980 /**
42981  * @class Roo.HtmlEditorCore
42982  * @extends Roo.Component
42983  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
42984  *
42985  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42986  */
42987
42988 Roo.HtmlEditorCore = function(config){
42989     
42990     
42991     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
42992     
42993     
42994     this.addEvents({
42995         /**
42996          * @event initialize
42997          * Fires when the editor is fully initialized (including the iframe)
42998          * @param {Roo.HtmlEditorCore} this
42999          */
43000         initialize: true,
43001         /**
43002          * @event activate
43003          * Fires when the editor is first receives the focus. Any insertion must wait
43004          * until after this event.
43005          * @param {Roo.HtmlEditorCore} this
43006          */
43007         activate: true,
43008          /**
43009          * @event beforesync
43010          * Fires before the textarea is updated with content from the editor iframe. Return false
43011          * to cancel the sync.
43012          * @param {Roo.HtmlEditorCore} this
43013          * @param {String} html
43014          */
43015         beforesync: true,
43016          /**
43017          * @event beforepush
43018          * Fires before the iframe editor is updated with content from the textarea. Return false
43019          * to cancel the push.
43020          * @param {Roo.HtmlEditorCore} this
43021          * @param {String} html
43022          */
43023         beforepush: true,
43024          /**
43025          * @event sync
43026          * Fires when the textarea is updated with content from the editor iframe.
43027          * @param {Roo.HtmlEditorCore} this
43028          * @param {String} html
43029          */
43030         sync: true,
43031          /**
43032          * @event push
43033          * Fires when the iframe editor is updated with content from the textarea.
43034          * @param {Roo.HtmlEditorCore} this
43035          * @param {String} html
43036          */
43037         push: true,
43038         
43039         /**
43040          * @event editorevent
43041          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
43042          * @param {Roo.HtmlEditorCore} this
43043          */
43044         editorevent: true
43045         
43046     });
43047     
43048     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
43049     
43050     // defaults : white / black...
43051     this.applyBlacklists();
43052     
43053     
43054     
43055 };
43056
43057
43058 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
43059
43060
43061      /**
43062      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
43063      */
43064     
43065     owner : false,
43066     
43067      /**
43068      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
43069      *                        Roo.resizable.
43070      */
43071     resizable : false,
43072      /**
43073      * @cfg {Number} height (in pixels)
43074      */   
43075     height: 300,
43076    /**
43077      * @cfg {Number} width (in pixels)
43078      */   
43079     width: 500,
43080     
43081     /**
43082      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
43083      * 
43084      */
43085     stylesheets: false,
43086     
43087     // id of frame..
43088     frameId: false,
43089     
43090     // private properties
43091     validationEvent : false,
43092     deferHeight: true,
43093     initialized : false,
43094     activated : false,
43095     sourceEditMode : false,
43096     onFocus : Roo.emptyFn,
43097     iframePad:3,
43098     hideMode:'offsets',
43099     
43100     clearUp: true,
43101     
43102     // blacklist + whitelisted elements..
43103     black: false,
43104     white: false,
43105      
43106     bodyCls : '',
43107
43108     /**
43109      * Protected method that will not generally be called directly. It
43110      * is called when the editor initializes the iframe with HTML contents. Override this method if you
43111      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
43112      */
43113     getDocMarkup : function(){
43114         // body styles..
43115         var st = '';
43116         
43117         // inherit styels from page...?? 
43118         if (this.stylesheets === false) {
43119             
43120             Roo.get(document.head).select('style').each(function(node) {
43121                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
43122             });
43123             
43124             Roo.get(document.head).select('link').each(function(node) { 
43125                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
43126             });
43127             
43128         } else if (!this.stylesheets.length) {
43129                 // simple..
43130                 st = '<style type="text/css">' +
43131                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
43132                    '</style>';
43133         } else { 
43134             st = '<style type="text/css">' +
43135                     this.stylesheets +
43136                 '</style>';
43137         }
43138         
43139         st +=  '<style type="text/css">' +
43140             'IMG { cursor: pointer } ' +
43141         '</style>';
43142
43143         var cls = 'roo-htmleditor-body';
43144         
43145         if(this.bodyCls.length){
43146             cls += ' ' + this.bodyCls;
43147         }
43148         
43149         return '<html><head>' + st  +
43150             //<style type="text/css">' +
43151             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
43152             //'</style>' +
43153             ' </head><body class="' +  cls + '"></body></html>';
43154     },
43155
43156     // private
43157     onRender : function(ct, position)
43158     {
43159         var _t = this;
43160         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
43161         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
43162         
43163         
43164         this.el.dom.style.border = '0 none';
43165         this.el.dom.setAttribute('tabIndex', -1);
43166         this.el.addClass('x-hidden hide');
43167         
43168         
43169         
43170         if(Roo.isIE){ // fix IE 1px bogus margin
43171             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
43172         }
43173        
43174         
43175         this.frameId = Roo.id();
43176         
43177          
43178         
43179         var iframe = this.owner.wrap.createChild({
43180             tag: 'iframe',
43181             cls: 'form-control', // bootstrap..
43182             id: this.frameId,
43183             name: this.frameId,
43184             frameBorder : 'no',
43185             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
43186         }, this.el
43187         );
43188         
43189         
43190         this.iframe = iframe.dom;
43191
43192          this.assignDocWin();
43193         
43194         this.doc.designMode = 'on';
43195        
43196         this.doc.open();
43197         this.doc.write(this.getDocMarkup());
43198         this.doc.close();
43199
43200         
43201         var task = { // must defer to wait for browser to be ready
43202             run : function(){
43203                 //console.log("run task?" + this.doc.readyState);
43204                 this.assignDocWin();
43205                 if(this.doc.body || this.doc.readyState == 'complete'){
43206                     try {
43207                         this.doc.designMode="on";
43208                     } catch (e) {
43209                         return;
43210                     }
43211                     Roo.TaskMgr.stop(task);
43212                     this.initEditor.defer(10, this);
43213                 }
43214             },
43215             interval : 10,
43216             duration: 10000,
43217             scope: this
43218         };
43219         Roo.TaskMgr.start(task);
43220
43221     },
43222
43223     // private
43224     onResize : function(w, h)
43225     {
43226          Roo.log('resize: ' +w + ',' + h );
43227         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
43228         if(!this.iframe){
43229             return;
43230         }
43231         if(typeof w == 'number'){
43232             
43233             this.iframe.style.width = w + 'px';
43234         }
43235         if(typeof h == 'number'){
43236             
43237             this.iframe.style.height = h + 'px';
43238             if(this.doc){
43239                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
43240             }
43241         }
43242         
43243     },
43244
43245     /**
43246      * Toggles the editor between standard and source edit mode.
43247      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
43248      */
43249     toggleSourceEdit : function(sourceEditMode){
43250         
43251         this.sourceEditMode = sourceEditMode === true;
43252         
43253         if(this.sourceEditMode){
43254  
43255             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
43256             
43257         }else{
43258             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
43259             //this.iframe.className = '';
43260             this.deferFocus();
43261         }
43262         //this.setSize(this.owner.wrap.getSize());
43263         //this.fireEvent('editmodechange', this, this.sourceEditMode);
43264     },
43265
43266     
43267   
43268
43269     /**
43270      * Protected method that will not generally be called directly. If you need/want
43271      * custom HTML cleanup, this is the method you should override.
43272      * @param {String} html The HTML to be cleaned
43273      * return {String} The cleaned HTML
43274      */
43275     cleanHtml : function(html){
43276         html = String(html);
43277         if(html.length > 5){
43278             if(Roo.isSafari){ // strip safari nonsense
43279                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
43280             }
43281         }
43282         if(html == '&nbsp;'){
43283             html = '';
43284         }
43285         return html;
43286     },
43287
43288     /**
43289      * HTML Editor -> Textarea
43290      * Protected method that will not generally be called directly. Syncs the contents
43291      * of the editor iframe with the textarea.
43292      */
43293     syncValue : function(){
43294         if(this.initialized){
43295             var bd = (this.doc.body || this.doc.documentElement);
43296             //this.cleanUpPaste(); -- this is done else where and causes havoc..
43297             var html = bd.innerHTML;
43298             if(Roo.isSafari){
43299                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
43300                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
43301                 if(m && m[1]){
43302                     html = '<div style="'+m[0]+'">' + html + '</div>';
43303                 }
43304             }
43305             html = this.cleanHtml(html);
43306             // fix up the special chars.. normaly like back quotes in word...
43307             // however we do not want to do this with chinese..
43308             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
43309                 
43310                 var cc = match.charCodeAt();
43311
43312                 // Get the character value, handling surrogate pairs
43313                 if (match.length == 2) {
43314                     // It's a surrogate pair, calculate the Unicode code point
43315                     var high = match.charCodeAt(0) - 0xD800;
43316                     var low  = match.charCodeAt(1) - 0xDC00;
43317                     cc = (high * 0x400) + low + 0x10000;
43318                 }  else if (
43319                     (cc >= 0x4E00 && cc < 0xA000 ) ||
43320                     (cc >= 0x3400 && cc < 0x4E00 ) ||
43321                     (cc >= 0xf900 && cc < 0xfb00 )
43322                 ) {
43323                         return match;
43324                 }  
43325          
43326                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
43327                 return "&#" + cc + ";";
43328                 
43329                 
43330             });
43331             
43332             
43333              
43334             if(this.owner.fireEvent('beforesync', this, html) !== false){
43335                 this.el.dom.value = html;
43336                 this.owner.fireEvent('sync', this, html);
43337             }
43338         }
43339     },
43340
43341     /**
43342      * Protected method that will not generally be called directly. Pushes the value of the textarea
43343      * into the iframe editor.
43344      */
43345     pushValue : function(){
43346         if(this.initialized){
43347             var v = this.el.dom.value.trim();
43348             
43349 //            if(v.length < 1){
43350 //                v = '&#160;';
43351 //            }
43352             
43353             if(this.owner.fireEvent('beforepush', this, v) !== false){
43354                 var d = (this.doc.body || this.doc.documentElement);
43355                 d.innerHTML = v;
43356                 this.cleanUpPaste();
43357                 this.el.dom.value = d.innerHTML;
43358                 this.owner.fireEvent('push', this, v);
43359             }
43360         }
43361     },
43362
43363     // private
43364     deferFocus : function(){
43365         this.focus.defer(10, this);
43366     },
43367
43368     // doc'ed in Field
43369     focus : function(){
43370         if(this.win && !this.sourceEditMode){
43371             this.win.focus();
43372         }else{
43373             this.el.focus();
43374         }
43375     },
43376     
43377     assignDocWin: function()
43378     {
43379         var iframe = this.iframe;
43380         
43381          if(Roo.isIE){
43382             this.doc = iframe.contentWindow.document;
43383             this.win = iframe.contentWindow;
43384         } else {
43385 //            if (!Roo.get(this.frameId)) {
43386 //                return;
43387 //            }
43388 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43389 //            this.win = Roo.get(this.frameId).dom.contentWindow;
43390             
43391             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
43392                 return;
43393             }
43394             
43395             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43396             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
43397         }
43398     },
43399     
43400     // private
43401     initEditor : function(){
43402         //console.log("INIT EDITOR");
43403         this.assignDocWin();
43404         
43405         
43406         
43407         this.doc.designMode="on";
43408         this.doc.open();
43409         this.doc.write(this.getDocMarkup());
43410         this.doc.close();
43411         
43412         var dbody = (this.doc.body || this.doc.documentElement);
43413         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
43414         // this copies styles from the containing element into thsi one..
43415         // not sure why we need all of this..
43416         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
43417         
43418         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
43419         //ss['background-attachment'] = 'fixed'; // w3c
43420         dbody.bgProperties = 'fixed'; // ie
43421         //Roo.DomHelper.applyStyles(dbody, ss);
43422         Roo.EventManager.on(this.doc, {
43423             //'mousedown': this.onEditorEvent,
43424             'mouseup': this.onEditorEvent,
43425             'dblclick': this.onEditorEvent,
43426             'click': this.onEditorEvent,
43427             'keyup': this.onEditorEvent,
43428             buffer:100,
43429             scope: this
43430         });
43431         if(Roo.isGecko){
43432             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
43433         }
43434         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
43435             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
43436         }
43437         this.initialized = true;
43438
43439         this.owner.fireEvent('initialize', this);
43440         this.pushValue();
43441     },
43442
43443     // private
43444     onDestroy : function(){
43445         
43446         
43447         
43448         if(this.rendered){
43449             
43450             //for (var i =0; i < this.toolbars.length;i++) {
43451             //    // fixme - ask toolbars for heights?
43452             //    this.toolbars[i].onDestroy();
43453            // }
43454             
43455             //this.wrap.dom.innerHTML = '';
43456             //this.wrap.remove();
43457         }
43458     },
43459
43460     // private
43461     onFirstFocus : function(){
43462         
43463         this.assignDocWin();
43464         
43465         
43466         this.activated = true;
43467          
43468     
43469         if(Roo.isGecko){ // prevent silly gecko errors
43470             this.win.focus();
43471             var s = this.win.getSelection();
43472             if(!s.focusNode || s.focusNode.nodeType != 3){
43473                 var r = s.getRangeAt(0);
43474                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
43475                 r.collapse(true);
43476                 this.deferFocus();
43477             }
43478             try{
43479                 this.execCmd('useCSS', true);
43480                 this.execCmd('styleWithCSS', false);
43481             }catch(e){}
43482         }
43483         this.owner.fireEvent('activate', this);
43484     },
43485
43486     // private
43487     adjustFont: function(btn){
43488         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
43489         //if(Roo.isSafari){ // safari
43490         //    adjust *= 2;
43491        // }
43492         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
43493         if(Roo.isSafari){ // safari
43494             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
43495             v =  (v < 10) ? 10 : v;
43496             v =  (v > 48) ? 48 : v;
43497             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
43498             
43499         }
43500         
43501         
43502         v = Math.max(1, v+adjust);
43503         
43504         this.execCmd('FontSize', v  );
43505     },
43506
43507     onEditorEvent : function(e)
43508     {
43509         this.owner.fireEvent('editorevent', this, e);
43510       //  this.updateToolbar();
43511         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
43512     },
43513
43514     insertTag : function(tg)
43515     {
43516         // could be a bit smarter... -> wrap the current selected tRoo..
43517         if (tg.toLowerCase() == 'span' ||
43518             tg.toLowerCase() == 'code' ||
43519             tg.toLowerCase() == 'sup' ||
43520             tg.toLowerCase() == 'sub' 
43521             ) {
43522             
43523             range = this.createRange(this.getSelection());
43524             var wrappingNode = this.doc.createElement(tg.toLowerCase());
43525             wrappingNode.appendChild(range.extractContents());
43526             range.insertNode(wrappingNode);
43527
43528             return;
43529             
43530             
43531             
43532         }
43533         this.execCmd("formatblock",   tg);
43534         
43535     },
43536     
43537     insertText : function(txt)
43538     {
43539         
43540         
43541         var range = this.createRange();
43542         range.deleteContents();
43543                //alert(Sender.getAttribute('label'));
43544                
43545         range.insertNode(this.doc.createTextNode(txt));
43546     } ,
43547     
43548      
43549
43550     /**
43551      * Executes a Midas editor command on the editor document and performs necessary focus and
43552      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
43553      * @param {String} cmd The Midas command
43554      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
43555      */
43556     relayCmd : function(cmd, value){
43557         this.win.focus();
43558         this.execCmd(cmd, value);
43559         this.owner.fireEvent('editorevent', this);
43560         //this.updateToolbar();
43561         this.owner.deferFocus();
43562     },
43563
43564     /**
43565      * Executes a Midas editor command directly on the editor document.
43566      * For visual commands, you should use {@link #relayCmd} instead.
43567      * <b>This should only be called after the editor is initialized.</b>
43568      * @param {String} cmd The Midas command
43569      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
43570      */
43571     execCmd : function(cmd, value){
43572         this.doc.execCommand(cmd, false, value === undefined ? null : value);
43573         this.syncValue();
43574     },
43575  
43576  
43577    
43578     /**
43579      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
43580      * to insert tRoo.
43581      * @param {String} text | dom node.. 
43582      */
43583     insertAtCursor : function(text)
43584     {
43585         
43586         if(!this.activated){
43587             return;
43588         }
43589         /*
43590         if(Roo.isIE){
43591             this.win.focus();
43592             var r = this.doc.selection.createRange();
43593             if(r){
43594                 r.collapse(true);
43595                 r.pasteHTML(text);
43596                 this.syncValue();
43597                 this.deferFocus();
43598             
43599             }
43600             return;
43601         }
43602         */
43603         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
43604             this.win.focus();
43605             
43606             
43607             // from jquery ui (MIT licenced)
43608             var range, node;
43609             var win = this.win;
43610             
43611             if (win.getSelection && win.getSelection().getRangeAt) {
43612                 range = win.getSelection().getRangeAt(0);
43613                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
43614                 range.insertNode(node);
43615             } else if (win.document.selection && win.document.selection.createRange) {
43616                 // no firefox support
43617                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
43618                 win.document.selection.createRange().pasteHTML(txt);
43619             } else {
43620                 // no firefox support
43621                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
43622                 this.execCmd('InsertHTML', txt);
43623             } 
43624             
43625             this.syncValue();
43626             
43627             this.deferFocus();
43628         }
43629     },
43630  // private
43631     mozKeyPress : function(e){
43632         if(e.ctrlKey){
43633             var c = e.getCharCode(), cmd;
43634           
43635             if(c > 0){
43636                 c = String.fromCharCode(c).toLowerCase();
43637                 switch(c){
43638                     case 'b':
43639                         cmd = 'bold';
43640                         break;
43641                     case 'i':
43642                         cmd = 'italic';
43643                         break;
43644                     
43645                     case 'u':
43646                         cmd = 'underline';
43647                         break;
43648                     
43649                     case 'v':
43650                         this.cleanUpPaste.defer(100, this);
43651                         return;
43652                         
43653                 }
43654                 if(cmd){
43655                     this.win.focus();
43656                     this.execCmd(cmd);
43657                     this.deferFocus();
43658                     e.preventDefault();
43659                 }
43660                 
43661             }
43662         }
43663     },
43664
43665     // private
43666     fixKeys : function(){ // load time branching for fastest keydown performance
43667         if(Roo.isIE){
43668             return function(e){
43669                 var k = e.getKey(), r;
43670                 if(k == e.TAB){
43671                     e.stopEvent();
43672                     r = this.doc.selection.createRange();
43673                     if(r){
43674                         r.collapse(true);
43675                         r.pasteHTML('&#160;&#160;&#160;&#160;');
43676                         this.deferFocus();
43677                     }
43678                     return;
43679                 }
43680                 
43681                 if(k == e.ENTER){
43682                     r = this.doc.selection.createRange();
43683                     if(r){
43684                         var target = r.parentElement();
43685                         if(!target || target.tagName.toLowerCase() != 'li'){
43686                             e.stopEvent();
43687                             r.pasteHTML('<br />');
43688                             r.collapse(false);
43689                             r.select();
43690                         }
43691                     }
43692                 }
43693                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43694                     this.cleanUpPaste.defer(100, this);
43695                     return;
43696                 }
43697                 
43698                 
43699             };
43700         }else if(Roo.isOpera){
43701             return function(e){
43702                 var k = e.getKey();
43703                 if(k == e.TAB){
43704                     e.stopEvent();
43705                     this.win.focus();
43706                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
43707                     this.deferFocus();
43708                 }
43709                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43710                     this.cleanUpPaste.defer(100, this);
43711                     return;
43712                 }
43713                 
43714             };
43715         }else if(Roo.isSafari){
43716             return function(e){
43717                 var k = e.getKey();
43718                 
43719                 if(k == e.TAB){
43720                     e.stopEvent();
43721                     this.execCmd('InsertText','\t');
43722                     this.deferFocus();
43723                     return;
43724                 }
43725                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43726                     this.cleanUpPaste.defer(100, this);
43727                     return;
43728                 }
43729                 
43730              };
43731         }
43732     }(),
43733     
43734     getAllAncestors: function()
43735     {
43736         var p = this.getSelectedNode();
43737         var a = [];
43738         if (!p) {
43739             a.push(p); // push blank onto stack..
43740             p = this.getParentElement();
43741         }
43742         
43743         
43744         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
43745             a.push(p);
43746             p = p.parentNode;
43747         }
43748         a.push(this.doc.body);
43749         return a;
43750     },
43751     lastSel : false,
43752     lastSelNode : false,
43753     
43754     
43755     getSelection : function() 
43756     {
43757         this.assignDocWin();
43758         return Roo.isIE ? this.doc.selection : this.win.getSelection();
43759     },
43760     
43761     getSelectedNode: function() 
43762     {
43763         // this may only work on Gecko!!!
43764         
43765         // should we cache this!!!!
43766         
43767         
43768         
43769          
43770         var range = this.createRange(this.getSelection()).cloneRange();
43771         
43772         if (Roo.isIE) {
43773             var parent = range.parentElement();
43774             while (true) {
43775                 var testRange = range.duplicate();
43776                 testRange.moveToElementText(parent);
43777                 if (testRange.inRange(range)) {
43778                     break;
43779                 }
43780                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
43781                     break;
43782                 }
43783                 parent = parent.parentElement;
43784             }
43785             return parent;
43786         }
43787         
43788         // is ancestor a text element.
43789         var ac =  range.commonAncestorContainer;
43790         if (ac.nodeType == 3) {
43791             ac = ac.parentNode;
43792         }
43793         
43794         var ar = ac.childNodes;
43795          
43796         var nodes = [];
43797         var other_nodes = [];
43798         var has_other_nodes = false;
43799         for (var i=0;i<ar.length;i++) {
43800             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
43801                 continue;
43802             }
43803             // fullly contained node.
43804             
43805             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
43806                 nodes.push(ar[i]);
43807                 continue;
43808             }
43809             
43810             // probably selected..
43811             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
43812                 other_nodes.push(ar[i]);
43813                 continue;
43814             }
43815             // outer..
43816             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
43817                 continue;
43818             }
43819             
43820             
43821             has_other_nodes = true;
43822         }
43823         if (!nodes.length && other_nodes.length) {
43824             nodes= other_nodes;
43825         }
43826         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
43827             return false;
43828         }
43829         
43830         return nodes[0];
43831     },
43832     createRange: function(sel)
43833     {
43834         // this has strange effects when using with 
43835         // top toolbar - not sure if it's a great idea.
43836         //this.editor.contentWindow.focus();
43837         if (typeof sel != "undefined") {
43838             try {
43839                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
43840             } catch(e) {
43841                 return this.doc.createRange();
43842             }
43843         } else {
43844             return this.doc.createRange();
43845         }
43846     },
43847     getParentElement: function()
43848     {
43849         
43850         this.assignDocWin();
43851         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
43852         
43853         var range = this.createRange(sel);
43854          
43855         try {
43856             var p = range.commonAncestorContainer;
43857             while (p.nodeType == 3) { // text node
43858                 p = p.parentNode;
43859             }
43860             return p;
43861         } catch (e) {
43862             return null;
43863         }
43864     
43865     },
43866     /***
43867      *
43868      * Range intersection.. the hard stuff...
43869      *  '-1' = before
43870      *  '0' = hits..
43871      *  '1' = after.
43872      *         [ -- selected range --- ]
43873      *   [fail]                        [fail]
43874      *
43875      *    basically..
43876      *      if end is before start or  hits it. fail.
43877      *      if start is after end or hits it fail.
43878      *
43879      *   if either hits (but other is outside. - then it's not 
43880      *   
43881      *    
43882      **/
43883     
43884     
43885     // @see http://www.thismuchiknow.co.uk/?p=64.
43886     rangeIntersectsNode : function(range, node)
43887     {
43888         var nodeRange = node.ownerDocument.createRange();
43889         try {
43890             nodeRange.selectNode(node);
43891         } catch (e) {
43892             nodeRange.selectNodeContents(node);
43893         }
43894     
43895         var rangeStartRange = range.cloneRange();
43896         rangeStartRange.collapse(true);
43897     
43898         var rangeEndRange = range.cloneRange();
43899         rangeEndRange.collapse(false);
43900     
43901         var nodeStartRange = nodeRange.cloneRange();
43902         nodeStartRange.collapse(true);
43903     
43904         var nodeEndRange = nodeRange.cloneRange();
43905         nodeEndRange.collapse(false);
43906     
43907         return rangeStartRange.compareBoundaryPoints(
43908                  Range.START_TO_START, nodeEndRange) == -1 &&
43909                rangeEndRange.compareBoundaryPoints(
43910                  Range.START_TO_START, nodeStartRange) == 1;
43911         
43912          
43913     },
43914     rangeCompareNode : function(range, node)
43915     {
43916         var nodeRange = node.ownerDocument.createRange();
43917         try {
43918             nodeRange.selectNode(node);
43919         } catch (e) {
43920             nodeRange.selectNodeContents(node);
43921         }
43922         
43923         
43924         range.collapse(true);
43925     
43926         nodeRange.collapse(true);
43927      
43928         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
43929         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
43930          
43931         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
43932         
43933         var nodeIsBefore   =  ss == 1;
43934         var nodeIsAfter    = ee == -1;
43935         
43936         if (nodeIsBefore && nodeIsAfter) {
43937             return 0; // outer
43938         }
43939         if (!nodeIsBefore && nodeIsAfter) {
43940             return 1; //right trailed.
43941         }
43942         
43943         if (nodeIsBefore && !nodeIsAfter) {
43944             return 2;  // left trailed.
43945         }
43946         // fully contined.
43947         return 3;
43948     },
43949
43950     // private? - in a new class?
43951     cleanUpPaste :  function()
43952     {
43953         // cleans up the whole document..
43954         Roo.log('cleanuppaste');
43955         
43956         this.cleanUpChildren(this.doc.body);
43957         var clean = this.cleanWordChars(this.doc.body.innerHTML);
43958         if (clean != this.doc.body.innerHTML) {
43959             this.doc.body.innerHTML = clean;
43960         }
43961         
43962     },
43963     
43964     cleanWordChars : function(input) {// change the chars to hex code
43965         var he = Roo.HtmlEditorCore;
43966         
43967         var output = input;
43968         Roo.each(he.swapCodes, function(sw) { 
43969             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
43970             
43971             output = output.replace(swapper, sw[1]);
43972         });
43973         
43974         return output;
43975     },
43976     
43977     
43978     cleanUpChildren : function (n)
43979     {
43980         if (!n.childNodes.length) {
43981             return;
43982         }
43983         for (var i = n.childNodes.length-1; i > -1 ; i--) {
43984            this.cleanUpChild(n.childNodes[i]);
43985         }
43986     },
43987     
43988     
43989         
43990     
43991     cleanUpChild : function (node)
43992     {
43993         var ed = this;
43994         //console.log(node);
43995         if (node.nodeName == "#text") {
43996             // clean up silly Windows -- stuff?
43997             return; 
43998         }
43999         if (node.nodeName == "#comment") {
44000             node.parentNode.removeChild(node);
44001             // clean up silly Windows -- stuff?
44002             return; 
44003         }
44004         var lcname = node.tagName.toLowerCase();
44005         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
44006         // whitelist of tags..
44007         
44008         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
44009             // remove node.
44010             node.parentNode.removeChild(node);
44011             return;
44012             
44013         }
44014         
44015         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
44016         
44017         // spans with no attributes - just remove them..
44018         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
44019             remove_keep_children = true;
44020         }
44021         
44022         // remove <a name=....> as rendering on yahoo mailer is borked with this.
44023         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
44024         
44025         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
44026         //    remove_keep_children = true;
44027         //}
44028         
44029         if (remove_keep_children) {
44030             this.cleanUpChildren(node);
44031             // inserts everything just before this node...
44032             while (node.childNodes.length) {
44033                 var cn = node.childNodes[0];
44034                 node.removeChild(cn);
44035                 node.parentNode.insertBefore(cn, node);
44036             }
44037             node.parentNode.removeChild(node);
44038             return;
44039         }
44040         
44041         if (!node.attributes || !node.attributes.length) {
44042             
44043           
44044             
44045             
44046             this.cleanUpChildren(node);
44047             return;
44048         }
44049         
44050         function cleanAttr(n,v)
44051         {
44052             
44053             if (v.match(/^\./) || v.match(/^\//)) {
44054                 return;
44055             }
44056             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
44057                 return;
44058             }
44059             if (v.match(/^#/)) {
44060                 return;
44061             }
44062 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
44063             node.removeAttribute(n);
44064             
44065         }
44066         
44067         var cwhite = this.cwhite;
44068         var cblack = this.cblack;
44069             
44070         function cleanStyle(n,v)
44071         {
44072             if (v.match(/expression/)) { //XSS?? should we even bother..
44073                 node.removeAttribute(n);
44074                 return;
44075             }
44076             
44077             var parts = v.split(/;/);
44078             var clean = [];
44079             
44080             Roo.each(parts, function(p) {
44081                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
44082                 if (!p.length) {
44083                     return true;
44084                 }
44085                 var l = p.split(':').shift().replace(/\s+/g,'');
44086                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
44087                 
44088                 if ( cwhite.length && cblack.indexOf(l) > -1) {
44089 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
44090                     //node.removeAttribute(n);
44091                     return true;
44092                 }
44093                 //Roo.log()
44094                 // only allow 'c whitelisted system attributes'
44095                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
44096 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
44097                     //node.removeAttribute(n);
44098                     return true;
44099                 }
44100                 
44101                 
44102                  
44103                 
44104                 clean.push(p);
44105                 return true;
44106             });
44107             if (clean.length) { 
44108                 node.setAttribute(n, clean.join(';'));
44109             } else {
44110                 node.removeAttribute(n);
44111             }
44112             
44113         }
44114         
44115         
44116         for (var i = node.attributes.length-1; i > -1 ; i--) {
44117             var a = node.attributes[i];
44118             //console.log(a);
44119             
44120             if (a.name.toLowerCase().substr(0,2)=='on')  {
44121                 node.removeAttribute(a.name);
44122                 continue;
44123             }
44124             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
44125                 node.removeAttribute(a.name);
44126                 continue;
44127             }
44128             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
44129                 cleanAttr(a.name,a.value); // fixme..
44130                 continue;
44131             }
44132             if (a.name == 'style') {
44133                 cleanStyle(a.name,a.value);
44134                 continue;
44135             }
44136             /// clean up MS crap..
44137             // tecnically this should be a list of valid class'es..
44138             
44139             
44140             if (a.name == 'class') {
44141                 if (a.value.match(/^Mso/)) {
44142                     node.removeAttribute('class');
44143                 }
44144                 
44145                 if (a.value.match(/^body$/)) {
44146                     node.removeAttribute('class');
44147                 }
44148                 continue;
44149             }
44150             
44151             // style cleanup!?
44152             // class cleanup?
44153             
44154         }
44155         
44156         
44157         this.cleanUpChildren(node);
44158         
44159         
44160     },
44161     
44162     /**
44163      * Clean up MS wordisms...
44164      */
44165     cleanWord : function(node)
44166     {
44167         if (!node) {
44168             this.cleanWord(this.doc.body);
44169             return;
44170         }
44171         
44172         if(
44173                 node.nodeName == 'SPAN' &&
44174                 !node.hasAttributes() &&
44175                 node.childNodes.length == 1 &&
44176                 node.firstChild.nodeName == "#text"  
44177         ) {
44178             var textNode = node.firstChild;
44179             node.removeChild(textNode);
44180             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44181                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
44182             }
44183             node.parentNode.insertBefore(textNode, node);
44184             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44185                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
44186             }
44187             node.parentNode.removeChild(node);
44188         }
44189         
44190         if (node.nodeName == "#text") {
44191             // clean up silly Windows -- stuff?
44192             return; 
44193         }
44194         if (node.nodeName == "#comment") {
44195             node.parentNode.removeChild(node);
44196             // clean up silly Windows -- stuff?
44197             return; 
44198         }
44199         
44200         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
44201             node.parentNode.removeChild(node);
44202             return;
44203         }
44204         //Roo.log(node.tagName);
44205         // remove - but keep children..
44206         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
44207             //Roo.log('-- removed');
44208             while (node.childNodes.length) {
44209                 var cn = node.childNodes[0];
44210                 node.removeChild(cn);
44211                 node.parentNode.insertBefore(cn, node);
44212                 // move node to parent - and clean it..
44213                 this.cleanWord(cn);
44214             }
44215             node.parentNode.removeChild(node);
44216             /// no need to iterate chidlren = it's got none..
44217             //this.iterateChildren(node, this.cleanWord);
44218             return;
44219         }
44220         // clean styles
44221         if (node.className.length) {
44222             
44223             var cn = node.className.split(/\W+/);
44224             var cna = [];
44225             Roo.each(cn, function(cls) {
44226                 if (cls.match(/Mso[a-zA-Z]+/)) {
44227                     return;
44228                 }
44229                 cna.push(cls);
44230             });
44231             node.className = cna.length ? cna.join(' ') : '';
44232             if (!cna.length) {
44233                 node.removeAttribute("class");
44234             }
44235         }
44236         
44237         if (node.hasAttribute("lang")) {
44238             node.removeAttribute("lang");
44239         }
44240         
44241         if (node.hasAttribute("style")) {
44242             
44243             var styles = node.getAttribute("style").split(";");
44244             var nstyle = [];
44245             Roo.each(styles, function(s) {
44246                 if (!s.match(/:/)) {
44247                     return;
44248                 }
44249                 var kv = s.split(":");
44250                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
44251                     return;
44252                 }
44253                 // what ever is left... we allow.
44254                 nstyle.push(s);
44255             });
44256             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44257             if (!nstyle.length) {
44258                 node.removeAttribute('style');
44259             }
44260         }
44261         this.iterateChildren(node, this.cleanWord);
44262         
44263         
44264         
44265     },
44266     /**
44267      * iterateChildren of a Node, calling fn each time, using this as the scole..
44268      * @param {DomNode} node node to iterate children of.
44269      * @param {Function} fn method of this class to call on each item.
44270      */
44271     iterateChildren : function(node, fn)
44272     {
44273         if (!node.childNodes.length) {
44274                 return;
44275         }
44276         for (var i = node.childNodes.length-1; i > -1 ; i--) {
44277            fn.call(this, node.childNodes[i])
44278         }
44279     },
44280     
44281     
44282     /**
44283      * cleanTableWidths.
44284      *
44285      * Quite often pasting from word etc.. results in tables with column and widths.
44286      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
44287      *
44288      */
44289     cleanTableWidths : function(node)
44290     {
44291          
44292          
44293         if (!node) {
44294             this.cleanTableWidths(this.doc.body);
44295             return;
44296         }
44297         
44298         // ignore list...
44299         if (node.nodeName == "#text" || node.nodeName == "#comment") {
44300             return; 
44301         }
44302         Roo.log(node.tagName);
44303         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
44304             this.iterateChildren(node, this.cleanTableWidths);
44305             return;
44306         }
44307         if (node.hasAttribute('width')) {
44308             node.removeAttribute('width');
44309         }
44310         
44311          
44312         if (node.hasAttribute("style")) {
44313             // pretty basic...
44314             
44315             var styles = node.getAttribute("style").split(";");
44316             var nstyle = [];
44317             Roo.each(styles, function(s) {
44318                 if (!s.match(/:/)) {
44319                     return;
44320                 }
44321                 var kv = s.split(":");
44322                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
44323                     return;
44324                 }
44325                 // what ever is left... we allow.
44326                 nstyle.push(s);
44327             });
44328             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44329             if (!nstyle.length) {
44330                 node.removeAttribute('style');
44331             }
44332         }
44333         
44334         this.iterateChildren(node, this.cleanTableWidths);
44335         
44336         
44337     },
44338     
44339     
44340     
44341     
44342     domToHTML : function(currentElement, depth, nopadtext) {
44343         
44344         depth = depth || 0;
44345         nopadtext = nopadtext || false;
44346     
44347         if (!currentElement) {
44348             return this.domToHTML(this.doc.body);
44349         }
44350         
44351         //Roo.log(currentElement);
44352         var j;
44353         var allText = false;
44354         var nodeName = currentElement.nodeName;
44355         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
44356         
44357         if  (nodeName == '#text') {
44358             
44359             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
44360         }
44361         
44362         
44363         var ret = '';
44364         if (nodeName != 'BODY') {
44365              
44366             var i = 0;
44367             // Prints the node tagName, such as <A>, <IMG>, etc
44368             if (tagName) {
44369                 var attr = [];
44370                 for(i = 0; i < currentElement.attributes.length;i++) {
44371                     // quoting?
44372                     var aname = currentElement.attributes.item(i).name;
44373                     if (!currentElement.attributes.item(i).value.length) {
44374                         continue;
44375                     }
44376                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
44377                 }
44378                 
44379                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
44380             } 
44381             else {
44382                 
44383                 // eack
44384             }
44385         } else {
44386             tagName = false;
44387         }
44388         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
44389             return ret;
44390         }
44391         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
44392             nopadtext = true;
44393         }
44394         
44395         
44396         // Traverse the tree
44397         i = 0;
44398         var currentElementChild = currentElement.childNodes.item(i);
44399         var allText = true;
44400         var innerHTML  = '';
44401         lastnode = '';
44402         while (currentElementChild) {
44403             // Formatting code (indent the tree so it looks nice on the screen)
44404             var nopad = nopadtext;
44405             if (lastnode == 'SPAN') {
44406                 nopad  = true;
44407             }
44408             // text
44409             if  (currentElementChild.nodeName == '#text') {
44410                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
44411                 toadd = nopadtext ? toadd : toadd.trim();
44412                 if (!nopad && toadd.length > 80) {
44413                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
44414                 }
44415                 innerHTML  += toadd;
44416                 
44417                 i++;
44418                 currentElementChild = currentElement.childNodes.item(i);
44419                 lastNode = '';
44420                 continue;
44421             }
44422             allText = false;
44423             
44424             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
44425                 
44426             // Recursively traverse the tree structure of the child node
44427             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
44428             lastnode = currentElementChild.nodeName;
44429             i++;
44430             currentElementChild=currentElement.childNodes.item(i);
44431         }
44432         
44433         ret += innerHTML;
44434         
44435         if (!allText) {
44436                 // The remaining code is mostly for formatting the tree
44437             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
44438         }
44439         
44440         
44441         if (tagName) {
44442             ret+= "</"+tagName+">";
44443         }
44444         return ret;
44445         
44446     },
44447         
44448     applyBlacklists : function()
44449     {
44450         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
44451         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
44452         
44453         this.white = [];
44454         this.black = [];
44455         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
44456             if (b.indexOf(tag) > -1) {
44457                 return;
44458             }
44459             this.white.push(tag);
44460             
44461         }, this);
44462         
44463         Roo.each(w, function(tag) {
44464             if (b.indexOf(tag) > -1) {
44465                 return;
44466             }
44467             if (this.white.indexOf(tag) > -1) {
44468                 return;
44469             }
44470             this.white.push(tag);
44471             
44472         }, this);
44473         
44474         
44475         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
44476             if (w.indexOf(tag) > -1) {
44477                 return;
44478             }
44479             this.black.push(tag);
44480             
44481         }, this);
44482         
44483         Roo.each(b, function(tag) {
44484             if (w.indexOf(tag) > -1) {
44485                 return;
44486             }
44487             if (this.black.indexOf(tag) > -1) {
44488                 return;
44489             }
44490             this.black.push(tag);
44491             
44492         }, this);
44493         
44494         
44495         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
44496         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
44497         
44498         this.cwhite = [];
44499         this.cblack = [];
44500         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
44501             if (b.indexOf(tag) > -1) {
44502                 return;
44503             }
44504             this.cwhite.push(tag);
44505             
44506         }, this);
44507         
44508         Roo.each(w, function(tag) {
44509             if (b.indexOf(tag) > -1) {
44510                 return;
44511             }
44512             if (this.cwhite.indexOf(tag) > -1) {
44513                 return;
44514             }
44515             this.cwhite.push(tag);
44516             
44517         }, this);
44518         
44519         
44520         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
44521             if (w.indexOf(tag) > -1) {
44522                 return;
44523             }
44524             this.cblack.push(tag);
44525             
44526         }, this);
44527         
44528         Roo.each(b, function(tag) {
44529             if (w.indexOf(tag) > -1) {
44530                 return;
44531             }
44532             if (this.cblack.indexOf(tag) > -1) {
44533                 return;
44534             }
44535             this.cblack.push(tag);
44536             
44537         }, this);
44538     },
44539     
44540     setStylesheets : function(stylesheets)
44541     {
44542         if(typeof(stylesheets) == 'string'){
44543             Roo.get(this.iframe.contentDocument.head).createChild({
44544                 tag : 'link',
44545                 rel : 'stylesheet',
44546                 type : 'text/css',
44547                 href : stylesheets
44548             });
44549             
44550             return;
44551         }
44552         var _this = this;
44553      
44554         Roo.each(stylesheets, function(s) {
44555             if(!s.length){
44556                 return;
44557             }
44558             
44559             Roo.get(_this.iframe.contentDocument.head).createChild({
44560                 tag : 'link',
44561                 rel : 'stylesheet',
44562                 type : 'text/css',
44563                 href : s
44564             });
44565         });
44566
44567         
44568     },
44569     
44570     removeStylesheets : function()
44571     {
44572         var _this = this;
44573         
44574         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
44575             s.remove();
44576         });
44577     },
44578     
44579     setStyle : function(style)
44580     {
44581         Roo.get(this.iframe.contentDocument.head).createChild({
44582             tag : 'style',
44583             type : 'text/css',
44584             html : style
44585         });
44586
44587         return;
44588     }
44589     
44590     // hide stuff that is not compatible
44591     /**
44592      * @event blur
44593      * @hide
44594      */
44595     /**
44596      * @event change
44597      * @hide
44598      */
44599     /**
44600      * @event focus
44601      * @hide
44602      */
44603     /**
44604      * @event specialkey
44605      * @hide
44606      */
44607     /**
44608      * @cfg {String} fieldClass @hide
44609      */
44610     /**
44611      * @cfg {String} focusClass @hide
44612      */
44613     /**
44614      * @cfg {String} autoCreate @hide
44615      */
44616     /**
44617      * @cfg {String} inputType @hide
44618      */
44619     /**
44620      * @cfg {String} invalidClass @hide
44621      */
44622     /**
44623      * @cfg {String} invalidText @hide
44624      */
44625     /**
44626      * @cfg {String} msgFx @hide
44627      */
44628     /**
44629      * @cfg {String} validateOnBlur @hide
44630      */
44631 });
44632
44633 Roo.HtmlEditorCore.white = [
44634         'area', 'br', 'img', 'input', 'hr', 'wbr',
44635         
44636        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
44637        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
44638        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
44639        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
44640        'table',   'ul',         'xmp', 
44641        
44642        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
44643       'thead',   'tr', 
44644      
44645       'dir', 'menu', 'ol', 'ul', 'dl',
44646        
44647       'embed',  'object'
44648 ];
44649
44650
44651 Roo.HtmlEditorCore.black = [
44652     //    'embed',  'object', // enable - backend responsiblity to clean thiese
44653         'applet', // 
44654         'base',   'basefont', 'bgsound', 'blink',  'body', 
44655         'frame',  'frameset', 'head',    'html',   'ilayer', 
44656         'iframe', 'layer',  'link',     'meta',    'object',   
44657         'script', 'style' ,'title',  'xml' // clean later..
44658 ];
44659 Roo.HtmlEditorCore.clean = [
44660     'script', 'style', 'title', 'xml'
44661 ];
44662 Roo.HtmlEditorCore.remove = [
44663     'font'
44664 ];
44665 // attributes..
44666
44667 Roo.HtmlEditorCore.ablack = [
44668     'on'
44669 ];
44670     
44671 Roo.HtmlEditorCore.aclean = [ 
44672     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
44673 ];
44674
44675 // protocols..
44676 Roo.HtmlEditorCore.pwhite= [
44677         'http',  'https',  'mailto'
44678 ];
44679
44680 // white listed style attributes.
44681 Roo.HtmlEditorCore.cwhite= [
44682       //  'text-align', /// default is to allow most things..
44683       
44684          
44685 //        'font-size'//??
44686 ];
44687
44688 // black listed style attributes.
44689 Roo.HtmlEditorCore.cblack= [
44690       //  'font-size' -- this can be set by the project 
44691 ];
44692
44693
44694 Roo.HtmlEditorCore.swapCodes   =[ 
44695     [    8211, "--" ], 
44696     [    8212, "--" ], 
44697     [    8216,  "'" ],  
44698     [    8217, "'" ],  
44699     [    8220, '"' ],  
44700     [    8221, '"' ],  
44701     [    8226, "*" ],  
44702     [    8230, "..." ]
44703 ]; 
44704
44705     //<script type="text/javascript">
44706
44707 /*
44708  * Ext JS Library 1.1.1
44709  * Copyright(c) 2006-2007, Ext JS, LLC.
44710  * Licence LGPL
44711  * 
44712  */
44713  
44714  
44715 Roo.form.HtmlEditor = function(config){
44716     
44717     
44718     
44719     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
44720     
44721     if (!this.toolbars) {
44722         this.toolbars = [];
44723     }
44724     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
44725     
44726     
44727 };
44728
44729 /**
44730  * @class Roo.form.HtmlEditor
44731  * @extends Roo.form.Field
44732  * Provides a lightweight HTML Editor component.
44733  *
44734  * This has been tested on Fireforx / Chrome.. IE may not be so great..
44735  * 
44736  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
44737  * supported by this editor.</b><br/><br/>
44738  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
44739  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
44740  */
44741 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
44742     /**
44743      * @cfg {Boolean} clearUp
44744      */
44745     clearUp : true,
44746       /**
44747      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
44748      */
44749     toolbars : false,
44750    
44751      /**
44752      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
44753      *                        Roo.resizable.
44754      */
44755     resizable : false,
44756      /**
44757      * @cfg {Number} height (in pixels)
44758      */   
44759     height: 300,
44760    /**
44761      * @cfg {Number} width (in pixels)
44762      */   
44763     width: 500,
44764     
44765     /**
44766      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
44767      * 
44768      */
44769     stylesheets: false,
44770     
44771     
44772      /**
44773      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
44774      * 
44775      */
44776     cblack: false,
44777     /**
44778      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
44779      * 
44780      */
44781     cwhite: false,
44782     
44783      /**
44784      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
44785      * 
44786      */
44787     black: false,
44788     /**
44789      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
44790      * 
44791      */
44792     white: false,
44793     
44794     // id of frame..
44795     frameId: false,
44796     
44797     // private properties
44798     validationEvent : false,
44799     deferHeight: true,
44800     initialized : false,
44801     activated : false,
44802     
44803     onFocus : Roo.emptyFn,
44804     iframePad:3,
44805     hideMode:'offsets',
44806     
44807     actionMode : 'container', // defaults to hiding it...
44808     
44809     defaultAutoCreate : { // modified by initCompnoent..
44810         tag: "textarea",
44811         style:"width:500px;height:300px;",
44812         autocomplete: "new-password"
44813     },
44814
44815     // private
44816     initComponent : function(){
44817         this.addEvents({
44818             /**
44819              * @event initialize
44820              * Fires when the editor is fully initialized (including the iframe)
44821              * @param {HtmlEditor} this
44822              */
44823             initialize: true,
44824             /**
44825              * @event activate
44826              * Fires when the editor is first receives the focus. Any insertion must wait
44827              * until after this event.
44828              * @param {HtmlEditor} this
44829              */
44830             activate: true,
44831              /**
44832              * @event beforesync
44833              * Fires before the textarea is updated with content from the editor iframe. Return false
44834              * to cancel the sync.
44835              * @param {HtmlEditor} this
44836              * @param {String} html
44837              */
44838             beforesync: true,
44839              /**
44840              * @event beforepush
44841              * Fires before the iframe editor is updated with content from the textarea. Return false
44842              * to cancel the push.
44843              * @param {HtmlEditor} this
44844              * @param {String} html
44845              */
44846             beforepush: true,
44847              /**
44848              * @event sync
44849              * Fires when the textarea is updated with content from the editor iframe.
44850              * @param {HtmlEditor} this
44851              * @param {String} html
44852              */
44853             sync: true,
44854              /**
44855              * @event push
44856              * Fires when the iframe editor is updated with content from the textarea.
44857              * @param {HtmlEditor} this
44858              * @param {String} html
44859              */
44860             push: true,
44861              /**
44862              * @event editmodechange
44863              * Fires when the editor switches edit modes
44864              * @param {HtmlEditor} this
44865              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
44866              */
44867             editmodechange: true,
44868             /**
44869              * @event editorevent
44870              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
44871              * @param {HtmlEditor} this
44872              */
44873             editorevent: true,
44874             /**
44875              * @event firstfocus
44876              * Fires when on first focus - needed by toolbars..
44877              * @param {HtmlEditor} this
44878              */
44879             firstfocus: true,
44880             /**
44881              * @event autosave
44882              * Auto save the htmlEditor value as a file into Events
44883              * @param {HtmlEditor} this
44884              */
44885             autosave: true,
44886             /**
44887              * @event savedpreview
44888              * preview the saved version of htmlEditor
44889              * @param {HtmlEditor} this
44890              */
44891             savedpreview: true,
44892             
44893             /**
44894             * @event stylesheetsclick
44895             * Fires when press the Sytlesheets button
44896             * @param {Roo.HtmlEditorCore} this
44897             */
44898             stylesheetsclick: true
44899         });
44900         this.defaultAutoCreate =  {
44901             tag: "textarea",
44902             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
44903             autocomplete: "new-password"
44904         };
44905     },
44906
44907     /**
44908      * Protected method that will not generally be called directly. It
44909      * is called when the editor creates its toolbar. Override this method if you need to
44910      * add custom toolbar buttons.
44911      * @param {HtmlEditor} editor
44912      */
44913     createToolbar : function(editor){
44914         Roo.log("create toolbars");
44915         if (!editor.toolbars || !editor.toolbars.length) {
44916             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
44917         }
44918         
44919         for (var i =0 ; i < editor.toolbars.length;i++) {
44920             editor.toolbars[i] = Roo.factory(
44921                     typeof(editor.toolbars[i]) == 'string' ?
44922                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
44923                 Roo.form.HtmlEditor);
44924             editor.toolbars[i].init(editor);
44925         }
44926          
44927         
44928     },
44929
44930      
44931     // private
44932     onRender : function(ct, position)
44933     {
44934         var _t = this;
44935         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
44936         
44937         this.wrap = this.el.wrap({
44938             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
44939         });
44940         
44941         this.editorcore.onRender(ct, position);
44942          
44943         if (this.resizable) {
44944             this.resizeEl = new Roo.Resizable(this.wrap, {
44945                 pinned : true,
44946                 wrap: true,
44947                 dynamic : true,
44948                 minHeight : this.height,
44949                 height: this.height,
44950                 handles : this.resizable,
44951                 width: this.width,
44952                 listeners : {
44953                     resize : function(r, w, h) {
44954                         _t.onResize(w,h); // -something
44955                     }
44956                 }
44957             });
44958             
44959         }
44960         this.createToolbar(this);
44961        
44962         
44963         if(!this.width){
44964             this.setSize(this.wrap.getSize());
44965         }
44966         if (this.resizeEl) {
44967             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
44968             // should trigger onReize..
44969         }
44970         
44971         this.keyNav = new Roo.KeyNav(this.el, {
44972             
44973             "tab" : function(e){
44974                 e.preventDefault();
44975                 
44976                 var value = this.getValue();
44977                 
44978                 var start = this.el.dom.selectionStart;
44979                 var end = this.el.dom.selectionEnd;
44980                 
44981                 if(!e.shiftKey){
44982                     
44983                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
44984                     this.el.dom.setSelectionRange(end + 1, end + 1);
44985                     return;
44986                 }
44987                 
44988                 var f = value.substring(0, start).split("\t");
44989                 
44990                 if(f.pop().length != 0){
44991                     return;
44992                 }
44993                 
44994                 this.setValue(f.join("\t") + value.substring(end));
44995                 this.el.dom.setSelectionRange(start - 1, start - 1);
44996                 
44997             },
44998             
44999             "home" : function(e){
45000                 e.preventDefault();
45001                 
45002                 var curr = this.el.dom.selectionStart;
45003                 var lines = this.getValue().split("\n");
45004                 
45005                 if(!lines.length){
45006                     return;
45007                 }
45008                 
45009                 if(e.ctrlKey){
45010                     this.el.dom.setSelectionRange(0, 0);
45011                     return;
45012                 }
45013                 
45014                 var pos = 0;
45015                 
45016                 for (var i = 0; i < lines.length;i++) {
45017                     pos += lines[i].length;
45018                     
45019                     if(i != 0){
45020                         pos += 1;
45021                     }
45022                     
45023                     if(pos < curr){
45024                         continue;
45025                     }
45026                     
45027                     pos -= lines[i].length;
45028                     
45029                     break;
45030                 }
45031                 
45032                 if(!e.shiftKey){
45033                     this.el.dom.setSelectionRange(pos, pos);
45034                     return;
45035                 }
45036                 
45037                 this.el.dom.selectionStart = pos;
45038                 this.el.dom.selectionEnd = curr;
45039             },
45040             
45041             "end" : function(e){
45042                 e.preventDefault();
45043                 
45044                 var curr = this.el.dom.selectionStart;
45045                 var lines = this.getValue().split("\n");
45046                 
45047                 if(!lines.length){
45048                     return;
45049                 }
45050                 
45051                 if(e.ctrlKey){
45052                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
45053                     return;
45054                 }
45055                 
45056                 var pos = 0;
45057                 
45058                 for (var i = 0; i < lines.length;i++) {
45059                     
45060                     pos += lines[i].length;
45061                     
45062                     if(i != 0){
45063                         pos += 1;
45064                     }
45065                     
45066                     if(pos < curr){
45067                         continue;
45068                     }
45069                     
45070                     break;
45071                 }
45072                 
45073                 if(!e.shiftKey){
45074                     this.el.dom.setSelectionRange(pos, pos);
45075                     return;
45076                 }
45077                 
45078                 this.el.dom.selectionStart = curr;
45079                 this.el.dom.selectionEnd = pos;
45080             },
45081
45082             scope : this,
45083
45084             doRelay : function(foo, bar, hname){
45085                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
45086             },
45087
45088             forceKeyDown: true
45089         });
45090         
45091 //        if(this.autosave && this.w){
45092 //            this.autoSaveFn = setInterval(this.autosave, 1000);
45093 //        }
45094     },
45095
45096     // private
45097     onResize : function(w, h)
45098     {
45099         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
45100         var ew = false;
45101         var eh = false;
45102         
45103         if(this.el ){
45104             if(typeof w == 'number'){
45105                 var aw = w - this.wrap.getFrameWidth('lr');
45106                 this.el.setWidth(this.adjustWidth('textarea', aw));
45107                 ew = aw;
45108             }
45109             if(typeof h == 'number'){
45110                 var tbh = 0;
45111                 for (var i =0; i < this.toolbars.length;i++) {
45112                     // fixme - ask toolbars for heights?
45113                     tbh += this.toolbars[i].tb.el.getHeight();
45114                     if (this.toolbars[i].footer) {
45115                         tbh += this.toolbars[i].footer.el.getHeight();
45116                     }
45117                 }
45118                 
45119                 
45120                 
45121                 
45122                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
45123                 ah -= 5; // knock a few pixes off for look..
45124 //                Roo.log(ah);
45125                 this.el.setHeight(this.adjustWidth('textarea', ah));
45126                 var eh = ah;
45127             }
45128         }
45129         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
45130         this.editorcore.onResize(ew,eh);
45131         
45132     },
45133
45134     /**
45135      * Toggles the editor between standard and source edit mode.
45136      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
45137      */
45138     toggleSourceEdit : function(sourceEditMode)
45139     {
45140         this.editorcore.toggleSourceEdit(sourceEditMode);
45141         
45142         if(this.editorcore.sourceEditMode){
45143             Roo.log('editor - showing textarea');
45144             
45145 //            Roo.log('in');
45146 //            Roo.log(this.syncValue());
45147             this.editorcore.syncValue();
45148             this.el.removeClass('x-hidden');
45149             this.el.dom.removeAttribute('tabIndex');
45150             this.el.focus();
45151             
45152             for (var i = 0; i < this.toolbars.length; i++) {
45153                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
45154                     this.toolbars[i].tb.hide();
45155                     this.toolbars[i].footer.hide();
45156                 }
45157             }
45158             
45159         }else{
45160             Roo.log('editor - hiding textarea');
45161 //            Roo.log('out')
45162 //            Roo.log(this.pushValue()); 
45163             this.editorcore.pushValue();
45164             
45165             this.el.addClass('x-hidden');
45166             this.el.dom.setAttribute('tabIndex', -1);
45167             
45168             for (var i = 0; i < this.toolbars.length; i++) {
45169                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
45170                     this.toolbars[i].tb.show();
45171                     this.toolbars[i].footer.show();
45172                 }
45173             }
45174             
45175             //this.deferFocus();
45176         }
45177         
45178         this.setSize(this.wrap.getSize());
45179         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
45180         
45181         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
45182     },
45183  
45184     // private (for BoxComponent)
45185     adjustSize : Roo.BoxComponent.prototype.adjustSize,
45186
45187     // private (for BoxComponent)
45188     getResizeEl : function(){
45189         return this.wrap;
45190     },
45191
45192     // private (for BoxComponent)
45193     getPositionEl : function(){
45194         return this.wrap;
45195     },
45196
45197     // private
45198     initEvents : function(){
45199         this.originalValue = this.getValue();
45200     },
45201
45202     /**
45203      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
45204      * @method
45205      */
45206     markInvalid : Roo.emptyFn,
45207     /**
45208      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
45209      * @method
45210      */
45211     clearInvalid : Roo.emptyFn,
45212
45213     setValue : function(v){
45214         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
45215         this.editorcore.pushValue();
45216     },
45217
45218      
45219     // private
45220     deferFocus : function(){
45221         this.focus.defer(10, this);
45222     },
45223
45224     // doc'ed in Field
45225     focus : function(){
45226         this.editorcore.focus();
45227         
45228     },
45229       
45230
45231     // private
45232     onDestroy : function(){
45233         
45234         
45235         
45236         if(this.rendered){
45237             
45238             for (var i =0; i < this.toolbars.length;i++) {
45239                 // fixme - ask toolbars for heights?
45240                 this.toolbars[i].onDestroy();
45241             }
45242             
45243             this.wrap.dom.innerHTML = '';
45244             this.wrap.remove();
45245         }
45246     },
45247
45248     // private
45249     onFirstFocus : function(){
45250         //Roo.log("onFirstFocus");
45251         this.editorcore.onFirstFocus();
45252          for (var i =0; i < this.toolbars.length;i++) {
45253             this.toolbars[i].onFirstFocus();
45254         }
45255         
45256     },
45257     
45258     // private
45259     syncValue : function()
45260     {
45261         this.editorcore.syncValue();
45262     },
45263     
45264     pushValue : function()
45265     {
45266         this.editorcore.pushValue();
45267     },
45268     
45269     setStylesheets : function(stylesheets)
45270     {
45271         this.editorcore.setStylesheets(stylesheets);
45272     },
45273     
45274     removeStylesheets : function()
45275     {
45276         this.editorcore.removeStylesheets();
45277     }
45278      
45279     
45280     // hide stuff that is not compatible
45281     /**
45282      * @event blur
45283      * @hide
45284      */
45285     /**
45286      * @event change
45287      * @hide
45288      */
45289     /**
45290      * @event focus
45291      * @hide
45292      */
45293     /**
45294      * @event specialkey
45295      * @hide
45296      */
45297     /**
45298      * @cfg {String} fieldClass @hide
45299      */
45300     /**
45301      * @cfg {String} focusClass @hide
45302      */
45303     /**
45304      * @cfg {String} autoCreate @hide
45305      */
45306     /**
45307      * @cfg {String} inputType @hide
45308      */
45309     /**
45310      * @cfg {String} invalidClass @hide
45311      */
45312     /**
45313      * @cfg {String} invalidText @hide
45314      */
45315     /**
45316      * @cfg {String} msgFx @hide
45317      */
45318     /**
45319      * @cfg {String} validateOnBlur @hide
45320      */
45321 });
45322  
45323     // <script type="text/javascript">
45324 /*
45325  * Based on
45326  * Ext JS Library 1.1.1
45327  * Copyright(c) 2006-2007, Ext JS, LLC.
45328  *  
45329  
45330  */
45331
45332 /**
45333  * @class Roo.form.HtmlEditorToolbar1
45334  * Basic Toolbar
45335  * 
45336  * Usage:
45337  *
45338  new Roo.form.HtmlEditor({
45339     ....
45340     toolbars : [
45341         new Roo.form.HtmlEditorToolbar1({
45342             disable : { fonts: 1 , format: 1, ..., ... , ...],
45343             btns : [ .... ]
45344         })
45345     }
45346      
45347  * 
45348  * @cfg {Object} disable List of elements to disable..
45349  * @cfg {Array} btns List of additional buttons.
45350  * 
45351  * 
45352  * NEEDS Extra CSS? 
45353  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
45354  */
45355  
45356 Roo.form.HtmlEditor.ToolbarStandard = function(config)
45357 {
45358     
45359     Roo.apply(this, config);
45360     
45361     // default disabled, based on 'good practice'..
45362     this.disable = this.disable || {};
45363     Roo.applyIf(this.disable, {
45364         fontSize : true,
45365         colors : true,
45366         specialElements : true
45367     });
45368     
45369     
45370     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
45371     // dont call parent... till later.
45372 }
45373
45374 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
45375     
45376     tb: false,
45377     
45378     rendered: false,
45379     
45380     editor : false,
45381     editorcore : false,
45382     /**
45383      * @cfg {Object} disable  List of toolbar elements to disable
45384          
45385      */
45386     disable : false,
45387     
45388     
45389      /**
45390      * @cfg {String} createLinkText The default text for the create link prompt
45391      */
45392     createLinkText : 'Please enter the URL for the link:',
45393     /**
45394      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
45395      */
45396     defaultLinkValue : 'http:/'+'/',
45397    
45398     
45399       /**
45400      * @cfg {Array} fontFamilies An array of available font families
45401      */
45402     fontFamilies : [
45403         'Arial',
45404         'Courier New',
45405         'Tahoma',
45406         'Times New Roman',
45407         'Verdana'
45408     ],
45409     
45410     specialChars : [
45411            "&#169;",
45412           "&#174;",     
45413           "&#8482;",    
45414           "&#163;" ,    
45415          // "&#8212;",    
45416           "&#8230;",    
45417           "&#247;" ,    
45418         //  "&#225;" ,     ?? a acute?
45419            "&#8364;"    , //Euro
45420        //   "&#8220;"    ,
45421         //  "&#8221;"    ,
45422         //  "&#8226;"    ,
45423           "&#176;"  //   , // degrees
45424
45425          // "&#233;"     , // e ecute
45426          // "&#250;"     , // u ecute?
45427     ],
45428     
45429     specialElements : [
45430         {
45431             text: "Insert Table",
45432             xtype: 'MenuItem',
45433             xns : Roo.Menu,
45434             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
45435                 
45436         },
45437         {    
45438             text: "Insert Image",
45439             xtype: 'MenuItem',
45440             xns : Roo.Menu,
45441             ihtml : '<img src="about:blank"/>'
45442             
45443         }
45444         
45445          
45446     ],
45447     
45448     
45449     inputElements : [ 
45450             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
45451             "input:submit", "input:button", "select", "textarea", "label" ],
45452     formats : [
45453         ["p"] ,  
45454         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
45455         ["pre"],[ "code"], 
45456         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
45457         ['div'],['span'],
45458         ['sup'],['sub']
45459     ],
45460     
45461     cleanStyles : [
45462         "font-size"
45463     ],
45464      /**
45465      * @cfg {String} defaultFont default font to use.
45466      */
45467     defaultFont: 'tahoma',
45468    
45469     fontSelect : false,
45470     
45471     
45472     formatCombo : false,
45473     
45474     init : function(editor)
45475     {
45476         this.editor = editor;
45477         this.editorcore = editor.editorcore ? editor.editorcore : editor;
45478         var editorcore = this.editorcore;
45479         
45480         var _t = this;
45481         
45482         var fid = editorcore.frameId;
45483         var etb = this;
45484         function btn(id, toggle, handler){
45485             var xid = fid + '-'+ id ;
45486             return {
45487                 id : xid,
45488                 cmd : id,
45489                 cls : 'x-btn-icon x-edit-'+id,
45490                 enableToggle:toggle !== false,
45491                 scope: _t, // was editor...
45492                 handler:handler||_t.relayBtnCmd,
45493                 clickEvent:'mousedown',
45494                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
45495                 tabIndex:-1
45496             };
45497         }
45498         
45499         
45500         
45501         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
45502         this.tb = tb;
45503          // stop form submits
45504         tb.el.on('click', function(e){
45505             e.preventDefault(); // what does this do?
45506         });
45507
45508         if(!this.disable.font) { // && !Roo.isSafari){
45509             /* why no safari for fonts 
45510             editor.fontSelect = tb.el.createChild({
45511                 tag:'select',
45512                 tabIndex: -1,
45513                 cls:'x-font-select',
45514                 html: this.createFontOptions()
45515             });
45516             
45517             editor.fontSelect.on('change', function(){
45518                 var font = editor.fontSelect.dom.value;
45519                 editor.relayCmd('fontname', font);
45520                 editor.deferFocus();
45521             }, editor);
45522             
45523             tb.add(
45524                 editor.fontSelect.dom,
45525                 '-'
45526             );
45527             */
45528             
45529         };
45530         if(!this.disable.formats){
45531             this.formatCombo = new Roo.form.ComboBox({
45532                 store: new Roo.data.SimpleStore({
45533                     id : 'tag',
45534                     fields: ['tag'],
45535                     data : this.formats // from states.js
45536                 }),
45537                 blockFocus : true,
45538                 name : '',
45539                 //autoCreate : {tag: "div",  size: "20"},
45540                 displayField:'tag',
45541                 typeAhead: false,
45542                 mode: 'local',
45543                 editable : false,
45544                 triggerAction: 'all',
45545                 emptyText:'Add tag',
45546                 selectOnFocus:true,
45547                 width:135,
45548                 listeners : {
45549                     'select': function(c, r, i) {
45550                         editorcore.insertTag(r.get('tag'));
45551                         editor.focus();
45552                     }
45553                 }
45554
45555             });
45556             tb.addField(this.formatCombo);
45557             
45558         }
45559         
45560         if(!this.disable.format){
45561             tb.add(
45562                 btn('bold'),
45563                 btn('italic'),
45564                 btn('underline'),
45565                 btn('strikethrough')
45566             );
45567         };
45568         if(!this.disable.fontSize){
45569             tb.add(
45570                 '-',
45571                 
45572                 
45573                 btn('increasefontsize', false, editorcore.adjustFont),
45574                 btn('decreasefontsize', false, editorcore.adjustFont)
45575             );
45576         };
45577         
45578         
45579         if(!this.disable.colors){
45580             tb.add(
45581                 '-', {
45582                     id:editorcore.frameId +'-forecolor',
45583                     cls:'x-btn-icon x-edit-forecolor',
45584                     clickEvent:'mousedown',
45585                     tooltip: this.buttonTips['forecolor'] || undefined,
45586                     tabIndex:-1,
45587                     menu : new Roo.menu.ColorMenu({
45588                         allowReselect: true,
45589                         focus: Roo.emptyFn,
45590                         value:'000000',
45591                         plain:true,
45592                         selectHandler: function(cp, color){
45593                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
45594                             editor.deferFocus();
45595                         },
45596                         scope: editorcore,
45597                         clickEvent:'mousedown'
45598                     })
45599                 }, {
45600                     id:editorcore.frameId +'backcolor',
45601                     cls:'x-btn-icon x-edit-backcolor',
45602                     clickEvent:'mousedown',
45603                     tooltip: this.buttonTips['backcolor'] || undefined,
45604                     tabIndex:-1,
45605                     menu : new Roo.menu.ColorMenu({
45606                         focus: Roo.emptyFn,
45607                         value:'FFFFFF',
45608                         plain:true,
45609                         allowReselect: true,
45610                         selectHandler: function(cp, color){
45611                             if(Roo.isGecko){
45612                                 editorcore.execCmd('useCSS', false);
45613                                 editorcore.execCmd('hilitecolor', color);
45614                                 editorcore.execCmd('useCSS', true);
45615                                 editor.deferFocus();
45616                             }else{
45617                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
45618                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
45619                                 editor.deferFocus();
45620                             }
45621                         },
45622                         scope:editorcore,
45623                         clickEvent:'mousedown'
45624                     })
45625                 }
45626             );
45627         };
45628         // now add all the items...
45629         
45630
45631         if(!this.disable.alignments){
45632             tb.add(
45633                 '-',
45634                 btn('justifyleft'),
45635                 btn('justifycenter'),
45636                 btn('justifyright')
45637             );
45638         };
45639
45640         //if(!Roo.isSafari){
45641             if(!this.disable.links){
45642                 tb.add(
45643                     '-',
45644                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
45645                 );
45646             };
45647
45648             if(!this.disable.lists){
45649                 tb.add(
45650                     '-',
45651                     btn('insertorderedlist'),
45652                     btn('insertunorderedlist')
45653                 );
45654             }
45655             if(!this.disable.sourceEdit){
45656                 tb.add(
45657                     '-',
45658                     btn('sourceedit', true, function(btn){
45659                         this.toggleSourceEdit(btn.pressed);
45660                     })
45661                 );
45662             }
45663         //}
45664         
45665         var smenu = { };
45666         // special menu.. - needs to be tidied up..
45667         if (!this.disable.special) {
45668             smenu = {
45669                 text: "&#169;",
45670                 cls: 'x-edit-none',
45671                 
45672                 menu : {
45673                     items : []
45674                 }
45675             };
45676             for (var i =0; i < this.specialChars.length; i++) {
45677                 smenu.menu.items.push({
45678                     
45679                     html: this.specialChars[i],
45680                     handler: function(a,b) {
45681                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
45682                         //editor.insertAtCursor(a.html);
45683                         
45684                     },
45685                     tabIndex:-1
45686                 });
45687             }
45688             
45689             
45690             tb.add(smenu);
45691             
45692             
45693         }
45694         
45695         var cmenu = { };
45696         if (!this.disable.cleanStyles) {
45697             cmenu = {
45698                 cls: 'x-btn-icon x-btn-clear',
45699                 
45700                 menu : {
45701                     items : []
45702                 }
45703             };
45704             for (var i =0; i < this.cleanStyles.length; i++) {
45705                 cmenu.menu.items.push({
45706                     actiontype : this.cleanStyles[i],
45707                     html: 'Remove ' + this.cleanStyles[i],
45708                     handler: function(a,b) {
45709 //                        Roo.log(a);
45710 //                        Roo.log(b);
45711                         var c = Roo.get(editorcore.doc.body);
45712                         c.select('[style]').each(function(s) {
45713                             s.dom.style.removeProperty(a.actiontype);
45714                         });
45715                         editorcore.syncValue();
45716                     },
45717                     tabIndex:-1
45718                 });
45719             }
45720              cmenu.menu.items.push({
45721                 actiontype : 'tablewidths',
45722                 html: 'Remove Table Widths',
45723                 handler: function(a,b) {
45724                     editorcore.cleanTableWidths();
45725                     editorcore.syncValue();
45726                 },
45727                 tabIndex:-1
45728             });
45729             cmenu.menu.items.push({
45730                 actiontype : 'word',
45731                 html: 'Remove MS Word Formating',
45732                 handler: function(a,b) {
45733                     editorcore.cleanWord();
45734                     editorcore.syncValue();
45735                 },
45736                 tabIndex:-1
45737             });
45738             
45739             cmenu.menu.items.push({
45740                 actiontype : 'all',
45741                 html: 'Remove All Styles',
45742                 handler: function(a,b) {
45743                     
45744                     var c = Roo.get(editorcore.doc.body);
45745                     c.select('[style]').each(function(s) {
45746                         s.dom.removeAttribute('style');
45747                     });
45748                     editorcore.syncValue();
45749                 },
45750                 tabIndex:-1
45751             });
45752             
45753             cmenu.menu.items.push({
45754                 actiontype : 'all',
45755                 html: 'Remove All CSS Classes',
45756                 handler: function(a,b) {
45757                     
45758                     var c = Roo.get(editorcore.doc.body);
45759                     c.select('[class]').each(function(s) {
45760                         s.dom.removeAttribute('class');
45761                     });
45762                     editorcore.cleanWord();
45763                     editorcore.syncValue();
45764                 },
45765                 tabIndex:-1
45766             });
45767             
45768              cmenu.menu.items.push({
45769                 actiontype : 'tidy',
45770                 html: 'Tidy HTML Source',
45771                 handler: function(a,b) {
45772                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
45773                     editorcore.syncValue();
45774                 },
45775                 tabIndex:-1
45776             });
45777             
45778             
45779             tb.add(cmenu);
45780         }
45781          
45782         if (!this.disable.specialElements) {
45783             var semenu = {
45784                 text: "Other;",
45785                 cls: 'x-edit-none',
45786                 menu : {
45787                     items : []
45788                 }
45789             };
45790             for (var i =0; i < this.specialElements.length; i++) {
45791                 semenu.menu.items.push(
45792                     Roo.apply({ 
45793                         handler: function(a,b) {
45794                             editor.insertAtCursor(this.ihtml);
45795                         }
45796                     }, this.specialElements[i])
45797                 );
45798                     
45799             }
45800             
45801             tb.add(semenu);
45802             
45803             
45804         }
45805          
45806         
45807         if (this.btns) {
45808             for(var i =0; i< this.btns.length;i++) {
45809                 var b = Roo.factory(this.btns[i],Roo.form);
45810                 b.cls =  'x-edit-none';
45811                 
45812                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
45813                     b.cls += ' x-init-enable';
45814                 }
45815                 
45816                 b.scope = editorcore;
45817                 tb.add(b);
45818             }
45819         
45820         }
45821         
45822         
45823         
45824         // disable everything...
45825         
45826         this.tb.items.each(function(item){
45827             
45828            if(
45829                 item.id != editorcore.frameId+ '-sourceedit' && 
45830                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
45831             ){
45832                 
45833                 item.disable();
45834             }
45835         });
45836         this.rendered = true;
45837         
45838         // the all the btns;
45839         editor.on('editorevent', this.updateToolbar, this);
45840         // other toolbars need to implement this..
45841         //editor.on('editmodechange', this.updateToolbar, this);
45842     },
45843     
45844     
45845     relayBtnCmd : function(btn) {
45846         this.editorcore.relayCmd(btn.cmd);
45847     },
45848     // private used internally
45849     createLink : function(){
45850         Roo.log("create link?");
45851         var url = prompt(this.createLinkText, this.defaultLinkValue);
45852         if(url && url != 'http:/'+'/'){
45853             this.editorcore.relayCmd('createlink', url);
45854         }
45855     },
45856
45857     
45858     /**
45859      * Protected method that will not generally be called directly. It triggers
45860      * a toolbar update by reading the markup state of the current selection in the editor.
45861      */
45862     updateToolbar: function(){
45863
45864         if(!this.editorcore.activated){
45865             this.editor.onFirstFocus();
45866             return;
45867         }
45868
45869         var btns = this.tb.items.map, 
45870             doc = this.editorcore.doc,
45871             frameId = this.editorcore.frameId;
45872
45873         if(!this.disable.font && !Roo.isSafari){
45874             /*
45875             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
45876             if(name != this.fontSelect.dom.value){
45877                 this.fontSelect.dom.value = name;
45878             }
45879             */
45880         }
45881         if(!this.disable.format){
45882             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
45883             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
45884             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
45885             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
45886         }
45887         if(!this.disable.alignments){
45888             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
45889             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
45890             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
45891         }
45892         if(!Roo.isSafari && !this.disable.lists){
45893             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
45894             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
45895         }
45896         
45897         var ans = this.editorcore.getAllAncestors();
45898         if (this.formatCombo) {
45899             
45900             
45901             var store = this.formatCombo.store;
45902             this.formatCombo.setValue("");
45903             for (var i =0; i < ans.length;i++) {
45904                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
45905                     // select it..
45906                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
45907                     break;
45908                 }
45909             }
45910         }
45911         
45912         
45913         
45914         // hides menus... - so this cant be on a menu...
45915         Roo.menu.MenuMgr.hideAll();
45916
45917         //this.editorsyncValue();
45918     },
45919    
45920     
45921     createFontOptions : function(){
45922         var buf = [], fs = this.fontFamilies, ff, lc;
45923         
45924         
45925         
45926         for(var i = 0, len = fs.length; i< len; i++){
45927             ff = fs[i];
45928             lc = ff.toLowerCase();
45929             buf.push(
45930                 '<option value="',lc,'" style="font-family:',ff,';"',
45931                     (this.defaultFont == lc ? ' selected="true">' : '>'),
45932                     ff,
45933                 '</option>'
45934             );
45935         }
45936         return buf.join('');
45937     },
45938     
45939     toggleSourceEdit : function(sourceEditMode){
45940         
45941         Roo.log("toolbar toogle");
45942         if(sourceEditMode === undefined){
45943             sourceEditMode = !this.sourceEditMode;
45944         }
45945         this.sourceEditMode = sourceEditMode === true;
45946         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
45947         // just toggle the button?
45948         if(btn.pressed !== this.sourceEditMode){
45949             btn.toggle(this.sourceEditMode);
45950             return;
45951         }
45952         
45953         if(sourceEditMode){
45954             Roo.log("disabling buttons");
45955             this.tb.items.each(function(item){
45956                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
45957                     item.disable();
45958                 }
45959             });
45960           
45961         }else{
45962             Roo.log("enabling buttons");
45963             if(this.editorcore.initialized){
45964                 this.tb.items.each(function(item){
45965                     item.enable();
45966                 });
45967             }
45968             
45969         }
45970         Roo.log("calling toggole on editor");
45971         // tell the editor that it's been pressed..
45972         this.editor.toggleSourceEdit(sourceEditMode);
45973        
45974     },
45975      /**
45976      * Object collection of toolbar tooltips for the buttons in the editor. The key
45977      * is the command id associated with that button and the value is a valid QuickTips object.
45978      * For example:
45979 <pre><code>
45980 {
45981     bold : {
45982         title: 'Bold (Ctrl+B)',
45983         text: 'Make the selected text bold.',
45984         cls: 'x-html-editor-tip'
45985     },
45986     italic : {
45987         title: 'Italic (Ctrl+I)',
45988         text: 'Make the selected text italic.',
45989         cls: 'x-html-editor-tip'
45990     },
45991     ...
45992 </code></pre>
45993     * @type Object
45994      */
45995     buttonTips : {
45996         bold : {
45997             title: 'Bold (Ctrl+B)',
45998             text: 'Make the selected text bold.',
45999             cls: 'x-html-editor-tip'
46000         },
46001         italic : {
46002             title: 'Italic (Ctrl+I)',
46003             text: 'Make the selected text italic.',
46004             cls: 'x-html-editor-tip'
46005         },
46006         underline : {
46007             title: 'Underline (Ctrl+U)',
46008             text: 'Underline the selected text.',
46009             cls: 'x-html-editor-tip'
46010         },
46011         strikethrough : {
46012             title: 'Strikethrough',
46013             text: 'Strikethrough the selected text.',
46014             cls: 'x-html-editor-tip'
46015         },
46016         increasefontsize : {
46017             title: 'Grow Text',
46018             text: 'Increase the font size.',
46019             cls: 'x-html-editor-tip'
46020         },
46021         decreasefontsize : {
46022             title: 'Shrink Text',
46023             text: 'Decrease the font size.',
46024             cls: 'x-html-editor-tip'
46025         },
46026         backcolor : {
46027             title: 'Text Highlight Color',
46028             text: 'Change the background color of the selected text.',
46029             cls: 'x-html-editor-tip'
46030         },
46031         forecolor : {
46032             title: 'Font Color',
46033             text: 'Change the color of the selected text.',
46034             cls: 'x-html-editor-tip'
46035         },
46036         justifyleft : {
46037             title: 'Align Text Left',
46038             text: 'Align text to the left.',
46039             cls: 'x-html-editor-tip'
46040         },
46041         justifycenter : {
46042             title: 'Center Text',
46043             text: 'Center text in the editor.',
46044             cls: 'x-html-editor-tip'
46045         },
46046         justifyright : {
46047             title: 'Align Text Right',
46048             text: 'Align text to the right.',
46049             cls: 'x-html-editor-tip'
46050         },
46051         insertunorderedlist : {
46052             title: 'Bullet List',
46053             text: 'Start a bulleted list.',
46054             cls: 'x-html-editor-tip'
46055         },
46056         insertorderedlist : {
46057             title: 'Numbered List',
46058             text: 'Start a numbered list.',
46059             cls: 'x-html-editor-tip'
46060         },
46061         createlink : {
46062             title: 'Hyperlink',
46063             text: 'Make the selected text a hyperlink.',
46064             cls: 'x-html-editor-tip'
46065         },
46066         sourceedit : {
46067             title: 'Source Edit',
46068             text: 'Switch to source editing mode.',
46069             cls: 'x-html-editor-tip'
46070         }
46071     },
46072     // private
46073     onDestroy : function(){
46074         if(this.rendered){
46075             
46076             this.tb.items.each(function(item){
46077                 if(item.menu){
46078                     item.menu.removeAll();
46079                     if(item.menu.el){
46080                         item.menu.el.destroy();
46081                     }
46082                 }
46083                 item.destroy();
46084             });
46085              
46086         }
46087     },
46088     onFirstFocus: function() {
46089         this.tb.items.each(function(item){
46090            item.enable();
46091         });
46092     }
46093 });
46094
46095
46096
46097
46098 // <script type="text/javascript">
46099 /*
46100  * Based on
46101  * Ext JS Library 1.1.1
46102  * Copyright(c) 2006-2007, Ext JS, LLC.
46103  *  
46104  
46105  */
46106
46107  
46108 /**
46109  * @class Roo.form.HtmlEditor.ToolbarContext
46110  * Context Toolbar
46111  * 
46112  * Usage:
46113  *
46114  new Roo.form.HtmlEditor({
46115     ....
46116     toolbars : [
46117         { xtype: 'ToolbarStandard', styles : {} }
46118         { xtype: 'ToolbarContext', disable : {} }
46119     ]
46120 })
46121
46122      
46123  * 
46124  * @config : {Object} disable List of elements to disable.. (not done yet.)
46125  * @config : {Object} styles  Map of styles available.
46126  * 
46127  */
46128
46129 Roo.form.HtmlEditor.ToolbarContext = function(config)
46130 {
46131     
46132     Roo.apply(this, config);
46133     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
46134     // dont call parent... till later.
46135     this.styles = this.styles || {};
46136 }
46137
46138  
46139
46140 Roo.form.HtmlEditor.ToolbarContext.types = {
46141     'IMG' : {
46142         width : {
46143             title: "Width",
46144             width: 40
46145         },
46146         height:  {
46147             title: "Height",
46148             width: 40
46149         },
46150         align: {
46151             title: "Align",
46152             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
46153             width : 80
46154             
46155         },
46156         border: {
46157             title: "Border",
46158             width: 40
46159         },
46160         alt: {
46161             title: "Alt",
46162             width: 120
46163         },
46164         src : {
46165             title: "Src",
46166             width: 220
46167         }
46168         
46169     },
46170     'A' : {
46171         name : {
46172             title: "Name",
46173             width: 50
46174         },
46175         target:  {
46176             title: "Target",
46177             width: 120
46178         },
46179         href:  {
46180             title: "Href",
46181             width: 220
46182         } // border?
46183         
46184     },
46185     'TABLE' : {
46186         rows : {
46187             title: "Rows",
46188             width: 20
46189         },
46190         cols : {
46191             title: "Cols",
46192             width: 20
46193         },
46194         width : {
46195             title: "Width",
46196             width: 40
46197         },
46198         height : {
46199             title: "Height",
46200             width: 40
46201         },
46202         border : {
46203             title: "Border",
46204             width: 20
46205         }
46206     },
46207     'TD' : {
46208         width : {
46209             title: "Width",
46210             width: 40
46211         },
46212         height : {
46213             title: "Height",
46214             width: 40
46215         },   
46216         align: {
46217             title: "Align",
46218             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
46219             width: 80
46220         },
46221         valign: {
46222             title: "Valign",
46223             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
46224             width: 80
46225         },
46226         colspan: {
46227             title: "Colspan",
46228             width: 20
46229             
46230         },
46231          'font-family'  : {
46232             title : "Font",
46233             style : 'fontFamily',
46234             displayField: 'display',
46235             optname : 'font-family',
46236             width: 140
46237         }
46238     },
46239     'INPUT' : {
46240         name : {
46241             title: "name",
46242             width: 120
46243         },
46244         value : {
46245             title: "Value",
46246             width: 120
46247         },
46248         width : {
46249             title: "Width",
46250             width: 40
46251         }
46252     },
46253     'LABEL' : {
46254         'for' : {
46255             title: "For",
46256             width: 120
46257         }
46258     },
46259     'TEXTAREA' : {
46260           name : {
46261             title: "name",
46262             width: 120
46263         },
46264         rows : {
46265             title: "Rows",
46266             width: 20
46267         },
46268         cols : {
46269             title: "Cols",
46270             width: 20
46271         }
46272     },
46273     'SELECT' : {
46274         name : {
46275             title: "name",
46276             width: 120
46277         },
46278         selectoptions : {
46279             title: "Options",
46280             width: 200
46281         }
46282     },
46283     
46284     // should we really allow this??
46285     // should this just be 
46286     'BODY' : {
46287         title : {
46288             title: "Title",
46289             width: 200,
46290             disabled : true
46291         }
46292     },
46293     'SPAN' : {
46294         'font-family'  : {
46295             title : "Font",
46296             style : 'fontFamily',
46297             displayField: 'display',
46298             optname : 'font-family',
46299             width: 140
46300         }
46301     },
46302     'DIV' : {
46303         'font-family'  : {
46304             title : "Font",
46305             style : 'fontFamily',
46306             displayField: 'display',
46307             optname : 'font-family',
46308             width: 140
46309         }
46310     },
46311      'P' : {
46312         'font-family'  : {
46313             title : "Font",
46314             style : 'fontFamily',
46315             displayField: 'display',
46316             optname : 'font-family',
46317             width: 140
46318         }
46319     },
46320     
46321     '*' : {
46322         // empty..
46323     }
46324
46325 };
46326
46327 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
46328 Roo.form.HtmlEditor.ToolbarContext.stores = false;
46329
46330 Roo.form.HtmlEditor.ToolbarContext.options = {
46331         'font-family'  : [ 
46332                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
46333                 [ 'Courier New', 'Courier New'],
46334                 [ 'Tahoma', 'Tahoma'],
46335                 [ 'Times New Roman,serif', 'Times'],
46336                 [ 'Verdana','Verdana' ]
46337         ]
46338 };
46339
46340 // fixme - these need to be configurable..
46341  
46342
46343 //Roo.form.HtmlEditor.ToolbarContext.types
46344
46345
46346 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
46347     
46348     tb: false,
46349     
46350     rendered: false,
46351     
46352     editor : false,
46353     editorcore : false,
46354     /**
46355      * @cfg {Object} disable  List of toolbar elements to disable
46356          
46357      */
46358     disable : false,
46359     /**
46360      * @cfg {Object} styles List of styles 
46361      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
46362      *
46363      * These must be defined in the page, so they get rendered correctly..
46364      * .headline { }
46365      * TD.underline { }
46366      * 
46367      */
46368     styles : false,
46369     
46370     options: false,
46371     
46372     toolbars : false,
46373     
46374     init : function(editor)
46375     {
46376         this.editor = editor;
46377         this.editorcore = editor.editorcore ? editor.editorcore : editor;
46378         var editorcore = this.editorcore;
46379         
46380         var fid = editorcore.frameId;
46381         var etb = this;
46382         function btn(id, toggle, handler){
46383             var xid = fid + '-'+ id ;
46384             return {
46385                 id : xid,
46386                 cmd : id,
46387                 cls : 'x-btn-icon x-edit-'+id,
46388                 enableToggle:toggle !== false,
46389                 scope: editorcore, // was editor...
46390                 handler:handler||editorcore.relayBtnCmd,
46391                 clickEvent:'mousedown',
46392                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
46393                 tabIndex:-1
46394             };
46395         }
46396         // create a new element.
46397         var wdiv = editor.wrap.createChild({
46398                 tag: 'div'
46399             }, editor.wrap.dom.firstChild.nextSibling, true);
46400         
46401         // can we do this more than once??
46402         
46403          // stop form submits
46404       
46405  
46406         // disable everything...
46407         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46408         this.toolbars = {};
46409            
46410         for (var i in  ty) {
46411           
46412             this.toolbars[i] = this.buildToolbar(ty[i],i);
46413         }
46414         this.tb = this.toolbars.BODY;
46415         this.tb.el.show();
46416         this.buildFooter();
46417         this.footer.show();
46418         editor.on('hide', function( ) { this.footer.hide() }, this);
46419         editor.on('show', function( ) { this.footer.show() }, this);
46420         
46421          
46422         this.rendered = true;
46423         
46424         // the all the btns;
46425         editor.on('editorevent', this.updateToolbar, this);
46426         // other toolbars need to implement this..
46427         //editor.on('editmodechange', this.updateToolbar, this);
46428     },
46429     
46430     
46431     
46432     /**
46433      * Protected method that will not generally be called directly. It triggers
46434      * a toolbar update by reading the markup state of the current selection in the editor.
46435      *
46436      * Note you can force an update by calling on('editorevent', scope, false)
46437      */
46438     updateToolbar: function(editor,ev,sel){
46439
46440         //Roo.log(ev);
46441         // capture mouse up - this is handy for selecting images..
46442         // perhaps should go somewhere else...
46443         if(!this.editorcore.activated){
46444              this.editor.onFirstFocus();
46445             return;
46446         }
46447         
46448         
46449         
46450         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
46451         // selectNode - might want to handle IE?
46452         if (ev &&
46453             (ev.type == 'mouseup' || ev.type == 'click' ) &&
46454             ev.target && ev.target.tagName == 'IMG') {
46455             // they have click on an image...
46456             // let's see if we can change the selection...
46457             sel = ev.target;
46458          
46459               var nodeRange = sel.ownerDocument.createRange();
46460             try {
46461                 nodeRange.selectNode(sel);
46462             } catch (e) {
46463                 nodeRange.selectNodeContents(sel);
46464             }
46465             //nodeRange.collapse(true);
46466             var s = this.editorcore.win.getSelection();
46467             s.removeAllRanges();
46468             s.addRange(nodeRange);
46469         }  
46470         
46471       
46472         var updateFooter = sel ? false : true;
46473         
46474         
46475         var ans = this.editorcore.getAllAncestors();
46476         
46477         // pick
46478         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46479         
46480         if (!sel) { 
46481             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
46482             sel = sel ? sel : this.editorcore.doc.body;
46483             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
46484             
46485         }
46486         // pick a menu that exists..
46487         var tn = sel.tagName.toUpperCase();
46488         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
46489         
46490         tn = sel.tagName.toUpperCase();
46491         
46492         var lastSel = this.tb.selectedNode;
46493         
46494         this.tb.selectedNode = sel;
46495         
46496         // if current menu does not match..
46497         
46498         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
46499                 
46500             this.tb.el.hide();
46501             ///console.log("show: " + tn);
46502             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
46503             this.tb.el.show();
46504             // update name
46505             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
46506             
46507             
46508             // update attributes
46509             if (this.tb.fields) {
46510                 this.tb.fields.each(function(e) {
46511                     if (e.stylename) {
46512                         e.setValue(sel.style[e.stylename]);
46513                         return;
46514                     } 
46515                    e.setValue(sel.getAttribute(e.attrname));
46516                 });
46517             }
46518             
46519             var hasStyles = false;
46520             for(var i in this.styles) {
46521                 hasStyles = true;
46522                 break;
46523             }
46524             
46525             // update styles
46526             if (hasStyles) { 
46527                 var st = this.tb.fields.item(0);
46528                 
46529                 st.store.removeAll();
46530                
46531                 
46532                 var cn = sel.className.split(/\s+/);
46533                 
46534                 var avs = [];
46535                 if (this.styles['*']) {
46536                     
46537                     Roo.each(this.styles['*'], function(v) {
46538                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
46539                     });
46540                 }
46541                 if (this.styles[tn]) { 
46542                     Roo.each(this.styles[tn], function(v) {
46543                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
46544                     });
46545                 }
46546                 
46547                 st.store.loadData(avs);
46548                 st.collapse();
46549                 st.setValue(cn);
46550             }
46551             // flag our selected Node.
46552             this.tb.selectedNode = sel;
46553            
46554            
46555             Roo.menu.MenuMgr.hideAll();
46556
46557         }
46558         
46559         if (!updateFooter) {
46560             //this.footDisp.dom.innerHTML = ''; 
46561             return;
46562         }
46563         // update the footer
46564         //
46565         var html = '';
46566         
46567         this.footerEls = ans.reverse();
46568         Roo.each(this.footerEls, function(a,i) {
46569             if (!a) { return; }
46570             html += html.length ? ' &gt; '  :  '';
46571             
46572             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
46573             
46574         });
46575        
46576         // 
46577         var sz = this.footDisp.up('td').getSize();
46578         this.footDisp.dom.style.width = (sz.width -10) + 'px';
46579         this.footDisp.dom.style.marginLeft = '5px';
46580         
46581         this.footDisp.dom.style.overflow = 'hidden';
46582         
46583         this.footDisp.dom.innerHTML = html;
46584             
46585         //this.editorsyncValue();
46586     },
46587      
46588     
46589    
46590        
46591     // private
46592     onDestroy : function(){
46593         if(this.rendered){
46594             
46595             this.tb.items.each(function(item){
46596                 if(item.menu){
46597                     item.menu.removeAll();
46598                     if(item.menu.el){
46599                         item.menu.el.destroy();
46600                     }
46601                 }
46602                 item.destroy();
46603             });
46604              
46605         }
46606     },
46607     onFirstFocus: function() {
46608         // need to do this for all the toolbars..
46609         this.tb.items.each(function(item){
46610            item.enable();
46611         });
46612     },
46613     buildToolbar: function(tlist, nm)
46614     {
46615         var editor = this.editor;
46616         var editorcore = this.editorcore;
46617          // create a new element.
46618         var wdiv = editor.wrap.createChild({
46619                 tag: 'div'
46620             }, editor.wrap.dom.firstChild.nextSibling, true);
46621         
46622        
46623         var tb = new Roo.Toolbar(wdiv);
46624         // add the name..
46625         
46626         tb.add(nm+ ":&nbsp;");
46627         
46628         var styles = [];
46629         for(var i in this.styles) {
46630             styles.push(i);
46631         }
46632         
46633         // styles...
46634         if (styles && styles.length) {
46635             
46636             // this needs a multi-select checkbox...
46637             tb.addField( new Roo.form.ComboBox({
46638                 store: new Roo.data.SimpleStore({
46639                     id : 'val',
46640                     fields: ['val', 'selected'],
46641                     data : [] 
46642                 }),
46643                 name : '-roo-edit-className',
46644                 attrname : 'className',
46645                 displayField: 'val',
46646                 typeAhead: false,
46647                 mode: 'local',
46648                 editable : false,
46649                 triggerAction: 'all',
46650                 emptyText:'Select Style',
46651                 selectOnFocus:true,
46652                 width: 130,
46653                 listeners : {
46654                     'select': function(c, r, i) {
46655                         // initial support only for on class per el..
46656                         tb.selectedNode.className =  r ? r.get('val') : '';
46657                         editorcore.syncValue();
46658                     }
46659                 }
46660     
46661             }));
46662         }
46663         
46664         var tbc = Roo.form.HtmlEditor.ToolbarContext;
46665         var tbops = tbc.options;
46666         
46667         for (var i in tlist) {
46668             
46669             var item = tlist[i];
46670             tb.add(item.title + ":&nbsp;");
46671             
46672             
46673             //optname == used so you can configure the options available..
46674             var opts = item.opts ? item.opts : false;
46675             if (item.optname) {
46676                 opts = tbops[item.optname];
46677            
46678             }
46679             
46680             if (opts) {
46681                 // opts == pulldown..
46682                 tb.addField( new Roo.form.ComboBox({
46683                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
46684                         id : 'val',
46685                         fields: ['val', 'display'],
46686                         data : opts  
46687                     }),
46688                     name : '-roo-edit-' + i,
46689                     attrname : i,
46690                     stylename : item.style ? item.style : false,
46691                     displayField: item.displayField ? item.displayField : 'val',
46692                     valueField :  'val',
46693                     typeAhead: false,
46694                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
46695                     editable : false,
46696                     triggerAction: 'all',
46697                     emptyText:'Select',
46698                     selectOnFocus:true,
46699                     width: item.width ? item.width  : 130,
46700                     listeners : {
46701                         'select': function(c, r, i) {
46702                             if (c.stylename) {
46703                                 tb.selectedNode.style[c.stylename] =  r.get('val');
46704                                 return;
46705                             }
46706                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
46707                         }
46708                     }
46709
46710                 }));
46711                 continue;
46712                     
46713                  
46714                 
46715                 tb.addField( new Roo.form.TextField({
46716                     name: i,
46717                     width: 100,
46718                     //allowBlank:false,
46719                     value: ''
46720                 }));
46721                 continue;
46722             }
46723             tb.addField( new Roo.form.TextField({
46724                 name: '-roo-edit-' + i,
46725                 attrname : i,
46726                 
46727                 width: item.width,
46728                 //allowBlank:true,
46729                 value: '',
46730                 listeners: {
46731                     'change' : function(f, nv, ov) {
46732                         tb.selectedNode.setAttribute(f.attrname, nv);
46733                         editorcore.syncValue();
46734                     }
46735                 }
46736             }));
46737              
46738         }
46739         
46740         var _this = this;
46741         
46742         if(nm == 'BODY'){
46743             tb.addSeparator();
46744         
46745             tb.addButton( {
46746                 text: 'Stylesheets',
46747
46748                 listeners : {
46749                     click : function ()
46750                     {
46751                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
46752                     }
46753                 }
46754             });
46755         }
46756         
46757         tb.addFill();
46758         tb.addButton( {
46759             text: 'Remove Tag',
46760     
46761             listeners : {
46762                 click : function ()
46763                 {
46764                     // remove
46765                     // undo does not work.
46766                      
46767                     var sn = tb.selectedNode;
46768                     
46769                     var pn = sn.parentNode;
46770                     
46771                     var stn =  sn.childNodes[0];
46772                     var en = sn.childNodes[sn.childNodes.length - 1 ];
46773                     while (sn.childNodes.length) {
46774                         var node = sn.childNodes[0];
46775                         sn.removeChild(node);
46776                         //Roo.log(node);
46777                         pn.insertBefore(node, sn);
46778                         
46779                     }
46780                     pn.removeChild(sn);
46781                     var range = editorcore.createRange();
46782         
46783                     range.setStart(stn,0);
46784                     range.setEnd(en,0); //????
46785                     //range.selectNode(sel);
46786                     
46787                     
46788                     var selection = editorcore.getSelection();
46789                     selection.removeAllRanges();
46790                     selection.addRange(range);
46791                     
46792                     
46793                     
46794                     //_this.updateToolbar(null, null, pn);
46795                     _this.updateToolbar(null, null, null);
46796                     _this.footDisp.dom.innerHTML = ''; 
46797                 }
46798             }
46799             
46800                     
46801                 
46802             
46803         });
46804         
46805         
46806         tb.el.on('click', function(e){
46807             e.preventDefault(); // what does this do?
46808         });
46809         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
46810         tb.el.hide();
46811         tb.name = nm;
46812         // dont need to disable them... as they will get hidden
46813         return tb;
46814          
46815         
46816     },
46817     buildFooter : function()
46818     {
46819         
46820         var fel = this.editor.wrap.createChild();
46821         this.footer = new Roo.Toolbar(fel);
46822         // toolbar has scrolly on left / right?
46823         var footDisp= new Roo.Toolbar.Fill();
46824         var _t = this;
46825         this.footer.add(
46826             {
46827                 text : '&lt;',
46828                 xtype: 'Button',
46829                 handler : function() {
46830                     _t.footDisp.scrollTo('left',0,true)
46831                 }
46832             }
46833         );
46834         this.footer.add( footDisp );
46835         this.footer.add( 
46836             {
46837                 text : '&gt;',
46838                 xtype: 'Button',
46839                 handler : function() {
46840                     // no animation..
46841                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
46842                 }
46843             }
46844         );
46845         var fel = Roo.get(footDisp.el);
46846         fel.addClass('x-editor-context');
46847         this.footDispWrap = fel; 
46848         this.footDispWrap.overflow  = 'hidden';
46849         
46850         this.footDisp = fel.createChild();
46851         this.footDispWrap.on('click', this.onContextClick, this)
46852         
46853         
46854     },
46855     onContextClick : function (ev,dom)
46856     {
46857         ev.preventDefault();
46858         var  cn = dom.className;
46859         //Roo.log(cn);
46860         if (!cn.match(/x-ed-loc-/)) {
46861             return;
46862         }
46863         var n = cn.split('-').pop();
46864         var ans = this.footerEls;
46865         var sel = ans[n];
46866         
46867          // pick
46868         var range = this.editorcore.createRange();
46869         
46870         range.selectNodeContents(sel);
46871         //range.selectNode(sel);
46872         
46873         
46874         var selection = this.editorcore.getSelection();
46875         selection.removeAllRanges();
46876         selection.addRange(range);
46877         
46878         
46879         
46880         this.updateToolbar(null, null, sel);
46881         
46882         
46883     }
46884     
46885     
46886     
46887     
46888     
46889 });
46890
46891
46892
46893
46894
46895 /*
46896  * Based on:
46897  * Ext JS Library 1.1.1
46898  * Copyright(c) 2006-2007, Ext JS, LLC.
46899  *
46900  * Originally Released Under LGPL - original licence link has changed is not relivant.
46901  *
46902  * Fork - LGPL
46903  * <script type="text/javascript">
46904  */
46905  
46906 /**
46907  * @class Roo.form.BasicForm
46908  * @extends Roo.util.Observable
46909  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
46910  * @constructor
46911  * @param {String/HTMLElement/Roo.Element} el The form element or its id
46912  * @param {Object} config Configuration options
46913  */
46914 Roo.form.BasicForm = function(el, config){
46915     this.allItems = [];
46916     this.childForms = [];
46917     Roo.apply(this, config);
46918     /*
46919      * The Roo.form.Field items in this form.
46920      * @type MixedCollection
46921      */
46922      
46923      
46924     this.items = new Roo.util.MixedCollection(false, function(o){
46925         return o.id || (o.id = Roo.id());
46926     });
46927     this.addEvents({
46928         /**
46929          * @event beforeaction
46930          * Fires before any action is performed. Return false to cancel the action.
46931          * @param {Form} this
46932          * @param {Action} action The action to be performed
46933          */
46934         beforeaction: true,
46935         /**
46936          * @event actionfailed
46937          * Fires when an action fails.
46938          * @param {Form} this
46939          * @param {Action} action The action that failed
46940          */
46941         actionfailed : true,
46942         /**
46943          * @event actioncomplete
46944          * Fires when an action is completed.
46945          * @param {Form} this
46946          * @param {Action} action The action that completed
46947          */
46948         actioncomplete : true
46949     });
46950     if(el){
46951         this.initEl(el);
46952     }
46953     Roo.form.BasicForm.superclass.constructor.call(this);
46954     
46955     Roo.form.BasicForm.popover.apply();
46956 };
46957
46958 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
46959     /**
46960      * @cfg {String} method
46961      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
46962      */
46963     /**
46964      * @cfg {DataReader} reader
46965      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
46966      * This is optional as there is built-in support for processing JSON.
46967      */
46968     /**
46969      * @cfg {DataReader} errorReader
46970      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
46971      * This is completely optional as there is built-in support for processing JSON.
46972      */
46973     /**
46974      * @cfg {String} url
46975      * The URL to use for form actions if one isn't supplied in the action options.
46976      */
46977     /**
46978      * @cfg {Boolean} fileUpload
46979      * Set to true if this form is a file upload.
46980      */
46981      
46982     /**
46983      * @cfg {Object} baseParams
46984      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
46985      */
46986      /**
46987      
46988     /**
46989      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
46990      */
46991     timeout: 30,
46992
46993     // private
46994     activeAction : null,
46995
46996     /**
46997      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
46998      * or setValues() data instead of when the form was first created.
46999      */
47000     trackResetOnLoad : false,
47001     
47002     
47003     /**
47004      * childForms - used for multi-tab forms
47005      * @type {Array}
47006      */
47007     childForms : false,
47008     
47009     /**
47010      * allItems - full list of fields.
47011      * @type {Array}
47012      */
47013     allItems : false,
47014     
47015     /**
47016      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
47017      * element by passing it or its id or mask the form itself by passing in true.
47018      * @type Mixed
47019      */
47020     waitMsgTarget : false,
47021     
47022     /**
47023      * @type Boolean
47024      */
47025     disableMask : false,
47026     
47027     /**
47028      * @cfg {Boolean} errorMask (true|false) default false
47029      */
47030     errorMask : false,
47031     
47032     /**
47033      * @cfg {Number} maskOffset Default 100
47034      */
47035     maskOffset : 100,
47036
47037     // private
47038     initEl : function(el){
47039         this.el = Roo.get(el);
47040         this.id = this.el.id || Roo.id();
47041         this.el.on('submit', this.onSubmit, this);
47042         this.el.addClass('x-form');
47043     },
47044
47045     // private
47046     onSubmit : function(e){
47047         e.stopEvent();
47048     },
47049
47050     /**
47051      * Returns true if client-side validation on the form is successful.
47052      * @return Boolean
47053      */
47054     isValid : function(){
47055         var valid = true;
47056         var target = false;
47057         this.items.each(function(f){
47058             if(f.validate()){
47059                 return;
47060             }
47061             
47062             valid = false;
47063                 
47064             if(!target && f.el.isVisible(true)){
47065                 target = f;
47066             }
47067         });
47068         
47069         if(this.errorMask && !valid){
47070             Roo.form.BasicForm.popover.mask(this, target);
47071         }
47072         
47073         return valid;
47074     },
47075
47076     /**
47077      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
47078      * @return Boolean
47079      */
47080     isDirty : function(){
47081         var dirty = false;
47082         this.items.each(function(f){
47083            if(f.isDirty()){
47084                dirty = true;
47085                return false;
47086            }
47087         });
47088         return dirty;
47089     },
47090     
47091     /**
47092      * Returns true if any fields in this form have changed since their original load. (New version)
47093      * @return Boolean
47094      */
47095     
47096     hasChanged : function()
47097     {
47098         var dirty = false;
47099         this.items.each(function(f){
47100            if(f.hasChanged()){
47101                dirty = true;
47102                return false;
47103            }
47104         });
47105         return dirty;
47106         
47107     },
47108     /**
47109      * Resets all hasChanged to 'false' -
47110      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
47111      * So hasChanged storage is only to be used for this purpose
47112      * @return Boolean
47113      */
47114     resetHasChanged : function()
47115     {
47116         this.items.each(function(f){
47117            f.resetHasChanged();
47118         });
47119         
47120     },
47121     
47122     
47123     /**
47124      * Performs a predefined action (submit or load) or custom actions you define on this form.
47125      * @param {String} actionName The name of the action type
47126      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
47127      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
47128      * accept other config options):
47129      * <pre>
47130 Property          Type             Description
47131 ----------------  ---------------  ----------------------------------------------------------------------------------
47132 url               String           The url for the action (defaults to the form's url)
47133 method            String           The form method to use (defaults to the form's method, or POST if not defined)
47134 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
47135 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
47136                                    validate the form on the client (defaults to false)
47137      * </pre>
47138      * @return {BasicForm} this
47139      */
47140     doAction : function(action, options){
47141         if(typeof action == 'string'){
47142             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
47143         }
47144         if(this.fireEvent('beforeaction', this, action) !== false){
47145             this.beforeAction(action);
47146             action.run.defer(100, action);
47147         }
47148         return this;
47149     },
47150
47151     /**
47152      * Shortcut to do a submit action.
47153      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
47154      * @return {BasicForm} this
47155      */
47156     submit : function(options){
47157         this.doAction('submit', options);
47158         return this;
47159     },
47160
47161     /**
47162      * Shortcut to do a load action.
47163      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
47164      * @return {BasicForm} this
47165      */
47166     load : function(options){
47167         this.doAction('load', options);
47168         return this;
47169     },
47170
47171     /**
47172      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
47173      * @param {Record} record The record to edit
47174      * @return {BasicForm} this
47175      */
47176     updateRecord : function(record){
47177         record.beginEdit();
47178         var fs = record.fields;
47179         fs.each(function(f){
47180             var field = this.findField(f.name);
47181             if(field){
47182                 record.set(f.name, field.getValue());
47183             }
47184         }, this);
47185         record.endEdit();
47186         return this;
47187     },
47188
47189     /**
47190      * Loads an Roo.data.Record into this form.
47191      * @param {Record} record The record to load
47192      * @return {BasicForm} this
47193      */
47194     loadRecord : function(record){
47195         this.setValues(record.data);
47196         return this;
47197     },
47198
47199     // private
47200     beforeAction : function(action){
47201         var o = action.options;
47202         
47203         if(!this.disableMask) {
47204             if(this.waitMsgTarget === true){
47205                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
47206             }else if(this.waitMsgTarget){
47207                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
47208                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
47209             }else {
47210                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
47211             }
47212         }
47213         
47214          
47215     },
47216
47217     // private
47218     afterAction : function(action, success){
47219         this.activeAction = null;
47220         var o = action.options;
47221         
47222         if(!this.disableMask) {
47223             if(this.waitMsgTarget === true){
47224                 this.el.unmask();
47225             }else if(this.waitMsgTarget){
47226                 this.waitMsgTarget.unmask();
47227             }else{
47228                 Roo.MessageBox.updateProgress(1);
47229                 Roo.MessageBox.hide();
47230             }
47231         }
47232         
47233         if(success){
47234             if(o.reset){
47235                 this.reset();
47236             }
47237             Roo.callback(o.success, o.scope, [this, action]);
47238             this.fireEvent('actioncomplete', this, action);
47239             
47240         }else{
47241             
47242             // failure condition..
47243             // we have a scenario where updates need confirming.
47244             // eg. if a locking scenario exists..
47245             // we look for { errors : { needs_confirm : true }} in the response.
47246             if (
47247                 (typeof(action.result) != 'undefined')  &&
47248                 (typeof(action.result.errors) != 'undefined')  &&
47249                 (typeof(action.result.errors.needs_confirm) != 'undefined')
47250            ){
47251                 var _t = this;
47252                 Roo.MessageBox.confirm(
47253                     "Change requires confirmation",
47254                     action.result.errorMsg,
47255                     function(r) {
47256                         if (r != 'yes') {
47257                             return;
47258                         }
47259                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
47260                     }
47261                     
47262                 );
47263                 
47264                 
47265                 
47266                 return;
47267             }
47268             
47269             Roo.callback(o.failure, o.scope, [this, action]);
47270             // show an error message if no failed handler is set..
47271             if (!this.hasListener('actionfailed')) {
47272                 Roo.MessageBox.alert("Error",
47273                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
47274                         action.result.errorMsg :
47275                         "Saving Failed, please check your entries or try again"
47276                 );
47277             }
47278             
47279             this.fireEvent('actionfailed', this, action);
47280         }
47281         
47282     },
47283
47284     /**
47285      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
47286      * @param {String} id The value to search for
47287      * @return Field
47288      */
47289     findField : function(id){
47290         var field = this.items.get(id);
47291         if(!field){
47292             this.items.each(function(f){
47293                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
47294                     field = f;
47295                     return false;
47296                 }
47297             });
47298         }
47299         return field || null;
47300     },
47301
47302     /**
47303      * Add a secondary form to this one, 
47304      * Used to provide tabbed forms. One form is primary, with hidden values 
47305      * which mirror the elements from the other forms.
47306      * 
47307      * @param {Roo.form.Form} form to add.
47308      * 
47309      */
47310     addForm : function(form)
47311     {
47312        
47313         if (this.childForms.indexOf(form) > -1) {
47314             // already added..
47315             return;
47316         }
47317         this.childForms.push(form);
47318         var n = '';
47319         Roo.each(form.allItems, function (fe) {
47320             
47321             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
47322             if (this.findField(n)) { // already added..
47323                 return;
47324             }
47325             var add = new Roo.form.Hidden({
47326                 name : n
47327             });
47328             add.render(this.el);
47329             
47330             this.add( add );
47331         }, this);
47332         
47333     },
47334     /**
47335      * Mark fields in this form invalid in bulk.
47336      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
47337      * @return {BasicForm} this
47338      */
47339     markInvalid : function(errors){
47340         if(errors instanceof Array){
47341             for(var i = 0, len = errors.length; i < len; i++){
47342                 var fieldError = errors[i];
47343                 var f = this.findField(fieldError.id);
47344                 if(f){
47345                     f.markInvalid(fieldError.msg);
47346                 }
47347             }
47348         }else{
47349             var field, id;
47350             for(id in errors){
47351                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
47352                     field.markInvalid(errors[id]);
47353                 }
47354             }
47355         }
47356         Roo.each(this.childForms || [], function (f) {
47357             f.markInvalid(errors);
47358         });
47359         
47360         return this;
47361     },
47362
47363     /**
47364      * Set values for fields in this form in bulk.
47365      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
47366      * @return {BasicForm} this
47367      */
47368     setValues : function(values){
47369         if(values instanceof Array){ // array of objects
47370             for(var i = 0, len = values.length; i < len; i++){
47371                 var v = values[i];
47372                 var f = this.findField(v.id);
47373                 if(f){
47374                     f.setValue(v.value);
47375                     if(this.trackResetOnLoad){
47376                         f.originalValue = f.getValue();
47377                     }
47378                 }
47379             }
47380         }else{ // object hash
47381             var field, id;
47382             for(id in values){
47383                 if(typeof values[id] != 'function' && (field = this.findField(id))){
47384                     
47385                     if (field.setFromData && 
47386                         field.valueField && 
47387                         field.displayField &&
47388                         // combos' with local stores can 
47389                         // be queried via setValue()
47390                         // to set their value..
47391                         (field.store && !field.store.isLocal)
47392                         ) {
47393                         // it's a combo
47394                         var sd = { };
47395                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
47396                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
47397                         field.setFromData(sd);
47398                         
47399                     } else {
47400                         field.setValue(values[id]);
47401                     }
47402                     
47403                     
47404                     if(this.trackResetOnLoad){
47405                         field.originalValue = field.getValue();
47406                     }
47407                 }
47408             }
47409         }
47410         this.resetHasChanged();
47411         
47412         
47413         Roo.each(this.childForms || [], function (f) {
47414             f.setValues(values);
47415             f.resetHasChanged();
47416         });
47417                 
47418         return this;
47419     },
47420  
47421     /**
47422      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
47423      * they are returned as an array.
47424      * @param {Boolean} asString
47425      * @return {Object}
47426      */
47427     getValues : function(asString){
47428         if (this.childForms) {
47429             // copy values from the child forms
47430             Roo.each(this.childForms, function (f) {
47431                 this.setValues(f.getValues());
47432             }, this);
47433         }
47434         
47435         // use formdata
47436         if (typeof(FormData) != 'undefined' && asString !== true) {
47437             // this relies on a 'recent' version of chrome apparently...
47438             try {
47439                 var fd = (new FormData(this.el.dom)).entries();
47440                 var ret = {};
47441                 var ent = fd.next();
47442                 while (!ent.done) {
47443                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
47444                     ent = fd.next();
47445                 };
47446                 return ret;
47447             } catch(e) {
47448                 
47449             }
47450             
47451         }
47452         
47453         
47454         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
47455         if(asString === true){
47456             return fs;
47457         }
47458         return Roo.urlDecode(fs);
47459     },
47460     
47461     /**
47462      * Returns the fields in this form as an object with key/value pairs. 
47463      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
47464      * @return {Object}
47465      */
47466     getFieldValues : function(with_hidden)
47467     {
47468         if (this.childForms) {
47469             // copy values from the child forms
47470             // should this call getFieldValues - probably not as we do not currently copy
47471             // hidden fields when we generate..
47472             Roo.each(this.childForms, function (f) {
47473                 this.setValues(f.getValues());
47474             }, this);
47475         }
47476         
47477         var ret = {};
47478         this.items.each(function(f){
47479             if (!f.getName()) {
47480                 return;
47481             }
47482             var v = f.getValue();
47483             if (f.inputType =='radio') {
47484                 if (typeof(ret[f.getName()]) == 'undefined') {
47485                     ret[f.getName()] = ''; // empty..
47486                 }
47487                 
47488                 if (!f.el.dom.checked) {
47489                     return;
47490                     
47491                 }
47492                 v = f.el.dom.value;
47493                 
47494             }
47495             
47496             // not sure if this supported any more..
47497             if ((typeof(v) == 'object') && f.getRawValue) {
47498                 v = f.getRawValue() ; // dates..
47499             }
47500             // combo boxes where name != hiddenName...
47501             if (f.name != f.getName()) {
47502                 ret[f.name] = f.getRawValue();
47503             }
47504             ret[f.getName()] = v;
47505         });
47506         
47507         return ret;
47508     },
47509
47510     /**
47511      * Clears all invalid messages in this form.
47512      * @return {BasicForm} this
47513      */
47514     clearInvalid : function(){
47515         this.items.each(function(f){
47516            f.clearInvalid();
47517         });
47518         
47519         Roo.each(this.childForms || [], function (f) {
47520             f.clearInvalid();
47521         });
47522         
47523         
47524         return this;
47525     },
47526
47527     /**
47528      * Resets this form.
47529      * @return {BasicForm} this
47530      */
47531     reset : function(){
47532         this.items.each(function(f){
47533             f.reset();
47534         });
47535         
47536         Roo.each(this.childForms || [], function (f) {
47537             f.reset();
47538         });
47539         this.resetHasChanged();
47540         
47541         return this;
47542     },
47543
47544     /**
47545      * Add Roo.form components to this form.
47546      * @param {Field} field1
47547      * @param {Field} field2 (optional)
47548      * @param {Field} etc (optional)
47549      * @return {BasicForm} this
47550      */
47551     add : function(){
47552         this.items.addAll(Array.prototype.slice.call(arguments, 0));
47553         return this;
47554     },
47555
47556
47557     /**
47558      * Removes a field from the items collection (does NOT remove its markup).
47559      * @param {Field} field
47560      * @return {BasicForm} this
47561      */
47562     remove : function(field){
47563         this.items.remove(field);
47564         return this;
47565     },
47566
47567     /**
47568      * Looks at the fields in this form, checks them for an id attribute,
47569      * and calls applyTo on the existing dom element with that id.
47570      * @return {BasicForm} this
47571      */
47572     render : function(){
47573         this.items.each(function(f){
47574             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
47575                 f.applyTo(f.id);
47576             }
47577         });
47578         return this;
47579     },
47580
47581     /**
47582      * Calls {@link Ext#apply} for all fields in this form with the passed object.
47583      * @param {Object} values
47584      * @return {BasicForm} this
47585      */
47586     applyToFields : function(o){
47587         this.items.each(function(f){
47588            Roo.apply(f, o);
47589         });
47590         return this;
47591     },
47592
47593     /**
47594      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
47595      * @param {Object} values
47596      * @return {BasicForm} this
47597      */
47598     applyIfToFields : function(o){
47599         this.items.each(function(f){
47600            Roo.applyIf(f, o);
47601         });
47602         return this;
47603     }
47604 });
47605
47606 // back compat
47607 Roo.BasicForm = Roo.form.BasicForm;
47608
47609 Roo.apply(Roo.form.BasicForm, {
47610     
47611     popover : {
47612         
47613         padding : 5,
47614         
47615         isApplied : false,
47616         
47617         isMasked : false,
47618         
47619         form : false,
47620         
47621         target : false,
47622         
47623         intervalID : false,
47624         
47625         maskEl : false,
47626         
47627         apply : function()
47628         {
47629             if(this.isApplied){
47630                 return;
47631             }
47632             
47633             this.maskEl = {
47634                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
47635                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
47636                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
47637                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
47638             };
47639             
47640             this.maskEl.top.enableDisplayMode("block");
47641             this.maskEl.left.enableDisplayMode("block");
47642             this.maskEl.bottom.enableDisplayMode("block");
47643             this.maskEl.right.enableDisplayMode("block");
47644             
47645             Roo.get(document.body).on('click', function(){
47646                 this.unmask();
47647             }, this);
47648             
47649             Roo.get(document.body).on('touchstart', function(){
47650                 this.unmask();
47651             }, this);
47652             
47653             this.isApplied = true
47654         },
47655         
47656         mask : function(form, target)
47657         {
47658             this.form = form;
47659             
47660             this.target = target;
47661             
47662             if(!this.form.errorMask || !target.el){
47663                 return;
47664             }
47665             
47666             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
47667             
47668             var ot = this.target.el.calcOffsetsTo(scrollable);
47669             
47670             var scrollTo = ot[1] - this.form.maskOffset;
47671             
47672             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
47673             
47674             scrollable.scrollTo('top', scrollTo);
47675             
47676             var el = this.target.wrap || this.target.el;
47677             
47678             var box = el.getBox();
47679             
47680             this.maskEl.top.setStyle('position', 'absolute');
47681             this.maskEl.top.setStyle('z-index', 10000);
47682             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
47683             this.maskEl.top.setLeft(0);
47684             this.maskEl.top.setTop(0);
47685             this.maskEl.top.show();
47686             
47687             this.maskEl.left.setStyle('position', 'absolute');
47688             this.maskEl.left.setStyle('z-index', 10000);
47689             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
47690             this.maskEl.left.setLeft(0);
47691             this.maskEl.left.setTop(box.y - this.padding);
47692             this.maskEl.left.show();
47693
47694             this.maskEl.bottom.setStyle('position', 'absolute');
47695             this.maskEl.bottom.setStyle('z-index', 10000);
47696             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
47697             this.maskEl.bottom.setLeft(0);
47698             this.maskEl.bottom.setTop(box.bottom + this.padding);
47699             this.maskEl.bottom.show();
47700
47701             this.maskEl.right.setStyle('position', 'absolute');
47702             this.maskEl.right.setStyle('z-index', 10000);
47703             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
47704             this.maskEl.right.setLeft(box.right + this.padding);
47705             this.maskEl.right.setTop(box.y - this.padding);
47706             this.maskEl.right.show();
47707
47708             this.intervalID = window.setInterval(function() {
47709                 Roo.form.BasicForm.popover.unmask();
47710             }, 10000);
47711
47712             window.onwheel = function(){ return false;};
47713             
47714             (function(){ this.isMasked = true; }).defer(500, this);
47715             
47716         },
47717         
47718         unmask : function()
47719         {
47720             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
47721                 return;
47722             }
47723             
47724             this.maskEl.top.setStyle('position', 'absolute');
47725             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
47726             this.maskEl.top.hide();
47727
47728             this.maskEl.left.setStyle('position', 'absolute');
47729             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
47730             this.maskEl.left.hide();
47731
47732             this.maskEl.bottom.setStyle('position', 'absolute');
47733             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
47734             this.maskEl.bottom.hide();
47735
47736             this.maskEl.right.setStyle('position', 'absolute');
47737             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
47738             this.maskEl.right.hide();
47739             
47740             window.onwheel = function(){ return true;};
47741             
47742             if(this.intervalID){
47743                 window.clearInterval(this.intervalID);
47744                 this.intervalID = false;
47745             }
47746             
47747             this.isMasked = false;
47748             
47749         }
47750         
47751     }
47752     
47753 });/*
47754  * Based on:
47755  * Ext JS Library 1.1.1
47756  * Copyright(c) 2006-2007, Ext JS, LLC.
47757  *
47758  * Originally Released Under LGPL - original licence link has changed is not relivant.
47759  *
47760  * Fork - LGPL
47761  * <script type="text/javascript">
47762  */
47763
47764 /**
47765  * @class Roo.form.Form
47766  * @extends Roo.form.BasicForm
47767  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
47768  * @constructor
47769  * @param {Object} config Configuration options
47770  */
47771 Roo.form.Form = function(config){
47772     var xitems =  [];
47773     if (config.items) {
47774         xitems = config.items;
47775         delete config.items;
47776     }
47777    
47778     
47779     Roo.form.Form.superclass.constructor.call(this, null, config);
47780     this.url = this.url || this.action;
47781     if(!this.root){
47782         this.root = new Roo.form.Layout(Roo.applyIf({
47783             id: Roo.id()
47784         }, config));
47785     }
47786     this.active = this.root;
47787     /**
47788      * Array of all the buttons that have been added to this form via {@link addButton}
47789      * @type Array
47790      */
47791     this.buttons = [];
47792     this.allItems = [];
47793     this.addEvents({
47794         /**
47795          * @event clientvalidation
47796          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
47797          * @param {Form} this
47798          * @param {Boolean} valid true if the form has passed client-side validation
47799          */
47800         clientvalidation: true,
47801         /**
47802          * @event rendered
47803          * Fires when the form is rendered
47804          * @param {Roo.form.Form} form
47805          */
47806         rendered : true
47807     });
47808     
47809     if (this.progressUrl) {
47810             // push a hidden field onto the list of fields..
47811             this.addxtype( {
47812                     xns: Roo.form, 
47813                     xtype : 'Hidden', 
47814                     name : 'UPLOAD_IDENTIFIER' 
47815             });
47816         }
47817         
47818     
47819     Roo.each(xitems, this.addxtype, this);
47820     
47821 };
47822
47823 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
47824     /**
47825      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
47826      */
47827     /**
47828      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
47829      */
47830     /**
47831      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
47832      */
47833     buttonAlign:'center',
47834
47835     /**
47836      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
47837      */
47838     minButtonWidth:75,
47839
47840     /**
47841      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
47842      * This property cascades to child containers if not set.
47843      */
47844     labelAlign:'left',
47845
47846     /**
47847      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
47848      * fires a looping event with that state. This is required to bind buttons to the valid
47849      * state using the config value formBind:true on the button.
47850      */
47851     monitorValid : false,
47852
47853     /**
47854      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
47855      */
47856     monitorPoll : 200,
47857     
47858     /**
47859      * @cfg {String} progressUrl - Url to return progress data 
47860      */
47861     
47862     progressUrl : false,
47863     /**
47864      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
47865      * sending a formdata with extra parameters - eg uploaded elements.
47866      */
47867     
47868     formData : false,
47869     
47870     /**
47871      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
47872      * fields are added and the column is closed. If no fields are passed the column remains open
47873      * until end() is called.
47874      * @param {Object} config The config to pass to the column
47875      * @param {Field} field1 (optional)
47876      * @param {Field} field2 (optional)
47877      * @param {Field} etc (optional)
47878      * @return Column The column container object
47879      */
47880     column : function(c){
47881         var col = new Roo.form.Column(c);
47882         this.start(col);
47883         if(arguments.length > 1){ // duplicate code required because of Opera
47884             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47885             this.end();
47886         }
47887         return col;
47888     },
47889
47890     /**
47891      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
47892      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
47893      * until end() is called.
47894      * @param {Object} config The config to pass to the fieldset
47895      * @param {Field} field1 (optional)
47896      * @param {Field} field2 (optional)
47897      * @param {Field} etc (optional)
47898      * @return FieldSet The fieldset container object
47899      */
47900     fieldset : function(c){
47901         var fs = new Roo.form.FieldSet(c);
47902         this.start(fs);
47903         if(arguments.length > 1){ // duplicate code required because of Opera
47904             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47905             this.end();
47906         }
47907         return fs;
47908     },
47909
47910     /**
47911      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
47912      * fields are added and the container is closed. If no fields are passed the container remains open
47913      * until end() is called.
47914      * @param {Object} config The config to pass to the Layout
47915      * @param {Field} field1 (optional)
47916      * @param {Field} field2 (optional)
47917      * @param {Field} etc (optional)
47918      * @return Layout The container object
47919      */
47920     container : function(c){
47921         var l = new Roo.form.Layout(c);
47922         this.start(l);
47923         if(arguments.length > 1){ // duplicate code required because of Opera
47924             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47925             this.end();
47926         }
47927         return l;
47928     },
47929
47930     /**
47931      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
47932      * @param {Object} container A Roo.form.Layout or subclass of Layout
47933      * @return {Form} this
47934      */
47935     start : function(c){
47936         // cascade label info
47937         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
47938         this.active.stack.push(c);
47939         c.ownerCt = this.active;
47940         this.active = c;
47941         return this;
47942     },
47943
47944     /**
47945      * Closes the current open container
47946      * @return {Form} this
47947      */
47948     end : function(){
47949         if(this.active == this.root){
47950             return this;
47951         }
47952         this.active = this.active.ownerCt;
47953         return this;
47954     },
47955
47956     /**
47957      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
47958      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
47959      * as the label of the field.
47960      * @param {Field} field1
47961      * @param {Field} field2 (optional)
47962      * @param {Field} etc. (optional)
47963      * @return {Form} this
47964      */
47965     add : function(){
47966         this.active.stack.push.apply(this.active.stack, arguments);
47967         this.allItems.push.apply(this.allItems,arguments);
47968         var r = [];
47969         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
47970             if(a[i].isFormField){
47971                 r.push(a[i]);
47972             }
47973         }
47974         if(r.length > 0){
47975             Roo.form.Form.superclass.add.apply(this, r);
47976         }
47977         return this;
47978     },
47979     
47980
47981     
47982     
47983     
47984      /**
47985      * Find any element that has been added to a form, using it's ID or name
47986      * This can include framesets, columns etc. along with regular fields..
47987      * @param {String} id - id or name to find.
47988      
47989      * @return {Element} e - or false if nothing found.
47990      */
47991     findbyId : function(id)
47992     {
47993         var ret = false;
47994         if (!id) {
47995             return ret;
47996         }
47997         Roo.each(this.allItems, function(f){
47998             if (f.id == id || f.name == id ){
47999                 ret = f;
48000                 return false;
48001             }
48002         });
48003         return ret;
48004     },
48005
48006     
48007     
48008     /**
48009      * Render this form into the passed container. This should only be called once!
48010      * @param {String/HTMLElement/Element} container The element this component should be rendered into
48011      * @return {Form} this
48012      */
48013     render : function(ct)
48014     {
48015         
48016         
48017         
48018         ct = Roo.get(ct);
48019         var o = this.autoCreate || {
48020             tag: 'form',
48021             method : this.method || 'POST',
48022             id : this.id || Roo.id()
48023         };
48024         this.initEl(ct.createChild(o));
48025
48026         this.root.render(this.el);
48027         
48028        
48029              
48030         this.items.each(function(f){
48031             f.render('x-form-el-'+f.id);
48032         });
48033
48034         if(this.buttons.length > 0){
48035             // tables are required to maintain order and for correct IE layout
48036             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
48037                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
48038                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
48039             }}, null, true);
48040             var tr = tb.getElementsByTagName('tr')[0];
48041             for(var i = 0, len = this.buttons.length; i < len; i++) {
48042                 var b = this.buttons[i];
48043                 var td = document.createElement('td');
48044                 td.className = 'x-form-btn-td';
48045                 b.render(tr.appendChild(td));
48046             }
48047         }
48048         if(this.monitorValid){ // initialize after render
48049             this.startMonitoring();
48050         }
48051         this.fireEvent('rendered', this);
48052         return this;
48053     },
48054
48055     /**
48056      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
48057      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
48058      * object or a valid Roo.DomHelper element config
48059      * @param {Function} handler The function called when the button is clicked
48060      * @param {Object} scope (optional) The scope of the handler function
48061      * @return {Roo.Button}
48062      */
48063     addButton : function(config, handler, scope){
48064         var bc = {
48065             handler: handler,
48066             scope: scope,
48067             minWidth: this.minButtonWidth,
48068             hideParent:true
48069         };
48070         if(typeof config == "string"){
48071             bc.text = config;
48072         }else{
48073             Roo.apply(bc, config);
48074         }
48075         var btn = new Roo.Button(null, bc);
48076         this.buttons.push(btn);
48077         return btn;
48078     },
48079
48080      /**
48081      * Adds a series of form elements (using the xtype property as the factory method.
48082      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
48083      * @param {Object} config 
48084      */
48085     
48086     addxtype : function()
48087     {
48088         var ar = Array.prototype.slice.call(arguments, 0);
48089         var ret = false;
48090         for(var i = 0; i < ar.length; i++) {
48091             if (!ar[i]) {
48092                 continue; // skip -- if this happends something invalid got sent, we 
48093                 // should ignore it, as basically that interface element will not show up
48094                 // and that should be pretty obvious!!
48095             }
48096             
48097             if (Roo.form[ar[i].xtype]) {
48098                 ar[i].form = this;
48099                 var fe = Roo.factory(ar[i], Roo.form);
48100                 if (!ret) {
48101                     ret = fe;
48102                 }
48103                 fe.form = this;
48104                 if (fe.store) {
48105                     fe.store.form = this;
48106                 }
48107                 if (fe.isLayout) {  
48108                          
48109                     this.start(fe);
48110                     this.allItems.push(fe);
48111                     if (fe.items && fe.addxtype) {
48112                         fe.addxtype.apply(fe, fe.items);
48113                         delete fe.items;
48114                     }
48115                      this.end();
48116                     continue;
48117                 }
48118                 
48119                 
48120                  
48121                 this.add(fe);
48122               //  console.log('adding ' + ar[i].xtype);
48123             }
48124             if (ar[i].xtype == 'Button') {  
48125                 //console.log('adding button');
48126                 //console.log(ar[i]);
48127                 this.addButton(ar[i]);
48128                 this.allItems.push(fe);
48129                 continue;
48130             }
48131             
48132             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
48133                 alert('end is not supported on xtype any more, use items');
48134             //    this.end();
48135             //    //console.log('adding end');
48136             }
48137             
48138         }
48139         return ret;
48140     },
48141     
48142     /**
48143      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
48144      * option "monitorValid"
48145      */
48146     startMonitoring : function(){
48147         if(!this.bound){
48148             this.bound = true;
48149             Roo.TaskMgr.start({
48150                 run : this.bindHandler,
48151                 interval : this.monitorPoll || 200,
48152                 scope: this
48153             });
48154         }
48155     },
48156
48157     /**
48158      * Stops monitoring of the valid state of this form
48159      */
48160     stopMonitoring : function(){
48161         this.bound = false;
48162     },
48163
48164     // private
48165     bindHandler : function(){
48166         if(!this.bound){
48167             return false; // stops binding
48168         }
48169         var valid = true;
48170         this.items.each(function(f){
48171             if(!f.isValid(true)){
48172                 valid = false;
48173                 return false;
48174             }
48175         });
48176         for(var i = 0, len = this.buttons.length; i < len; i++){
48177             var btn = this.buttons[i];
48178             if(btn.formBind === true && btn.disabled === valid){
48179                 btn.setDisabled(!valid);
48180             }
48181         }
48182         this.fireEvent('clientvalidation', this, valid);
48183     }
48184     
48185     
48186     
48187     
48188     
48189     
48190     
48191     
48192 });
48193
48194
48195 // back compat
48196 Roo.Form = Roo.form.Form;
48197 /*
48198  * Based on:
48199  * Ext JS Library 1.1.1
48200  * Copyright(c) 2006-2007, Ext JS, LLC.
48201  *
48202  * Originally Released Under LGPL - original licence link has changed is not relivant.
48203  *
48204  * Fork - LGPL
48205  * <script type="text/javascript">
48206  */
48207
48208 // as we use this in bootstrap.
48209 Roo.namespace('Roo.form');
48210  /**
48211  * @class Roo.form.Action
48212  * Internal Class used to handle form actions
48213  * @constructor
48214  * @param {Roo.form.BasicForm} el The form element or its id
48215  * @param {Object} config Configuration options
48216  */
48217
48218  
48219  
48220 // define the action interface
48221 Roo.form.Action = function(form, options){
48222     this.form = form;
48223     this.options = options || {};
48224 };
48225 /**
48226  * Client Validation Failed
48227  * @const 
48228  */
48229 Roo.form.Action.CLIENT_INVALID = 'client';
48230 /**
48231  * Server Validation Failed
48232  * @const 
48233  */
48234 Roo.form.Action.SERVER_INVALID = 'server';
48235  /**
48236  * Connect to Server Failed
48237  * @const 
48238  */
48239 Roo.form.Action.CONNECT_FAILURE = 'connect';
48240 /**
48241  * Reading Data from Server Failed
48242  * @const 
48243  */
48244 Roo.form.Action.LOAD_FAILURE = 'load';
48245
48246 Roo.form.Action.prototype = {
48247     type : 'default',
48248     failureType : undefined,
48249     response : undefined,
48250     result : undefined,
48251
48252     // interface method
48253     run : function(options){
48254
48255     },
48256
48257     // interface method
48258     success : function(response){
48259
48260     },
48261
48262     // interface method
48263     handleResponse : function(response){
48264
48265     },
48266
48267     // default connection failure
48268     failure : function(response){
48269         
48270         this.response = response;
48271         this.failureType = Roo.form.Action.CONNECT_FAILURE;
48272         this.form.afterAction(this, false);
48273     },
48274
48275     processResponse : function(response){
48276         this.response = response;
48277         if(!response.responseText){
48278             return true;
48279         }
48280         this.result = this.handleResponse(response);
48281         return this.result;
48282     },
48283
48284     // utility functions used internally
48285     getUrl : function(appendParams){
48286         var url = this.options.url || this.form.url || this.form.el.dom.action;
48287         if(appendParams){
48288             var p = this.getParams();
48289             if(p){
48290                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
48291             }
48292         }
48293         return url;
48294     },
48295
48296     getMethod : function(){
48297         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
48298     },
48299
48300     getParams : function(){
48301         var bp = this.form.baseParams;
48302         var p = this.options.params;
48303         if(p){
48304             if(typeof p == "object"){
48305                 p = Roo.urlEncode(Roo.applyIf(p, bp));
48306             }else if(typeof p == 'string' && bp){
48307                 p += '&' + Roo.urlEncode(bp);
48308             }
48309         }else if(bp){
48310             p = Roo.urlEncode(bp);
48311         }
48312         return p;
48313     },
48314
48315     createCallback : function(){
48316         return {
48317             success: this.success,
48318             failure: this.failure,
48319             scope: this,
48320             timeout: (this.form.timeout*1000),
48321             upload: this.form.fileUpload ? this.success : undefined
48322         };
48323     }
48324 };
48325
48326 Roo.form.Action.Submit = function(form, options){
48327     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
48328 };
48329
48330 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
48331     type : 'submit',
48332
48333     haveProgress : false,
48334     uploadComplete : false,
48335     
48336     // uploadProgress indicator.
48337     uploadProgress : function()
48338     {
48339         if (!this.form.progressUrl) {
48340             return;
48341         }
48342         
48343         if (!this.haveProgress) {
48344             Roo.MessageBox.progress("Uploading", "Uploading");
48345         }
48346         if (this.uploadComplete) {
48347            Roo.MessageBox.hide();
48348            return;
48349         }
48350         
48351         this.haveProgress = true;
48352    
48353         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
48354         
48355         var c = new Roo.data.Connection();
48356         c.request({
48357             url : this.form.progressUrl,
48358             params: {
48359                 id : uid
48360             },
48361             method: 'GET',
48362             success : function(req){
48363                //console.log(data);
48364                 var rdata = false;
48365                 var edata;
48366                 try  {
48367                    rdata = Roo.decode(req.responseText)
48368                 } catch (e) {
48369                     Roo.log("Invalid data from server..");
48370                     Roo.log(edata);
48371                     return;
48372                 }
48373                 if (!rdata || !rdata.success) {
48374                     Roo.log(rdata);
48375                     Roo.MessageBox.alert(Roo.encode(rdata));
48376                     return;
48377                 }
48378                 var data = rdata.data;
48379                 
48380                 if (this.uploadComplete) {
48381                    Roo.MessageBox.hide();
48382                    return;
48383                 }
48384                    
48385                 if (data){
48386                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
48387                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
48388                     );
48389                 }
48390                 this.uploadProgress.defer(2000,this);
48391             },
48392        
48393             failure: function(data) {
48394                 Roo.log('progress url failed ');
48395                 Roo.log(data);
48396             },
48397             scope : this
48398         });
48399            
48400     },
48401     
48402     
48403     run : function()
48404     {
48405         // run get Values on the form, so it syncs any secondary forms.
48406         this.form.getValues();
48407         
48408         var o = this.options;
48409         var method = this.getMethod();
48410         var isPost = method == 'POST';
48411         if(o.clientValidation === false || this.form.isValid()){
48412             
48413             if (this.form.progressUrl) {
48414                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
48415                     (new Date() * 1) + '' + Math.random());
48416                     
48417             } 
48418             
48419             
48420             Roo.Ajax.request(Roo.apply(this.createCallback(), {
48421                 form:this.form.el.dom,
48422                 url:this.getUrl(!isPost),
48423                 method: method,
48424                 params:isPost ? this.getParams() : null,
48425                 isUpload: this.form.fileUpload,
48426                 formData : this.form.formData
48427             }));
48428             
48429             this.uploadProgress();
48430
48431         }else if (o.clientValidation !== false){ // client validation failed
48432             this.failureType = Roo.form.Action.CLIENT_INVALID;
48433             this.form.afterAction(this, false);
48434         }
48435     },
48436
48437     success : function(response)
48438     {
48439         this.uploadComplete= true;
48440         if (this.haveProgress) {
48441             Roo.MessageBox.hide();
48442         }
48443         
48444         
48445         var result = this.processResponse(response);
48446         if(result === true || result.success){
48447             this.form.afterAction(this, true);
48448             return;
48449         }
48450         if(result.errors){
48451             this.form.markInvalid(result.errors);
48452             this.failureType = Roo.form.Action.SERVER_INVALID;
48453         }
48454         this.form.afterAction(this, false);
48455     },
48456     failure : function(response)
48457     {
48458         this.uploadComplete= true;
48459         if (this.haveProgress) {
48460             Roo.MessageBox.hide();
48461         }
48462         
48463         this.response = response;
48464         this.failureType = Roo.form.Action.CONNECT_FAILURE;
48465         this.form.afterAction(this, false);
48466     },
48467     
48468     handleResponse : function(response){
48469         if(this.form.errorReader){
48470             var rs = this.form.errorReader.read(response);
48471             var errors = [];
48472             if(rs.records){
48473                 for(var i = 0, len = rs.records.length; i < len; i++) {
48474                     var r = rs.records[i];
48475                     errors[i] = r.data;
48476                 }
48477             }
48478             if(errors.length < 1){
48479                 errors = null;
48480             }
48481             return {
48482                 success : rs.success,
48483                 errors : errors
48484             };
48485         }
48486         var ret = false;
48487         try {
48488             ret = Roo.decode(response.responseText);
48489         } catch (e) {
48490             ret = {
48491                 success: false,
48492                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
48493                 errors : []
48494             };
48495         }
48496         return ret;
48497         
48498     }
48499 });
48500
48501
48502 Roo.form.Action.Load = function(form, options){
48503     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
48504     this.reader = this.form.reader;
48505 };
48506
48507 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
48508     type : 'load',
48509
48510     run : function(){
48511         
48512         Roo.Ajax.request(Roo.apply(
48513                 this.createCallback(), {
48514                     method:this.getMethod(),
48515                     url:this.getUrl(false),
48516                     params:this.getParams()
48517         }));
48518     },
48519
48520     success : function(response){
48521         
48522         var result = this.processResponse(response);
48523         if(result === true || !result.success || !result.data){
48524             this.failureType = Roo.form.Action.LOAD_FAILURE;
48525             this.form.afterAction(this, false);
48526             return;
48527         }
48528         this.form.clearInvalid();
48529         this.form.setValues(result.data);
48530         this.form.afterAction(this, true);
48531     },
48532
48533     handleResponse : function(response){
48534         if(this.form.reader){
48535             var rs = this.form.reader.read(response);
48536             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
48537             return {
48538                 success : rs.success,
48539                 data : data
48540             };
48541         }
48542         return Roo.decode(response.responseText);
48543     }
48544 });
48545
48546 Roo.form.Action.ACTION_TYPES = {
48547     'load' : Roo.form.Action.Load,
48548     'submit' : Roo.form.Action.Submit
48549 };/*
48550  * Based on:
48551  * Ext JS Library 1.1.1
48552  * Copyright(c) 2006-2007, Ext JS, LLC.
48553  *
48554  * Originally Released Under LGPL - original licence link has changed is not relivant.
48555  *
48556  * Fork - LGPL
48557  * <script type="text/javascript">
48558  */
48559  
48560 /**
48561  * @class Roo.form.Layout
48562  * @extends Roo.Component
48563  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
48564  * @constructor
48565  * @param {Object} config Configuration options
48566  */
48567 Roo.form.Layout = function(config){
48568     var xitems = [];
48569     if (config.items) {
48570         xitems = config.items;
48571         delete config.items;
48572     }
48573     Roo.form.Layout.superclass.constructor.call(this, config);
48574     this.stack = [];
48575     Roo.each(xitems, this.addxtype, this);
48576      
48577 };
48578
48579 Roo.extend(Roo.form.Layout, Roo.Component, {
48580     /**
48581      * @cfg {String/Object} autoCreate
48582      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
48583      */
48584     /**
48585      * @cfg {String/Object/Function} style
48586      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
48587      * a function which returns such a specification.
48588      */
48589     /**
48590      * @cfg {String} labelAlign
48591      * Valid values are "left," "top" and "right" (defaults to "left")
48592      */
48593     /**
48594      * @cfg {Number} labelWidth
48595      * Fixed width in pixels of all field labels (defaults to undefined)
48596      */
48597     /**
48598      * @cfg {Boolean} clear
48599      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
48600      */
48601     clear : true,
48602     /**
48603      * @cfg {String} labelSeparator
48604      * The separator to use after field labels (defaults to ':')
48605      */
48606     labelSeparator : ':',
48607     /**
48608      * @cfg {Boolean} hideLabels
48609      * True to suppress the display of field labels in this layout (defaults to false)
48610      */
48611     hideLabels : false,
48612
48613     // private
48614     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
48615     
48616     isLayout : true,
48617     
48618     // private
48619     onRender : function(ct, position){
48620         if(this.el){ // from markup
48621             this.el = Roo.get(this.el);
48622         }else {  // generate
48623             var cfg = this.getAutoCreate();
48624             this.el = ct.createChild(cfg, position);
48625         }
48626         if(this.style){
48627             this.el.applyStyles(this.style);
48628         }
48629         if(this.labelAlign){
48630             this.el.addClass('x-form-label-'+this.labelAlign);
48631         }
48632         if(this.hideLabels){
48633             this.labelStyle = "display:none";
48634             this.elementStyle = "padding-left:0;";
48635         }else{
48636             if(typeof this.labelWidth == 'number'){
48637                 this.labelStyle = "width:"+this.labelWidth+"px;";
48638                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
48639             }
48640             if(this.labelAlign == 'top'){
48641                 this.labelStyle = "width:auto;";
48642                 this.elementStyle = "padding-left:0;";
48643             }
48644         }
48645         var stack = this.stack;
48646         var slen = stack.length;
48647         if(slen > 0){
48648             if(!this.fieldTpl){
48649                 var t = new Roo.Template(
48650                     '<div class="x-form-item {5}">',
48651                         '<label for="{0}" style="{2}">{1}{4}</label>',
48652                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
48653                         '</div>',
48654                     '</div><div class="x-form-clear-left"></div>'
48655                 );
48656                 t.disableFormats = true;
48657                 t.compile();
48658                 Roo.form.Layout.prototype.fieldTpl = t;
48659             }
48660             for(var i = 0; i < slen; i++) {
48661                 if(stack[i].isFormField){
48662                     this.renderField(stack[i]);
48663                 }else{
48664                     this.renderComponent(stack[i]);
48665                 }
48666             }
48667         }
48668         if(this.clear){
48669             this.el.createChild({cls:'x-form-clear'});
48670         }
48671     },
48672
48673     // private
48674     renderField : function(f){
48675         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
48676                f.id, //0
48677                f.fieldLabel, //1
48678                f.labelStyle||this.labelStyle||'', //2
48679                this.elementStyle||'', //3
48680                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
48681                f.itemCls||this.itemCls||''  //5
48682        ], true).getPrevSibling());
48683     },
48684
48685     // private
48686     renderComponent : function(c){
48687         c.render(c.isLayout ? this.el : this.el.createChild());    
48688     },
48689     /**
48690      * Adds a object form elements (using the xtype property as the factory method.)
48691      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
48692      * @param {Object} config 
48693      */
48694     addxtype : function(o)
48695     {
48696         // create the lement.
48697         o.form = this.form;
48698         var fe = Roo.factory(o, Roo.form);
48699         this.form.allItems.push(fe);
48700         this.stack.push(fe);
48701         
48702         if (fe.isFormField) {
48703             this.form.items.add(fe);
48704         }
48705          
48706         return fe;
48707     }
48708 });
48709
48710 /**
48711  * @class Roo.form.Column
48712  * @extends Roo.form.Layout
48713  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
48714  * @constructor
48715  * @param {Object} config Configuration options
48716  */
48717 Roo.form.Column = function(config){
48718     Roo.form.Column.superclass.constructor.call(this, config);
48719 };
48720
48721 Roo.extend(Roo.form.Column, Roo.form.Layout, {
48722     /**
48723      * @cfg {Number/String} width
48724      * The fixed width of the column in pixels or CSS value (defaults to "auto")
48725      */
48726     /**
48727      * @cfg {String/Object} autoCreate
48728      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
48729      */
48730
48731     // private
48732     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
48733
48734     // private
48735     onRender : function(ct, position){
48736         Roo.form.Column.superclass.onRender.call(this, ct, position);
48737         if(this.width){
48738             this.el.setWidth(this.width);
48739         }
48740     }
48741 });
48742
48743
48744 /**
48745  * @class Roo.form.Row
48746  * @extends Roo.form.Layout
48747  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
48748  * @constructor
48749  * @param {Object} config Configuration options
48750  */
48751
48752  
48753 Roo.form.Row = function(config){
48754     Roo.form.Row.superclass.constructor.call(this, config);
48755 };
48756  
48757 Roo.extend(Roo.form.Row, Roo.form.Layout, {
48758       /**
48759      * @cfg {Number/String} width
48760      * The fixed width of the column in pixels or CSS value (defaults to "auto")
48761      */
48762     /**
48763      * @cfg {Number/String} height
48764      * The fixed height of the column in pixels or CSS value (defaults to "auto")
48765      */
48766     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
48767     
48768     padWidth : 20,
48769     // private
48770     onRender : function(ct, position){
48771         //console.log('row render');
48772         if(!this.rowTpl){
48773             var t = new Roo.Template(
48774                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
48775                     '<label for="{0}" style="{2}">{1}{4}</label>',
48776                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
48777                     '</div>',
48778                 '</div>'
48779             );
48780             t.disableFormats = true;
48781             t.compile();
48782             Roo.form.Layout.prototype.rowTpl = t;
48783         }
48784         this.fieldTpl = this.rowTpl;
48785         
48786         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
48787         var labelWidth = 100;
48788         
48789         if ((this.labelAlign != 'top')) {
48790             if (typeof this.labelWidth == 'number') {
48791                 labelWidth = this.labelWidth
48792             }
48793             this.padWidth =  20 + labelWidth;
48794             
48795         }
48796         
48797         Roo.form.Column.superclass.onRender.call(this, ct, position);
48798         if(this.width){
48799             this.el.setWidth(this.width);
48800         }
48801         if(this.height){
48802             this.el.setHeight(this.height);
48803         }
48804     },
48805     
48806     // private
48807     renderField : function(f){
48808         f.fieldEl = this.fieldTpl.append(this.el, [
48809                f.id, f.fieldLabel,
48810                f.labelStyle||this.labelStyle||'',
48811                this.elementStyle||'',
48812                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
48813                f.itemCls||this.itemCls||'',
48814                f.width ? f.width + this.padWidth : 160 + this.padWidth
48815        ],true);
48816     }
48817 });
48818  
48819
48820 /**
48821  * @class Roo.form.FieldSet
48822  * @extends Roo.form.Layout
48823  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
48824  * @constructor
48825  * @param {Object} config Configuration options
48826  */
48827 Roo.form.FieldSet = function(config){
48828     Roo.form.FieldSet.superclass.constructor.call(this, config);
48829 };
48830
48831 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
48832     /**
48833      * @cfg {String} legend
48834      * The text to display as the legend for the FieldSet (defaults to '')
48835      */
48836     /**
48837      * @cfg {String/Object} autoCreate
48838      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
48839      */
48840
48841     // private
48842     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
48843
48844     // private
48845     onRender : function(ct, position){
48846         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
48847         if(this.legend){
48848             this.setLegend(this.legend);
48849         }
48850     },
48851
48852     // private
48853     setLegend : function(text){
48854         if(this.rendered){
48855             this.el.child('legend').update(text);
48856         }
48857     }
48858 });/*
48859  * Based on:
48860  * Ext JS Library 1.1.1
48861  * Copyright(c) 2006-2007, Ext JS, LLC.
48862  *
48863  * Originally Released Under LGPL - original licence link has changed is not relivant.
48864  *
48865  * Fork - LGPL
48866  * <script type="text/javascript">
48867  */
48868 /**
48869  * @class Roo.form.VTypes
48870  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
48871  * @singleton
48872  */
48873 Roo.form.VTypes = function(){
48874     // closure these in so they are only created once.
48875     var alpha = /^[a-zA-Z_]+$/;
48876     var alphanum = /^[a-zA-Z0-9_]+$/;
48877     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
48878     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
48879
48880     // All these messages and functions are configurable
48881     return {
48882         /**
48883          * The function used to validate email addresses
48884          * @param {String} value The email address
48885          */
48886         'email' : function(v){
48887             return email.test(v);
48888         },
48889         /**
48890          * The error text to display when the email validation function returns false
48891          * @type String
48892          */
48893         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
48894         /**
48895          * The keystroke filter mask to be applied on email input
48896          * @type RegExp
48897          */
48898         'emailMask' : /[a-z0-9_\.\-@]/i,
48899
48900         /**
48901          * The function used to validate URLs
48902          * @param {String} value The URL
48903          */
48904         'url' : function(v){
48905             return url.test(v);
48906         },
48907         /**
48908          * The error text to display when the url validation function returns false
48909          * @type String
48910          */
48911         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
48912         
48913         /**
48914          * The function used to validate alpha values
48915          * @param {String} value The value
48916          */
48917         'alpha' : function(v){
48918             return alpha.test(v);
48919         },
48920         /**
48921          * The error text to display when the alpha validation function returns false
48922          * @type String
48923          */
48924         'alphaText' : 'This field should only contain letters and _',
48925         /**
48926          * The keystroke filter mask to be applied on alpha input
48927          * @type RegExp
48928          */
48929         'alphaMask' : /[a-z_]/i,
48930
48931         /**
48932          * The function used to validate alphanumeric values
48933          * @param {String} value The value
48934          */
48935         'alphanum' : function(v){
48936             return alphanum.test(v);
48937         },
48938         /**
48939          * The error text to display when the alphanumeric validation function returns false
48940          * @type String
48941          */
48942         'alphanumText' : 'This field should only contain letters, numbers and _',
48943         /**
48944          * The keystroke filter mask to be applied on alphanumeric input
48945          * @type RegExp
48946          */
48947         'alphanumMask' : /[a-z0-9_]/i
48948     };
48949 }();//<script type="text/javascript">
48950
48951 /**
48952  * @class Roo.form.FCKeditor
48953  * @extends Roo.form.TextArea
48954  * Wrapper around the FCKEditor http://www.fckeditor.net
48955  * @constructor
48956  * Creates a new FCKeditor
48957  * @param {Object} config Configuration options
48958  */
48959 Roo.form.FCKeditor = function(config){
48960     Roo.form.FCKeditor.superclass.constructor.call(this, config);
48961     this.addEvents({
48962          /**
48963          * @event editorinit
48964          * Fired when the editor is initialized - you can add extra handlers here..
48965          * @param {FCKeditor} this
48966          * @param {Object} the FCK object.
48967          */
48968         editorinit : true
48969     });
48970     
48971     
48972 };
48973 Roo.form.FCKeditor.editors = { };
48974 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
48975 {
48976     //defaultAutoCreate : {
48977     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
48978     //},
48979     // private
48980     /**
48981      * @cfg {Object} fck options - see fck manual for details.
48982      */
48983     fckconfig : false,
48984     
48985     /**
48986      * @cfg {Object} fck toolbar set (Basic or Default)
48987      */
48988     toolbarSet : 'Basic',
48989     /**
48990      * @cfg {Object} fck BasePath
48991      */ 
48992     basePath : '/fckeditor/',
48993     
48994     
48995     frame : false,
48996     
48997     value : '',
48998     
48999    
49000     onRender : function(ct, position)
49001     {
49002         if(!this.el){
49003             this.defaultAutoCreate = {
49004                 tag: "textarea",
49005                 style:"width:300px;height:60px;",
49006                 autocomplete: "new-password"
49007             };
49008         }
49009         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
49010         /*
49011         if(this.grow){
49012             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
49013             if(this.preventScrollbars){
49014                 this.el.setStyle("overflow", "hidden");
49015             }
49016             this.el.setHeight(this.growMin);
49017         }
49018         */
49019         //console.log('onrender' + this.getId() );
49020         Roo.form.FCKeditor.editors[this.getId()] = this;
49021          
49022
49023         this.replaceTextarea() ;
49024         
49025     },
49026     
49027     getEditor : function() {
49028         return this.fckEditor;
49029     },
49030     /**
49031      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
49032      * @param {Mixed} value The value to set
49033      */
49034     
49035     
49036     setValue : function(value)
49037     {
49038         //console.log('setValue: ' + value);
49039         
49040         if(typeof(value) == 'undefined') { // not sure why this is happending...
49041             return;
49042         }
49043         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
49044         
49045         //if(!this.el || !this.getEditor()) {
49046         //    this.value = value;
49047             //this.setValue.defer(100,this,[value]);    
49048         //    return;
49049         //} 
49050         
49051         if(!this.getEditor()) {
49052             return;
49053         }
49054         
49055         this.getEditor().SetData(value);
49056         
49057         //
49058
49059     },
49060
49061     /**
49062      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
49063      * @return {Mixed} value The field value
49064      */
49065     getValue : function()
49066     {
49067         
49068         if (this.frame && this.frame.dom.style.display == 'none') {
49069             return Roo.form.FCKeditor.superclass.getValue.call(this);
49070         }
49071         
49072         if(!this.el || !this.getEditor()) {
49073            
49074            // this.getValue.defer(100,this); 
49075             return this.value;
49076         }
49077        
49078         
49079         var value=this.getEditor().GetData();
49080         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
49081         return Roo.form.FCKeditor.superclass.getValue.call(this);
49082         
49083
49084     },
49085
49086     /**
49087      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
49088      * @return {Mixed} value The field value
49089      */
49090     getRawValue : function()
49091     {
49092         if (this.frame && this.frame.dom.style.display == 'none') {
49093             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
49094         }
49095         
49096         if(!this.el || !this.getEditor()) {
49097             //this.getRawValue.defer(100,this); 
49098             return this.value;
49099             return;
49100         }
49101         
49102         
49103         
49104         var value=this.getEditor().GetData();
49105         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
49106         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
49107          
49108     },
49109     
49110     setSize : function(w,h) {
49111         
49112         
49113         
49114         //if (this.frame && this.frame.dom.style.display == 'none') {
49115         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
49116         //    return;
49117         //}
49118         //if(!this.el || !this.getEditor()) {
49119         //    this.setSize.defer(100,this, [w,h]); 
49120         //    return;
49121         //}
49122         
49123         
49124         
49125         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
49126         
49127         this.frame.dom.setAttribute('width', w);
49128         this.frame.dom.setAttribute('height', h);
49129         this.frame.setSize(w,h);
49130         
49131     },
49132     
49133     toggleSourceEdit : function(value) {
49134         
49135       
49136          
49137         this.el.dom.style.display = value ? '' : 'none';
49138         this.frame.dom.style.display = value ?  'none' : '';
49139         
49140     },
49141     
49142     
49143     focus: function(tag)
49144     {
49145         if (this.frame.dom.style.display == 'none') {
49146             return Roo.form.FCKeditor.superclass.focus.call(this);
49147         }
49148         if(!this.el || !this.getEditor()) {
49149             this.focus.defer(100,this, [tag]); 
49150             return;
49151         }
49152         
49153         
49154         
49155         
49156         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
49157         this.getEditor().Focus();
49158         if (tgs.length) {
49159             if (!this.getEditor().Selection.GetSelection()) {
49160                 this.focus.defer(100,this, [tag]); 
49161                 return;
49162             }
49163             
49164             
49165             var r = this.getEditor().EditorDocument.createRange();
49166             r.setStart(tgs[0],0);
49167             r.setEnd(tgs[0],0);
49168             this.getEditor().Selection.GetSelection().removeAllRanges();
49169             this.getEditor().Selection.GetSelection().addRange(r);
49170             this.getEditor().Focus();
49171         }
49172         
49173     },
49174     
49175     
49176     
49177     replaceTextarea : function()
49178     {
49179         if ( document.getElementById( this.getId() + '___Frame' ) ) {
49180             return ;
49181         }
49182         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
49183         //{
49184             // We must check the elements firstly using the Id and then the name.
49185         var oTextarea = document.getElementById( this.getId() );
49186         
49187         var colElementsByName = document.getElementsByName( this.getId() ) ;
49188          
49189         oTextarea.style.display = 'none' ;
49190
49191         if ( oTextarea.tabIndex ) {            
49192             this.TabIndex = oTextarea.tabIndex ;
49193         }
49194         
49195         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
49196         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
49197         this.frame = Roo.get(this.getId() + '___Frame')
49198     },
49199     
49200     _getConfigHtml : function()
49201     {
49202         var sConfig = '' ;
49203
49204         for ( var o in this.fckconfig ) {
49205             sConfig += sConfig.length > 0  ? '&amp;' : '';
49206             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
49207         }
49208
49209         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
49210     },
49211     
49212     
49213     _getIFrameHtml : function()
49214     {
49215         var sFile = 'fckeditor.html' ;
49216         /* no idea what this is about..
49217         try
49218         {
49219             if ( (/fcksource=true/i).test( window.top.location.search ) )
49220                 sFile = 'fckeditor.original.html' ;
49221         }
49222         catch (e) { 
49223         */
49224
49225         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
49226         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
49227         
49228         
49229         var html = '<iframe id="' + this.getId() +
49230             '___Frame" src="' + sLink +
49231             '" width="' + this.width +
49232             '" height="' + this.height + '"' +
49233             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
49234             ' frameborder="0" scrolling="no"></iframe>' ;
49235
49236         return html ;
49237     },
49238     
49239     _insertHtmlBefore : function( html, element )
49240     {
49241         if ( element.insertAdjacentHTML )       {
49242             // IE
49243             element.insertAdjacentHTML( 'beforeBegin', html ) ;
49244         } else { // Gecko
49245             var oRange = document.createRange() ;
49246             oRange.setStartBefore( element ) ;
49247             var oFragment = oRange.createContextualFragment( html );
49248             element.parentNode.insertBefore( oFragment, element ) ;
49249         }
49250     }
49251     
49252     
49253   
49254     
49255     
49256     
49257     
49258
49259 });
49260
49261 //Roo.reg('fckeditor', Roo.form.FCKeditor);
49262
49263 function FCKeditor_OnComplete(editorInstance){
49264     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
49265     f.fckEditor = editorInstance;
49266     //console.log("loaded");
49267     f.fireEvent('editorinit', f, editorInstance);
49268
49269   
49270
49271  
49272
49273
49274
49275
49276
49277
49278
49279
49280
49281
49282
49283
49284
49285
49286
49287 //<script type="text/javascript">
49288 /**
49289  * @class Roo.form.GridField
49290  * @extends Roo.form.Field
49291  * Embed a grid (or editable grid into a form)
49292  * STATUS ALPHA
49293  * 
49294  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
49295  * it needs 
49296  * xgrid.store = Roo.data.Store
49297  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
49298  * xgrid.store.reader = Roo.data.JsonReader 
49299  * 
49300  * 
49301  * @constructor
49302  * Creates a new GridField
49303  * @param {Object} config Configuration options
49304  */
49305 Roo.form.GridField = function(config){
49306     Roo.form.GridField.superclass.constructor.call(this, config);
49307      
49308 };
49309
49310 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
49311     /**
49312      * @cfg {Number} width  - used to restrict width of grid..
49313      */
49314     width : 100,
49315     /**
49316      * @cfg {Number} height - used to restrict height of grid..
49317      */
49318     height : 50,
49319      /**
49320      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
49321          * 
49322          *}
49323      */
49324     xgrid : false, 
49325     /**
49326      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
49327      * {tag: "input", type: "checkbox", autocomplete: "off"})
49328      */
49329    // defaultAutoCreate : { tag: 'div' },
49330     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
49331     /**
49332      * @cfg {String} addTitle Text to include for adding a title.
49333      */
49334     addTitle : false,
49335     //
49336     onResize : function(){
49337         Roo.form.Field.superclass.onResize.apply(this, arguments);
49338     },
49339
49340     initEvents : function(){
49341         // Roo.form.Checkbox.superclass.initEvents.call(this);
49342         // has no events...
49343        
49344     },
49345
49346
49347     getResizeEl : function(){
49348         return this.wrap;
49349     },
49350
49351     getPositionEl : function(){
49352         return this.wrap;
49353     },
49354
49355     // private
49356     onRender : function(ct, position){
49357         
49358         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
49359         var style = this.style;
49360         delete this.style;
49361         
49362         Roo.form.GridField.superclass.onRender.call(this, ct, position);
49363         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
49364         this.viewEl = this.wrap.createChild({ tag: 'div' });
49365         if (style) {
49366             this.viewEl.applyStyles(style);
49367         }
49368         if (this.width) {
49369             this.viewEl.setWidth(this.width);
49370         }
49371         if (this.height) {
49372             this.viewEl.setHeight(this.height);
49373         }
49374         //if(this.inputValue !== undefined){
49375         //this.setValue(this.value);
49376         
49377         
49378         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
49379         
49380         
49381         this.grid.render();
49382         this.grid.getDataSource().on('remove', this.refreshValue, this);
49383         this.grid.getDataSource().on('update', this.refreshValue, this);
49384         this.grid.on('afteredit', this.refreshValue, this);
49385  
49386     },
49387      
49388     
49389     /**
49390      * Sets the value of the item. 
49391      * @param {String} either an object  or a string..
49392      */
49393     setValue : function(v){
49394         //this.value = v;
49395         v = v || []; // empty set..
49396         // this does not seem smart - it really only affects memoryproxy grids..
49397         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
49398             var ds = this.grid.getDataSource();
49399             // assumes a json reader..
49400             var data = {}
49401             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
49402             ds.loadData( data);
49403         }
49404         // clear selection so it does not get stale.
49405         if (this.grid.sm) { 
49406             this.grid.sm.clearSelections();
49407         }
49408         
49409         Roo.form.GridField.superclass.setValue.call(this, v);
49410         this.refreshValue();
49411         // should load data in the grid really....
49412     },
49413     
49414     // private
49415     refreshValue: function() {
49416          var val = [];
49417         this.grid.getDataSource().each(function(r) {
49418             val.push(r.data);
49419         });
49420         this.el.dom.value = Roo.encode(val);
49421     }
49422     
49423      
49424     
49425     
49426 });/*
49427  * Based on:
49428  * Ext JS Library 1.1.1
49429  * Copyright(c) 2006-2007, Ext JS, LLC.
49430  *
49431  * Originally Released Under LGPL - original licence link has changed is not relivant.
49432  *
49433  * Fork - LGPL
49434  * <script type="text/javascript">
49435  */
49436 /**
49437  * @class Roo.form.DisplayField
49438  * @extends Roo.form.Field
49439  * A generic Field to display non-editable data.
49440  * @cfg {Boolean} closable (true|false) default false
49441  * @constructor
49442  * Creates a new Display Field item.
49443  * @param {Object} config Configuration options
49444  */
49445 Roo.form.DisplayField = function(config){
49446     Roo.form.DisplayField.superclass.constructor.call(this, config);
49447     
49448     this.addEvents({
49449         /**
49450          * @event close
49451          * Fires after the click the close btn
49452              * @param {Roo.form.DisplayField} this
49453              */
49454         close : true
49455     });
49456 };
49457
49458 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
49459     inputType:      'hidden',
49460     allowBlank:     true,
49461     readOnly:         true,
49462     
49463  
49464     /**
49465      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
49466      */
49467     focusClass : undefined,
49468     /**
49469      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
49470      */
49471     fieldClass: 'x-form-field',
49472     
49473      /**
49474      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
49475      */
49476     valueRenderer: undefined,
49477     
49478     width: 100,
49479     /**
49480      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
49481      * {tag: "input", type: "checkbox", autocomplete: "off"})
49482      */
49483      
49484  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
49485  
49486     closable : false,
49487     
49488     onResize : function(){
49489         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
49490         
49491     },
49492
49493     initEvents : function(){
49494         // Roo.form.Checkbox.superclass.initEvents.call(this);
49495         // has no events...
49496         
49497         if(this.closable){
49498             this.closeEl.on('click', this.onClose, this);
49499         }
49500        
49501     },
49502
49503
49504     getResizeEl : function(){
49505         return this.wrap;
49506     },
49507
49508     getPositionEl : function(){
49509         return this.wrap;
49510     },
49511
49512     // private
49513     onRender : function(ct, position){
49514         
49515         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
49516         //if(this.inputValue !== undefined){
49517         this.wrap = this.el.wrap();
49518         
49519         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
49520         
49521         if(this.closable){
49522             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
49523         }
49524         
49525         if (this.bodyStyle) {
49526             this.viewEl.applyStyles(this.bodyStyle);
49527         }
49528         //this.viewEl.setStyle('padding', '2px');
49529         
49530         this.setValue(this.value);
49531         
49532     },
49533 /*
49534     // private
49535     initValue : Roo.emptyFn,
49536
49537   */
49538
49539         // private
49540     onClick : function(){
49541         
49542     },
49543
49544     /**
49545      * Sets the checked state of the checkbox.
49546      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
49547      */
49548     setValue : function(v){
49549         this.value = v;
49550         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
49551         // this might be called before we have a dom element..
49552         if (!this.viewEl) {
49553             return;
49554         }
49555         this.viewEl.dom.innerHTML = html;
49556         Roo.form.DisplayField.superclass.setValue.call(this, v);
49557
49558     },
49559     
49560     onClose : function(e)
49561     {
49562         e.preventDefault();
49563         
49564         this.fireEvent('close', this);
49565     }
49566 });/*
49567  * 
49568  * Licence- LGPL
49569  * 
49570  */
49571
49572 /**
49573  * @class Roo.form.DayPicker
49574  * @extends Roo.form.Field
49575  * A Day picker show [M] [T] [W] ....
49576  * @constructor
49577  * Creates a new Day Picker
49578  * @param {Object} config Configuration options
49579  */
49580 Roo.form.DayPicker= function(config){
49581     Roo.form.DayPicker.superclass.constructor.call(this, config);
49582      
49583 };
49584
49585 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
49586     /**
49587      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
49588      */
49589     focusClass : undefined,
49590     /**
49591      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
49592      */
49593     fieldClass: "x-form-field",
49594    
49595     /**
49596      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
49597      * {tag: "input", type: "checkbox", autocomplete: "off"})
49598      */
49599     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
49600     
49601    
49602     actionMode : 'viewEl', 
49603     //
49604     // private
49605  
49606     inputType : 'hidden',
49607     
49608      
49609     inputElement: false, // real input element?
49610     basedOn: false, // ????
49611     
49612     isFormField: true, // not sure where this is needed!!!!
49613
49614     onResize : function(){
49615         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
49616         if(!this.boxLabel){
49617             this.el.alignTo(this.wrap, 'c-c');
49618         }
49619     },
49620
49621     initEvents : function(){
49622         Roo.form.Checkbox.superclass.initEvents.call(this);
49623         this.el.on("click", this.onClick,  this);
49624         this.el.on("change", this.onClick,  this);
49625     },
49626
49627
49628     getResizeEl : function(){
49629         return this.wrap;
49630     },
49631
49632     getPositionEl : function(){
49633         return this.wrap;
49634     },
49635
49636     
49637     // private
49638     onRender : function(ct, position){
49639         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
49640        
49641         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
49642         
49643         var r1 = '<table><tr>';
49644         var r2 = '<tr class="x-form-daypick-icons">';
49645         for (var i=0; i < 7; i++) {
49646             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
49647             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
49648         }
49649         
49650         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
49651         viewEl.select('img').on('click', this.onClick, this);
49652         this.viewEl = viewEl;   
49653         
49654         
49655         // this will not work on Chrome!!!
49656         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
49657         this.el.on('propertychange', this.setFromHidden,  this);  //ie
49658         
49659         
49660           
49661
49662     },
49663
49664     // private
49665     initValue : Roo.emptyFn,
49666
49667     /**
49668      * Returns the checked state of the checkbox.
49669      * @return {Boolean} True if checked, else false
49670      */
49671     getValue : function(){
49672         return this.el.dom.value;
49673         
49674     },
49675
49676         // private
49677     onClick : function(e){ 
49678         //this.setChecked(!this.checked);
49679         Roo.get(e.target).toggleClass('x-menu-item-checked');
49680         this.refreshValue();
49681         //if(this.el.dom.checked != this.checked){
49682         //    this.setValue(this.el.dom.checked);
49683        // }
49684     },
49685     
49686     // private
49687     refreshValue : function()
49688     {
49689         var val = '';
49690         this.viewEl.select('img',true).each(function(e,i,n)  {
49691             val += e.is(".x-menu-item-checked") ? String(n) : '';
49692         });
49693         this.setValue(val, true);
49694     },
49695
49696     /**
49697      * Sets the checked state of the checkbox.
49698      * On is always based on a string comparison between inputValue and the param.
49699      * @param {Boolean/String} value - the value to set 
49700      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
49701      */
49702     setValue : function(v,suppressEvent){
49703         if (!this.el.dom) {
49704             return;
49705         }
49706         var old = this.el.dom.value ;
49707         this.el.dom.value = v;
49708         if (suppressEvent) {
49709             return ;
49710         }
49711          
49712         // update display..
49713         this.viewEl.select('img',true).each(function(e,i,n)  {
49714             
49715             var on = e.is(".x-menu-item-checked");
49716             var newv = v.indexOf(String(n)) > -1;
49717             if (on != newv) {
49718                 e.toggleClass('x-menu-item-checked');
49719             }
49720             
49721         });
49722         
49723         
49724         this.fireEvent('change', this, v, old);
49725         
49726         
49727     },
49728    
49729     // handle setting of hidden value by some other method!!?!?
49730     setFromHidden: function()
49731     {
49732         if(!this.el){
49733             return;
49734         }
49735         //console.log("SET FROM HIDDEN");
49736         //alert('setFrom hidden');
49737         this.setValue(this.el.dom.value);
49738     },
49739     
49740     onDestroy : function()
49741     {
49742         if(this.viewEl){
49743             Roo.get(this.viewEl).remove();
49744         }
49745          
49746         Roo.form.DayPicker.superclass.onDestroy.call(this);
49747     }
49748
49749 });/*
49750  * RooJS Library 1.1.1
49751  * Copyright(c) 2008-2011  Alan Knowles
49752  *
49753  * License - LGPL
49754  */
49755  
49756
49757 /**
49758  * @class Roo.form.ComboCheck
49759  * @extends Roo.form.ComboBox
49760  * A combobox for multiple select items.
49761  *
49762  * FIXME - could do with a reset button..
49763  * 
49764  * @constructor
49765  * Create a new ComboCheck
49766  * @param {Object} config Configuration options
49767  */
49768 Roo.form.ComboCheck = function(config){
49769     Roo.form.ComboCheck.superclass.constructor.call(this, config);
49770     // should verify some data...
49771     // like
49772     // hiddenName = required..
49773     // displayField = required
49774     // valudField == required
49775     var req= [ 'hiddenName', 'displayField', 'valueField' ];
49776     var _t = this;
49777     Roo.each(req, function(e) {
49778         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
49779             throw "Roo.form.ComboCheck : missing value for: " + e;
49780         }
49781     });
49782     
49783     
49784 };
49785
49786 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
49787      
49788      
49789     editable : false,
49790      
49791     selectedClass: 'x-menu-item-checked', 
49792     
49793     // private
49794     onRender : function(ct, position){
49795         var _t = this;
49796         
49797         
49798         
49799         if(!this.tpl){
49800             var cls = 'x-combo-list';
49801
49802             
49803             this.tpl =  new Roo.Template({
49804                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
49805                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
49806                    '<span>{' + this.displayField + '}</span>' +
49807                     '</div>' 
49808                 
49809             });
49810         }
49811  
49812         
49813         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
49814         this.view.singleSelect = false;
49815         this.view.multiSelect = true;
49816         this.view.toggleSelect = true;
49817         this.pageTb.add(new Roo.Toolbar.Fill(), {
49818             
49819             text: 'Done',
49820             handler: function()
49821             {
49822                 _t.collapse();
49823             }
49824         });
49825     },
49826     
49827     onViewOver : function(e, t){
49828         // do nothing...
49829         return;
49830         
49831     },
49832     
49833     onViewClick : function(doFocus,index){
49834         return;
49835         
49836     },
49837     select: function () {
49838         //Roo.log("SELECT CALLED");
49839     },
49840      
49841     selectByValue : function(xv, scrollIntoView){
49842         var ar = this.getValueArray();
49843         var sels = [];
49844         
49845         Roo.each(ar, function(v) {
49846             if(v === undefined || v === null){
49847                 return;
49848             }
49849             var r = this.findRecord(this.valueField, v);
49850             if(r){
49851                 sels.push(this.store.indexOf(r))
49852                 
49853             }
49854         },this);
49855         this.view.select(sels);
49856         return false;
49857     },
49858     
49859     
49860     
49861     onSelect : function(record, index){
49862        // Roo.log("onselect Called");
49863        // this is only called by the clear button now..
49864         this.view.clearSelections();
49865         this.setValue('[]');
49866         if (this.value != this.valueBefore) {
49867             this.fireEvent('change', this, this.value, this.valueBefore);
49868             this.valueBefore = this.value;
49869         }
49870     },
49871     getValueArray : function()
49872     {
49873         var ar = [] ;
49874         
49875         try {
49876             //Roo.log(this.value);
49877             if (typeof(this.value) == 'undefined') {
49878                 return [];
49879             }
49880             var ar = Roo.decode(this.value);
49881             return  ar instanceof Array ? ar : []; //?? valid?
49882             
49883         } catch(e) {
49884             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
49885             return [];
49886         }
49887          
49888     },
49889     expand : function ()
49890     {
49891         
49892         Roo.form.ComboCheck.superclass.expand.call(this);
49893         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
49894         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
49895         
49896
49897     },
49898     
49899     collapse : function(){
49900         Roo.form.ComboCheck.superclass.collapse.call(this);
49901         var sl = this.view.getSelectedIndexes();
49902         var st = this.store;
49903         var nv = [];
49904         var tv = [];
49905         var r;
49906         Roo.each(sl, function(i) {
49907             r = st.getAt(i);
49908             nv.push(r.get(this.valueField));
49909         },this);
49910         this.setValue(Roo.encode(nv));
49911         if (this.value != this.valueBefore) {
49912
49913             this.fireEvent('change', this, this.value, this.valueBefore);
49914             this.valueBefore = this.value;
49915         }
49916         
49917     },
49918     
49919     setValue : function(v){
49920         // Roo.log(v);
49921         this.value = v;
49922         
49923         var vals = this.getValueArray();
49924         var tv = [];
49925         Roo.each(vals, function(k) {
49926             var r = this.findRecord(this.valueField, k);
49927             if(r){
49928                 tv.push(r.data[this.displayField]);
49929             }else if(this.valueNotFoundText !== undefined){
49930                 tv.push( this.valueNotFoundText );
49931             }
49932         },this);
49933        // Roo.log(tv);
49934         
49935         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
49936         this.hiddenField.value = v;
49937         this.value = v;
49938     }
49939     
49940 });/*
49941  * Based on:
49942  * Ext JS Library 1.1.1
49943  * Copyright(c) 2006-2007, Ext JS, LLC.
49944  *
49945  * Originally Released Under LGPL - original licence link has changed is not relivant.
49946  *
49947  * Fork - LGPL
49948  * <script type="text/javascript">
49949  */
49950  
49951 /**
49952  * @class Roo.form.Signature
49953  * @extends Roo.form.Field
49954  * Signature field.  
49955  * @constructor
49956  * 
49957  * @param {Object} config Configuration options
49958  */
49959
49960 Roo.form.Signature = function(config){
49961     Roo.form.Signature.superclass.constructor.call(this, config);
49962     
49963     this.addEvents({// not in used??
49964          /**
49965          * @event confirm
49966          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
49967              * @param {Roo.form.Signature} combo This combo box
49968              */
49969         'confirm' : true,
49970         /**
49971          * @event reset
49972          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
49973              * @param {Roo.form.ComboBox} combo This combo box
49974              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
49975              */
49976         'reset' : true
49977     });
49978 };
49979
49980 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
49981     /**
49982      * @cfg {Object} labels Label to use when rendering a form.
49983      * defaults to 
49984      * labels : { 
49985      *      clear : "Clear",
49986      *      confirm : "Confirm"
49987      *  }
49988      */
49989     labels : { 
49990         clear : "Clear",
49991         confirm : "Confirm"
49992     },
49993     /**
49994      * @cfg {Number} width The signature panel width (defaults to 300)
49995      */
49996     width: 300,
49997     /**
49998      * @cfg {Number} height The signature panel height (defaults to 100)
49999      */
50000     height : 100,
50001     /**
50002      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
50003      */
50004     allowBlank : false,
50005     
50006     //private
50007     // {Object} signPanel The signature SVG panel element (defaults to {})
50008     signPanel : {},
50009     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
50010     isMouseDown : false,
50011     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
50012     isConfirmed : false,
50013     // {String} signatureTmp SVG mapping string (defaults to empty string)
50014     signatureTmp : '',
50015     
50016     
50017     defaultAutoCreate : { // modified by initCompnoent..
50018         tag: "input",
50019         type:"hidden"
50020     },
50021
50022     // private
50023     onRender : function(ct, position){
50024         
50025         Roo.form.Signature.superclass.onRender.call(this, ct, position);
50026         
50027         this.wrap = this.el.wrap({
50028             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
50029         });
50030         
50031         this.createToolbar(this);
50032         this.signPanel = this.wrap.createChild({
50033                 tag: 'div',
50034                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
50035             }, this.el
50036         );
50037             
50038         this.svgID = Roo.id();
50039         this.svgEl = this.signPanel.createChild({
50040               xmlns : 'http://www.w3.org/2000/svg',
50041               tag : 'svg',
50042               id : this.svgID + "-svg",
50043               width: this.width,
50044               height: this.height,
50045               viewBox: '0 0 '+this.width+' '+this.height,
50046               cn : [
50047                 {
50048                     tag: "rect",
50049                     id: this.svgID + "-svg-r",
50050                     width: this.width,
50051                     height: this.height,
50052                     fill: "#ffa"
50053                 },
50054                 {
50055                     tag: "line",
50056                     id: this.svgID + "-svg-l",
50057                     x1: "0", // start
50058                     y1: (this.height*0.8), // start set the line in 80% of height
50059                     x2: this.width, // end
50060                     y2: (this.height*0.8), // end set the line in 80% of height
50061                     'stroke': "#666",
50062                     'stroke-width': "1",
50063                     'stroke-dasharray': "3",
50064                     'shape-rendering': "crispEdges",
50065                     'pointer-events': "none"
50066                 },
50067                 {
50068                     tag: "path",
50069                     id: this.svgID + "-svg-p",
50070                     'stroke': "navy",
50071                     'stroke-width': "3",
50072                     'fill': "none",
50073                     'pointer-events': 'none'
50074                 }
50075               ]
50076         });
50077         this.createSVG();
50078         this.svgBox = this.svgEl.dom.getScreenCTM();
50079     },
50080     createSVG : function(){ 
50081         var svg = this.signPanel;
50082         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
50083         var t = this;
50084
50085         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
50086         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
50087         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
50088         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
50089         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
50090         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
50091         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
50092         
50093     },
50094     isTouchEvent : function(e){
50095         return e.type.match(/^touch/);
50096     },
50097     getCoords : function (e) {
50098         var pt    = this.svgEl.dom.createSVGPoint();
50099         pt.x = e.clientX; 
50100         pt.y = e.clientY;
50101         if (this.isTouchEvent(e)) {
50102             pt.x =  e.targetTouches[0].clientX;
50103             pt.y = e.targetTouches[0].clientY;
50104         }
50105         var a = this.svgEl.dom.getScreenCTM();
50106         var b = a.inverse();
50107         var mx = pt.matrixTransform(b);
50108         return mx.x + ',' + mx.y;
50109     },
50110     //mouse event headler 
50111     down : function (e) {
50112         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
50113         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
50114         
50115         this.isMouseDown = true;
50116         
50117         e.preventDefault();
50118     },
50119     move : function (e) {
50120         if (this.isMouseDown) {
50121             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
50122             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
50123         }
50124         
50125         e.preventDefault();
50126     },
50127     up : function (e) {
50128         this.isMouseDown = false;
50129         var sp = this.signatureTmp.split(' ');
50130         
50131         if(sp.length > 1){
50132             if(!sp[sp.length-2].match(/^L/)){
50133                 sp.pop();
50134                 sp.pop();
50135                 sp.push("");
50136                 this.signatureTmp = sp.join(" ");
50137             }
50138         }
50139         if(this.getValue() != this.signatureTmp){
50140             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50141             this.isConfirmed = false;
50142         }
50143         e.preventDefault();
50144     },
50145     
50146     /**
50147      * Protected method that will not generally be called directly. It
50148      * is called when the editor creates its toolbar. Override this method if you need to
50149      * add custom toolbar buttons.
50150      * @param {HtmlEditor} editor
50151      */
50152     createToolbar : function(editor){
50153          function btn(id, toggle, handler){
50154             var xid = fid + '-'+ id ;
50155             return {
50156                 id : xid,
50157                 cmd : id,
50158                 cls : 'x-btn-icon x-edit-'+id,
50159                 enableToggle:toggle !== false,
50160                 scope: editor, // was editor...
50161                 handler:handler||editor.relayBtnCmd,
50162                 clickEvent:'mousedown',
50163                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
50164                 tabIndex:-1
50165             };
50166         }
50167         
50168         
50169         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
50170         this.tb = tb;
50171         this.tb.add(
50172            {
50173                 cls : ' x-signature-btn x-signature-'+id,
50174                 scope: editor, // was editor...
50175                 handler: this.reset,
50176                 clickEvent:'mousedown',
50177                 text: this.labels.clear
50178             },
50179             {
50180                  xtype : 'Fill',
50181                  xns: Roo.Toolbar
50182             }, 
50183             {
50184                 cls : '  x-signature-btn x-signature-'+id,
50185                 scope: editor, // was editor...
50186                 handler: this.confirmHandler,
50187                 clickEvent:'mousedown',
50188                 text: this.labels.confirm
50189             }
50190         );
50191     
50192     },
50193     //public
50194     /**
50195      * when user is clicked confirm then show this image.....
50196      * 
50197      * @return {String} Image Data URI
50198      */
50199     getImageDataURI : function(){
50200         var svg = this.svgEl.dom.parentNode.innerHTML;
50201         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
50202         return src; 
50203     },
50204     /**
50205      * 
50206      * @return {Boolean} this.isConfirmed
50207      */
50208     getConfirmed : function(){
50209         return this.isConfirmed;
50210     },
50211     /**
50212      * 
50213      * @return {Number} this.width
50214      */
50215     getWidth : function(){
50216         return this.width;
50217     },
50218     /**
50219      * 
50220      * @return {Number} this.height
50221      */
50222     getHeight : function(){
50223         return this.height;
50224     },
50225     // private
50226     getSignature : function(){
50227         return this.signatureTmp;
50228     },
50229     // private
50230     reset : function(){
50231         this.signatureTmp = '';
50232         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50233         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
50234         this.isConfirmed = false;
50235         Roo.form.Signature.superclass.reset.call(this);
50236     },
50237     setSignature : function(s){
50238         this.signatureTmp = s;
50239         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50240         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
50241         this.setValue(s);
50242         this.isConfirmed = false;
50243         Roo.form.Signature.superclass.reset.call(this);
50244     }, 
50245     test : function(){
50246 //        Roo.log(this.signPanel.dom.contentWindow.up())
50247     },
50248     //private
50249     setConfirmed : function(){
50250         
50251         
50252         
50253 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
50254     },
50255     // private
50256     confirmHandler : function(){
50257         if(!this.getSignature()){
50258             return;
50259         }
50260         
50261         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
50262         this.setValue(this.getSignature());
50263         this.isConfirmed = true;
50264         
50265         this.fireEvent('confirm', this);
50266     },
50267     // private
50268     // Subclasses should provide the validation implementation by overriding this
50269     validateValue : function(value){
50270         if(this.allowBlank){
50271             return true;
50272         }
50273         
50274         if(this.isConfirmed){
50275             return true;
50276         }
50277         return false;
50278     }
50279 });/*
50280  * Based on:
50281  * Ext JS Library 1.1.1
50282  * Copyright(c) 2006-2007, Ext JS, LLC.
50283  *
50284  * Originally Released Under LGPL - original licence link has changed is not relivant.
50285  *
50286  * Fork - LGPL
50287  * <script type="text/javascript">
50288  */
50289  
50290
50291 /**
50292  * @class Roo.form.ComboBox
50293  * @extends Roo.form.TriggerField
50294  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
50295  * @constructor
50296  * Create a new ComboBox.
50297  * @param {Object} config Configuration options
50298  */
50299 Roo.form.Select = function(config){
50300     Roo.form.Select.superclass.constructor.call(this, config);
50301      
50302 };
50303
50304 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
50305     /**
50306      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
50307      */
50308     /**
50309      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
50310      * rendering into an Roo.Editor, defaults to false)
50311      */
50312     /**
50313      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
50314      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
50315      */
50316     /**
50317      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
50318      */
50319     /**
50320      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
50321      * the dropdown list (defaults to undefined, with no header element)
50322      */
50323
50324      /**
50325      * @cfg {String/Roo.Template} tpl The template to use to render the output
50326      */
50327      
50328     // private
50329     defaultAutoCreate : {tag: "select"  },
50330     /**
50331      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
50332      */
50333     listWidth: undefined,
50334     /**
50335      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
50336      * mode = 'remote' or 'text' if mode = 'local')
50337      */
50338     displayField: undefined,
50339     /**
50340      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
50341      * mode = 'remote' or 'value' if mode = 'local'). 
50342      * Note: use of a valueField requires the user make a selection
50343      * in order for a value to be mapped.
50344      */
50345     valueField: undefined,
50346     
50347     
50348     /**
50349      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
50350      * field's data value (defaults to the underlying DOM element's name)
50351      */
50352     hiddenName: undefined,
50353     /**
50354      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
50355      */
50356     listClass: '',
50357     /**
50358      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
50359      */
50360     selectedClass: 'x-combo-selected',
50361     /**
50362      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
50363      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
50364      * which displays a downward arrow icon).
50365      */
50366     triggerClass : 'x-form-arrow-trigger',
50367     /**
50368      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
50369      */
50370     shadow:'sides',
50371     /**
50372      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
50373      * anchor positions (defaults to 'tl-bl')
50374      */
50375     listAlign: 'tl-bl?',
50376     /**
50377      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
50378      */
50379     maxHeight: 300,
50380     /**
50381      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
50382      * query specified by the allQuery config option (defaults to 'query')
50383      */
50384     triggerAction: 'query',
50385     /**
50386      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
50387      * (defaults to 4, does not apply if editable = false)
50388      */
50389     minChars : 4,
50390     /**
50391      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
50392      * delay (typeAheadDelay) if it matches a known value (defaults to false)
50393      */
50394     typeAhead: false,
50395     /**
50396      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
50397      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
50398      */
50399     queryDelay: 500,
50400     /**
50401      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
50402      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
50403      */
50404     pageSize: 0,
50405     /**
50406      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
50407      * when editable = true (defaults to false)
50408      */
50409     selectOnFocus:false,
50410     /**
50411      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
50412      */
50413     queryParam: 'query',
50414     /**
50415      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
50416      * when mode = 'remote' (defaults to 'Loading...')
50417      */
50418     loadingText: 'Loading...',
50419     /**
50420      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
50421      */
50422     resizable: false,
50423     /**
50424      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
50425      */
50426     handleHeight : 8,
50427     /**
50428      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
50429      * traditional select (defaults to true)
50430      */
50431     editable: true,
50432     /**
50433      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
50434      */
50435     allQuery: '',
50436     /**
50437      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
50438      */
50439     mode: 'remote',
50440     /**
50441      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
50442      * listWidth has a higher value)
50443      */
50444     minListWidth : 70,
50445     /**
50446      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
50447      * allow the user to set arbitrary text into the field (defaults to false)
50448      */
50449     forceSelection:false,
50450     /**
50451      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
50452      * if typeAhead = true (defaults to 250)
50453      */
50454     typeAheadDelay : 250,
50455     /**
50456      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
50457      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
50458      */
50459     valueNotFoundText : undefined,
50460     
50461     /**
50462      * @cfg {String} defaultValue The value displayed after loading the store.
50463      */
50464     defaultValue: '',
50465     
50466     /**
50467      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
50468      */
50469     blockFocus : false,
50470     
50471     /**
50472      * @cfg {Boolean} disableClear Disable showing of clear button.
50473      */
50474     disableClear : false,
50475     /**
50476      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
50477      */
50478     alwaysQuery : false,
50479     
50480     //private
50481     addicon : false,
50482     editicon: false,
50483     
50484     // element that contains real text value.. (when hidden is used..)
50485      
50486     // private
50487     onRender : function(ct, position){
50488         Roo.form.Field.prototype.onRender.call(this, ct, position);
50489         
50490         if(this.store){
50491             this.store.on('beforeload', this.onBeforeLoad, this);
50492             this.store.on('load', this.onLoad, this);
50493             this.store.on('loadexception', this.onLoadException, this);
50494             this.store.load({});
50495         }
50496         
50497         
50498         
50499     },
50500
50501     // private
50502     initEvents : function(){
50503         //Roo.form.ComboBox.superclass.initEvents.call(this);
50504  
50505     },
50506
50507     onDestroy : function(){
50508        
50509         if(this.store){
50510             this.store.un('beforeload', this.onBeforeLoad, this);
50511             this.store.un('load', this.onLoad, this);
50512             this.store.un('loadexception', this.onLoadException, this);
50513         }
50514         //Roo.form.ComboBox.superclass.onDestroy.call(this);
50515     },
50516
50517     // private
50518     fireKey : function(e){
50519         if(e.isNavKeyPress() && !this.list.isVisible()){
50520             this.fireEvent("specialkey", this, e);
50521         }
50522     },
50523
50524     // private
50525     onResize: function(w, h){
50526         
50527         return; 
50528     
50529         
50530     },
50531
50532     /**
50533      * Allow or prevent the user from directly editing the field text.  If false is passed,
50534      * the user will only be able to select from the items defined in the dropdown list.  This method
50535      * is the runtime equivalent of setting the 'editable' config option at config time.
50536      * @param {Boolean} value True to allow the user to directly edit the field text
50537      */
50538     setEditable : function(value){
50539          
50540     },
50541
50542     // private
50543     onBeforeLoad : function(){
50544         
50545         Roo.log("Select before load");
50546         return;
50547     
50548         this.innerList.update(this.loadingText ?
50549                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
50550         //this.restrictHeight();
50551         this.selectedIndex = -1;
50552     },
50553
50554     // private
50555     onLoad : function(){
50556
50557     
50558         var dom = this.el.dom;
50559         dom.innerHTML = '';
50560          var od = dom.ownerDocument;
50561          
50562         if (this.emptyText) {
50563             var op = od.createElement('option');
50564             op.setAttribute('value', '');
50565             op.innerHTML = String.format('{0}', this.emptyText);
50566             dom.appendChild(op);
50567         }
50568         if(this.store.getCount() > 0){
50569            
50570             var vf = this.valueField;
50571             var df = this.displayField;
50572             this.store.data.each(function(r) {
50573                 // which colmsn to use... testing - cdoe / title..
50574                 var op = od.createElement('option');
50575                 op.setAttribute('value', r.data[vf]);
50576                 op.innerHTML = String.format('{0}', r.data[df]);
50577                 dom.appendChild(op);
50578             });
50579             if (typeof(this.defaultValue != 'undefined')) {
50580                 this.setValue(this.defaultValue);
50581             }
50582             
50583              
50584         }else{
50585             //this.onEmptyResults();
50586         }
50587         //this.el.focus();
50588     },
50589     // private
50590     onLoadException : function()
50591     {
50592         dom.innerHTML = '';
50593             
50594         Roo.log("Select on load exception");
50595         return;
50596     
50597         this.collapse();
50598         Roo.log(this.store.reader.jsonData);
50599         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
50600             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
50601         }
50602         
50603         
50604     },
50605     // private
50606     onTypeAhead : function(){
50607          
50608     },
50609
50610     // private
50611     onSelect : function(record, index){
50612         Roo.log('on select?');
50613         return;
50614         if(this.fireEvent('beforeselect', this, record, index) !== false){
50615             this.setFromData(index > -1 ? record.data : false);
50616             this.collapse();
50617             this.fireEvent('select', this, record, index);
50618         }
50619     },
50620
50621     /**
50622      * Returns the currently selected field value or empty string if no value is set.
50623      * @return {String} value The selected value
50624      */
50625     getValue : function(){
50626         var dom = this.el.dom;
50627         this.value = dom.options[dom.selectedIndex].value;
50628         return this.value;
50629         
50630     },
50631
50632     /**
50633      * Clears any text/value currently set in the field
50634      */
50635     clearValue : function(){
50636         this.value = '';
50637         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
50638         
50639     },
50640
50641     /**
50642      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
50643      * will be displayed in the field.  If the value does not match the data value of an existing item,
50644      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
50645      * Otherwise the field will be blank (although the value will still be set).
50646      * @param {String} value The value to match
50647      */
50648     setValue : function(v){
50649         var d = this.el.dom;
50650         for (var i =0; i < d.options.length;i++) {
50651             if (v == d.options[i].value) {
50652                 d.selectedIndex = i;
50653                 this.value = v;
50654                 return;
50655             }
50656         }
50657         this.clearValue();
50658     },
50659     /**
50660      * @property {Object} the last set data for the element
50661      */
50662     
50663     lastData : false,
50664     /**
50665      * Sets the value of the field based on a object which is related to the record format for the store.
50666      * @param {Object} value the value to set as. or false on reset?
50667      */
50668     setFromData : function(o){
50669         Roo.log('setfrom data?');
50670          
50671         
50672         
50673     },
50674     // private
50675     reset : function(){
50676         this.clearValue();
50677     },
50678     // private
50679     findRecord : function(prop, value){
50680         
50681         return false;
50682     
50683         var record;
50684         if(this.store.getCount() > 0){
50685             this.store.each(function(r){
50686                 if(r.data[prop] == value){
50687                     record = r;
50688                     return false;
50689                 }
50690                 return true;
50691             });
50692         }
50693         return record;
50694     },
50695     
50696     getName: function()
50697     {
50698         // returns hidden if it's set..
50699         if (!this.rendered) {return ''};
50700         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
50701         
50702     },
50703      
50704
50705     
50706
50707     // private
50708     onEmptyResults : function(){
50709         Roo.log('empty results');
50710         //this.collapse();
50711     },
50712
50713     /**
50714      * Returns true if the dropdown list is expanded, else false.
50715      */
50716     isExpanded : function(){
50717         return false;
50718     },
50719
50720     /**
50721      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
50722      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
50723      * @param {String} value The data value of the item to select
50724      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
50725      * selected item if it is not currently in view (defaults to true)
50726      * @return {Boolean} True if the value matched an item in the list, else false
50727      */
50728     selectByValue : function(v, scrollIntoView){
50729         Roo.log('select By Value');
50730         return false;
50731     
50732         if(v !== undefined && v !== null){
50733             var r = this.findRecord(this.valueField || this.displayField, v);
50734             if(r){
50735                 this.select(this.store.indexOf(r), scrollIntoView);
50736                 return true;
50737             }
50738         }
50739         return false;
50740     },
50741
50742     /**
50743      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
50744      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
50745      * @param {Number} index The zero-based index of the list item to select
50746      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
50747      * selected item if it is not currently in view (defaults to true)
50748      */
50749     select : function(index, scrollIntoView){
50750         Roo.log('select ');
50751         return  ;
50752         
50753         this.selectedIndex = index;
50754         this.view.select(index);
50755         if(scrollIntoView !== false){
50756             var el = this.view.getNode(index);
50757             if(el){
50758                 this.innerList.scrollChildIntoView(el, false);
50759             }
50760         }
50761     },
50762
50763       
50764
50765     // private
50766     validateBlur : function(){
50767         
50768         return;
50769         
50770     },
50771
50772     // private
50773     initQuery : function(){
50774         this.doQuery(this.getRawValue());
50775     },
50776
50777     // private
50778     doForce : function(){
50779         if(this.el.dom.value.length > 0){
50780             this.el.dom.value =
50781                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
50782              
50783         }
50784     },
50785
50786     /**
50787      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
50788      * query allowing the query action to be canceled if needed.
50789      * @param {String} query The SQL query to execute
50790      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
50791      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
50792      * saved in the current store (defaults to false)
50793      */
50794     doQuery : function(q, forceAll){
50795         
50796         Roo.log('doQuery?');
50797         if(q === undefined || q === null){
50798             q = '';
50799         }
50800         var qe = {
50801             query: q,
50802             forceAll: forceAll,
50803             combo: this,
50804             cancel:false
50805         };
50806         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
50807             return false;
50808         }
50809         q = qe.query;
50810         forceAll = qe.forceAll;
50811         if(forceAll === true || (q.length >= this.minChars)){
50812             if(this.lastQuery != q || this.alwaysQuery){
50813                 this.lastQuery = q;
50814                 if(this.mode == 'local'){
50815                     this.selectedIndex = -1;
50816                     if(forceAll){
50817                         this.store.clearFilter();
50818                     }else{
50819                         this.store.filter(this.displayField, q);
50820                     }
50821                     this.onLoad();
50822                 }else{
50823                     this.store.baseParams[this.queryParam] = q;
50824                     this.store.load({
50825                         params: this.getParams(q)
50826                     });
50827                     this.expand();
50828                 }
50829             }else{
50830                 this.selectedIndex = -1;
50831                 this.onLoad();   
50832             }
50833         }
50834     },
50835
50836     // private
50837     getParams : function(q){
50838         var p = {};
50839         //p[this.queryParam] = q;
50840         if(this.pageSize){
50841             p.start = 0;
50842             p.limit = this.pageSize;
50843         }
50844         return p;
50845     },
50846
50847     /**
50848      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
50849      */
50850     collapse : function(){
50851         
50852     },
50853
50854     // private
50855     collapseIf : function(e){
50856         
50857     },
50858
50859     /**
50860      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
50861      */
50862     expand : function(){
50863         
50864     } ,
50865
50866     // private
50867      
50868
50869     /** 
50870     * @cfg {Boolean} grow 
50871     * @hide 
50872     */
50873     /** 
50874     * @cfg {Number} growMin 
50875     * @hide 
50876     */
50877     /** 
50878     * @cfg {Number} growMax 
50879     * @hide 
50880     */
50881     /**
50882      * @hide
50883      * @method autoSize
50884      */
50885     
50886     setWidth : function()
50887     {
50888         
50889     },
50890     getResizeEl : function(){
50891         return this.el;
50892     }
50893 });//<script type="text/javasscript">
50894  
50895
50896 /**
50897  * @class Roo.DDView
50898  * A DnD enabled version of Roo.View.
50899  * @param {Element/String} container The Element in which to create the View.
50900  * @param {String} tpl The template string used to create the markup for each element of the View
50901  * @param {Object} config The configuration properties. These include all the config options of
50902  * {@link Roo.View} plus some specific to this class.<br>
50903  * <p>
50904  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
50905  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
50906  * <p>
50907  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
50908 .x-view-drag-insert-above {
50909         border-top:1px dotted #3366cc;
50910 }
50911 .x-view-drag-insert-below {
50912         border-bottom:1px dotted #3366cc;
50913 }
50914 </code></pre>
50915  * 
50916  */
50917  
50918 Roo.DDView = function(container, tpl, config) {
50919     Roo.DDView.superclass.constructor.apply(this, arguments);
50920     this.getEl().setStyle("outline", "0px none");
50921     this.getEl().unselectable();
50922     if (this.dragGroup) {
50923                 this.setDraggable(this.dragGroup.split(","));
50924     }
50925     if (this.dropGroup) {
50926                 this.setDroppable(this.dropGroup.split(","));
50927     }
50928     if (this.deletable) {
50929         this.setDeletable();
50930     }
50931     this.isDirtyFlag = false;
50932         this.addEvents({
50933                 "drop" : true
50934         });
50935 };
50936
50937 Roo.extend(Roo.DDView, Roo.View, {
50938 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
50939 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
50940 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
50941 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
50942
50943         isFormField: true,
50944
50945         reset: Roo.emptyFn,
50946         
50947         clearInvalid: Roo.form.Field.prototype.clearInvalid,
50948
50949         validate: function() {
50950                 return true;
50951         },
50952         
50953         destroy: function() {
50954                 this.purgeListeners();
50955                 this.getEl.removeAllListeners();
50956                 this.getEl().remove();
50957                 if (this.dragZone) {
50958                         if (this.dragZone.destroy) {
50959                                 this.dragZone.destroy();
50960                         }
50961                 }
50962                 if (this.dropZone) {
50963                         if (this.dropZone.destroy) {
50964                                 this.dropZone.destroy();
50965                         }
50966                 }
50967         },
50968
50969 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
50970         getName: function() {
50971                 return this.name;
50972         },
50973
50974 /**     Loads the View from a JSON string representing the Records to put into the Store. */
50975         setValue: function(v) {
50976                 if (!this.store) {
50977                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
50978                 }
50979                 var data = {};
50980                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
50981                 this.store.proxy = new Roo.data.MemoryProxy(data);
50982                 this.store.load();
50983         },
50984
50985 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
50986         getValue: function() {
50987                 var result = '(';
50988                 this.store.each(function(rec) {
50989                         result += rec.id + ',';
50990                 });
50991                 return result.substr(0, result.length - 1) + ')';
50992         },
50993         
50994         getIds: function() {
50995                 var i = 0, result = new Array(this.store.getCount());
50996                 this.store.each(function(rec) {
50997                         result[i++] = rec.id;
50998                 });
50999                 return result;
51000         },
51001         
51002         isDirty: function() {
51003                 return this.isDirtyFlag;
51004         },
51005
51006 /**
51007  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
51008  *      whole Element becomes the target, and this causes the drop gesture to append.
51009  */
51010     getTargetFromEvent : function(e) {
51011                 var target = e.getTarget();
51012                 while ((target !== null) && (target.parentNode != this.el.dom)) {
51013                 target = target.parentNode;
51014                 }
51015                 if (!target) {
51016                         target = this.el.dom.lastChild || this.el.dom;
51017                 }
51018                 return target;
51019     },
51020
51021 /**
51022  *      Create the drag data which consists of an object which has the property "ddel" as
51023  *      the drag proxy element. 
51024  */
51025     getDragData : function(e) {
51026         var target = this.findItemFromChild(e.getTarget());
51027                 if(target) {
51028                         this.handleSelection(e);
51029                         var selNodes = this.getSelectedNodes();
51030             var dragData = {
51031                 source: this,
51032                 copy: this.copy || (this.allowCopy && e.ctrlKey),
51033                 nodes: selNodes,
51034                 records: []
51035                         };
51036                         var selectedIndices = this.getSelectedIndexes();
51037                         for (var i = 0; i < selectedIndices.length; i++) {
51038                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
51039                         }
51040                         if (selNodes.length == 1) {
51041                                 dragData.ddel = target.cloneNode(true); // the div element
51042                         } else {
51043                                 var div = document.createElement('div'); // create the multi element drag "ghost"
51044                                 div.className = 'multi-proxy';
51045                                 for (var i = 0, len = selNodes.length; i < len; i++) {
51046                                         div.appendChild(selNodes[i].cloneNode(true));
51047                                 }
51048                                 dragData.ddel = div;
51049                         }
51050             //console.log(dragData)
51051             //console.log(dragData.ddel.innerHTML)
51052                         return dragData;
51053                 }
51054         //console.log('nodragData')
51055                 return false;
51056     },
51057     
51058 /**     Specify to which ddGroup items in this DDView may be dragged. */
51059     setDraggable: function(ddGroup) {
51060         if (ddGroup instanceof Array) {
51061                 Roo.each(ddGroup, this.setDraggable, this);
51062                 return;
51063         }
51064         if (this.dragZone) {
51065                 this.dragZone.addToGroup(ddGroup);
51066         } else {
51067                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
51068                                 containerScroll: true,
51069                                 ddGroup: ddGroup 
51070
51071                         });
51072 //                      Draggability implies selection. DragZone's mousedown selects the element.
51073                         if (!this.multiSelect) { this.singleSelect = true; }
51074
51075 //                      Wire the DragZone's handlers up to methods in *this*
51076                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
51077                 }
51078     },
51079
51080 /**     Specify from which ddGroup this DDView accepts drops. */
51081     setDroppable: function(ddGroup) {
51082         if (ddGroup instanceof Array) {
51083                 Roo.each(ddGroup, this.setDroppable, this);
51084                 return;
51085         }
51086         if (this.dropZone) {
51087                 this.dropZone.addToGroup(ddGroup);
51088         } else {
51089                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
51090                                 containerScroll: true,
51091                                 ddGroup: ddGroup
51092                         });
51093
51094 //                      Wire the DropZone's handlers up to methods in *this*
51095                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
51096                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
51097                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
51098                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
51099                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
51100                 }
51101     },
51102
51103 /**     Decide whether to drop above or below a View node. */
51104     getDropPoint : function(e, n, dd){
51105         if (n == this.el.dom) { return "above"; }
51106                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
51107                 var c = t + (b - t) / 2;
51108                 var y = Roo.lib.Event.getPageY(e);
51109                 if(y <= c) {
51110                         return "above";
51111                 }else{
51112                         return "below";
51113                 }
51114     },
51115
51116     onNodeEnter : function(n, dd, e, data){
51117                 return false;
51118     },
51119     
51120     onNodeOver : function(n, dd, e, data){
51121                 var pt = this.getDropPoint(e, n, dd);
51122                 // set the insert point style on the target node
51123                 var dragElClass = this.dropNotAllowed;
51124                 if (pt) {
51125                         var targetElClass;
51126                         if (pt == "above"){
51127                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
51128                                 targetElClass = "x-view-drag-insert-above";
51129                         } else {
51130                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
51131                                 targetElClass = "x-view-drag-insert-below";
51132                         }
51133                         if (this.lastInsertClass != targetElClass){
51134                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
51135                                 this.lastInsertClass = targetElClass;
51136                         }
51137                 }
51138                 return dragElClass;
51139         },
51140
51141     onNodeOut : function(n, dd, e, data){
51142                 this.removeDropIndicators(n);
51143     },
51144
51145     onNodeDrop : function(n, dd, e, data){
51146         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
51147                 return false;
51148         }
51149         var pt = this.getDropPoint(e, n, dd);
51150                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
51151                 if (pt == "below") { insertAt++; }
51152                 for (var i = 0; i < data.records.length; i++) {
51153                         var r = data.records[i];
51154                         var dup = this.store.getById(r.id);
51155                         if (dup && (dd != this.dragZone)) {
51156                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
51157                         } else {
51158                                 if (data.copy) {
51159                                         this.store.insert(insertAt++, r.copy());
51160                                 } else {
51161                                         data.source.isDirtyFlag = true;
51162                                         r.store.remove(r);
51163                                         this.store.insert(insertAt++, r);
51164                                 }
51165                                 this.isDirtyFlag = true;
51166                         }
51167                 }
51168                 this.dragZone.cachedTarget = null;
51169                 return true;
51170     },
51171
51172     removeDropIndicators : function(n){
51173                 if(n){
51174                         Roo.fly(n).removeClass([
51175                                 "x-view-drag-insert-above",
51176                                 "x-view-drag-insert-below"]);
51177                         this.lastInsertClass = "_noclass";
51178                 }
51179     },
51180
51181 /**
51182  *      Utility method. Add a delete option to the DDView's context menu.
51183  *      @param {String} imageUrl The URL of the "delete" icon image.
51184  */
51185         setDeletable: function(imageUrl) {
51186                 if (!this.singleSelect && !this.multiSelect) {
51187                         this.singleSelect = true;
51188                 }
51189                 var c = this.getContextMenu();
51190                 this.contextMenu.on("itemclick", function(item) {
51191                         switch (item.id) {
51192                                 case "delete":
51193                                         this.remove(this.getSelectedIndexes());
51194                                         break;
51195                         }
51196                 }, this);
51197                 this.contextMenu.add({
51198                         icon: imageUrl,
51199                         id: "delete",
51200                         text: 'Delete'
51201                 });
51202         },
51203         
51204 /**     Return the context menu for this DDView. */
51205         getContextMenu: function() {
51206                 if (!this.contextMenu) {
51207 //                      Create the View's context menu
51208                         this.contextMenu = new Roo.menu.Menu({
51209                                 id: this.id + "-contextmenu"
51210                         });
51211                         this.el.on("contextmenu", this.showContextMenu, this);
51212                 }
51213                 return this.contextMenu;
51214         },
51215         
51216         disableContextMenu: function() {
51217                 if (this.contextMenu) {
51218                         this.el.un("contextmenu", this.showContextMenu, this);
51219                 }
51220         },
51221
51222         showContextMenu: function(e, item) {
51223         item = this.findItemFromChild(e.getTarget());
51224                 if (item) {
51225                         e.stopEvent();
51226                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
51227                         this.contextMenu.showAt(e.getXY());
51228             }
51229     },
51230
51231 /**
51232  *      Remove {@link Roo.data.Record}s at the specified indices.
51233  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
51234  */
51235     remove: function(selectedIndices) {
51236                 selectedIndices = [].concat(selectedIndices);
51237                 for (var i = 0; i < selectedIndices.length; i++) {
51238                         var rec = this.store.getAt(selectedIndices[i]);
51239                         this.store.remove(rec);
51240                 }
51241     },
51242
51243 /**
51244  *      Double click fires the event, but also, if this is draggable, and there is only one other
51245  *      related DropZone, it transfers the selected node.
51246  */
51247     onDblClick : function(e){
51248         var item = this.findItemFromChild(e.getTarget());
51249         if(item){
51250             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
51251                 return false;
51252             }
51253             if (this.dragGroup) {
51254                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
51255                     while (targets.indexOf(this.dropZone) > -1) {
51256                             targets.remove(this.dropZone);
51257                                 }
51258                     if (targets.length == 1) {
51259                                         this.dragZone.cachedTarget = null;
51260                         var el = Roo.get(targets[0].getEl());
51261                         var box = el.getBox(true);
51262                         targets[0].onNodeDrop(el.dom, {
51263                                 target: el.dom,
51264                                 xy: [box.x, box.y + box.height - 1]
51265                         }, null, this.getDragData(e));
51266                     }
51267                 }
51268         }
51269     },
51270     
51271     handleSelection: function(e) {
51272                 this.dragZone.cachedTarget = null;
51273         var item = this.findItemFromChild(e.getTarget());
51274         if (!item) {
51275                 this.clearSelections(true);
51276                 return;
51277         }
51278                 if (item && (this.multiSelect || this.singleSelect)){
51279                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
51280                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
51281                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
51282                                 this.unselect(item);
51283                         } else {
51284                                 this.select(item, this.multiSelect && e.ctrlKey);
51285                                 this.lastSelection = item;
51286                         }
51287                 }
51288     },
51289
51290     onItemClick : function(item, index, e){
51291                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
51292                         return false;
51293                 }
51294                 return true;
51295     },
51296
51297     unselect : function(nodeInfo, suppressEvent){
51298                 var node = this.getNode(nodeInfo);
51299                 if(node && this.isSelected(node)){
51300                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
51301                                 Roo.fly(node).removeClass(this.selectedClass);
51302                                 this.selections.remove(node);
51303                                 if(!suppressEvent){
51304                                         this.fireEvent("selectionchange", this, this.selections);
51305                                 }
51306                         }
51307                 }
51308     }
51309 });
51310 /*
51311  * Based on:
51312  * Ext JS Library 1.1.1
51313  * Copyright(c) 2006-2007, Ext JS, LLC.
51314  *
51315  * Originally Released Under LGPL - original licence link has changed is not relivant.
51316  *
51317  * Fork - LGPL
51318  * <script type="text/javascript">
51319  */
51320  
51321 /**
51322  * @class Roo.LayoutManager
51323  * @extends Roo.util.Observable
51324  * Base class for layout managers.
51325  */
51326 Roo.LayoutManager = function(container, config){
51327     Roo.LayoutManager.superclass.constructor.call(this);
51328     this.el = Roo.get(container);
51329     // ie scrollbar fix
51330     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
51331         document.body.scroll = "no";
51332     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
51333         this.el.position('relative');
51334     }
51335     this.id = this.el.id;
51336     this.el.addClass("x-layout-container");
51337     /** false to disable window resize monitoring @type Boolean */
51338     this.monitorWindowResize = true;
51339     this.regions = {};
51340     this.addEvents({
51341         /**
51342          * @event layout
51343          * Fires when a layout is performed. 
51344          * @param {Roo.LayoutManager} this
51345          */
51346         "layout" : true,
51347         /**
51348          * @event regionresized
51349          * Fires when the user resizes a region. 
51350          * @param {Roo.LayoutRegion} region The resized region
51351          * @param {Number} newSize The new size (width for east/west, height for north/south)
51352          */
51353         "regionresized" : true,
51354         /**
51355          * @event regioncollapsed
51356          * Fires when a region is collapsed. 
51357          * @param {Roo.LayoutRegion} region The collapsed region
51358          */
51359         "regioncollapsed" : true,
51360         /**
51361          * @event regionexpanded
51362          * Fires when a region is expanded.  
51363          * @param {Roo.LayoutRegion} region The expanded region
51364          */
51365         "regionexpanded" : true
51366     });
51367     this.updating = false;
51368     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
51369 };
51370
51371 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
51372     /**
51373      * Returns true if this layout is currently being updated
51374      * @return {Boolean}
51375      */
51376     isUpdating : function(){
51377         return this.updating; 
51378     },
51379     
51380     /**
51381      * Suspend the LayoutManager from doing auto-layouts while
51382      * making multiple add or remove calls
51383      */
51384     beginUpdate : function(){
51385         this.updating = true;    
51386     },
51387     
51388     /**
51389      * Restore auto-layouts and optionally disable the manager from performing a layout
51390      * @param {Boolean} noLayout true to disable a layout update 
51391      */
51392     endUpdate : function(noLayout){
51393         this.updating = false;
51394         if(!noLayout){
51395             this.layout();
51396         }    
51397     },
51398     
51399     layout: function(){
51400         
51401     },
51402     
51403     onRegionResized : function(region, newSize){
51404         this.fireEvent("regionresized", region, newSize);
51405         this.layout();
51406     },
51407     
51408     onRegionCollapsed : function(region){
51409         this.fireEvent("regioncollapsed", region);
51410     },
51411     
51412     onRegionExpanded : function(region){
51413         this.fireEvent("regionexpanded", region);
51414     },
51415         
51416     /**
51417      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
51418      * performs box-model adjustments.
51419      * @return {Object} The size as an object {width: (the width), height: (the height)}
51420      */
51421     getViewSize : function(){
51422         var size;
51423         if(this.el.dom != document.body){
51424             size = this.el.getSize();
51425         }else{
51426             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
51427         }
51428         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
51429         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
51430         return size;
51431     },
51432     
51433     /**
51434      * Returns the Element this layout is bound to.
51435      * @return {Roo.Element}
51436      */
51437     getEl : function(){
51438         return this.el;
51439     },
51440     
51441     /**
51442      * Returns the specified region.
51443      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
51444      * @return {Roo.LayoutRegion}
51445      */
51446     getRegion : function(target){
51447         return this.regions[target.toLowerCase()];
51448     },
51449     
51450     onWindowResize : function(){
51451         if(this.monitorWindowResize){
51452             this.layout();
51453         }
51454     }
51455 });/*
51456  * Based on:
51457  * Ext JS Library 1.1.1
51458  * Copyright(c) 2006-2007, Ext JS, LLC.
51459  *
51460  * Originally Released Under LGPL - original licence link has changed is not relivant.
51461  *
51462  * Fork - LGPL
51463  * <script type="text/javascript">
51464  */
51465 /**
51466  * @class Roo.BorderLayout
51467  * @extends Roo.LayoutManager
51468  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
51469  * please see: <br><br>
51470  * <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>
51471  * <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>
51472  * Example:
51473  <pre><code>
51474  var layout = new Roo.BorderLayout(document.body, {
51475     north: {
51476         initialSize: 25,
51477         titlebar: false
51478     },
51479     west: {
51480         split:true,
51481         initialSize: 200,
51482         minSize: 175,
51483         maxSize: 400,
51484         titlebar: true,
51485         collapsible: true
51486     },
51487     east: {
51488         split:true,
51489         initialSize: 202,
51490         minSize: 175,
51491         maxSize: 400,
51492         titlebar: true,
51493         collapsible: true
51494     },
51495     south: {
51496         split:true,
51497         initialSize: 100,
51498         minSize: 100,
51499         maxSize: 200,
51500         titlebar: true,
51501         collapsible: true
51502     },
51503     center: {
51504         titlebar: true,
51505         autoScroll:true,
51506         resizeTabs: true,
51507         minTabWidth: 50,
51508         preferredTabWidth: 150
51509     }
51510 });
51511
51512 // shorthand
51513 var CP = Roo.ContentPanel;
51514
51515 layout.beginUpdate();
51516 layout.add("north", new CP("north", "North"));
51517 layout.add("south", new CP("south", {title: "South", closable: true}));
51518 layout.add("west", new CP("west", {title: "West"}));
51519 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
51520 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
51521 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
51522 layout.getRegion("center").showPanel("center1");
51523 layout.endUpdate();
51524 </code></pre>
51525
51526 <b>The container the layout is rendered into can be either the body element or any other element.
51527 If it is not the body element, the container needs to either be an absolute positioned element,
51528 or you will need to add "position:relative" to the css of the container.  You will also need to specify
51529 the container size if it is not the body element.</b>
51530
51531 * @constructor
51532 * Create a new BorderLayout
51533 * @param {String/HTMLElement/Element} container The container this layout is bound to
51534 * @param {Object} config Configuration options
51535  */
51536 Roo.BorderLayout = function(container, config){
51537     config = config || {};
51538     Roo.BorderLayout.superclass.constructor.call(this, container, config);
51539     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
51540     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
51541         var target = this.factory.validRegions[i];
51542         if(config[target]){
51543             this.addRegion(target, config[target]);
51544         }
51545     }
51546 };
51547
51548 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
51549     /**
51550      * Creates and adds a new region if it doesn't already exist.
51551      * @param {String} target The target region key (north, south, east, west or center).
51552      * @param {Object} config The regions config object
51553      * @return {BorderLayoutRegion} The new region
51554      */
51555     addRegion : function(target, config){
51556         if(!this.regions[target]){
51557             var r = this.factory.create(target, this, config);
51558             this.bindRegion(target, r);
51559         }
51560         return this.regions[target];
51561     },
51562
51563     // private (kinda)
51564     bindRegion : function(name, r){
51565         this.regions[name] = r;
51566         r.on("visibilitychange", this.layout, this);
51567         r.on("paneladded", this.layout, this);
51568         r.on("panelremoved", this.layout, this);
51569         r.on("invalidated", this.layout, this);
51570         r.on("resized", this.onRegionResized, this);
51571         r.on("collapsed", this.onRegionCollapsed, this);
51572         r.on("expanded", this.onRegionExpanded, this);
51573     },
51574
51575     /**
51576      * Performs a layout update.
51577      */
51578     layout : function(){
51579         if(this.updating) {
51580             return;
51581         }
51582         var size = this.getViewSize();
51583         var w = size.width;
51584         var h = size.height;
51585         var centerW = w;
51586         var centerH = h;
51587         var centerY = 0;
51588         var centerX = 0;
51589         //var x = 0, y = 0;
51590
51591         var rs = this.regions;
51592         var north = rs["north"];
51593         var south = rs["south"]; 
51594         var west = rs["west"];
51595         var east = rs["east"];
51596         var center = rs["center"];
51597         //if(this.hideOnLayout){ // not supported anymore
51598             //c.el.setStyle("display", "none");
51599         //}
51600         if(north && north.isVisible()){
51601             var b = north.getBox();
51602             var m = north.getMargins();
51603             b.width = w - (m.left+m.right);
51604             b.x = m.left;
51605             b.y = m.top;
51606             centerY = b.height + b.y + m.bottom;
51607             centerH -= centerY;
51608             north.updateBox(this.safeBox(b));
51609         }
51610         if(south && south.isVisible()){
51611             var b = south.getBox();
51612             var m = south.getMargins();
51613             b.width = w - (m.left+m.right);
51614             b.x = m.left;
51615             var totalHeight = (b.height + m.top + m.bottom);
51616             b.y = h - totalHeight + m.top;
51617             centerH -= totalHeight;
51618             south.updateBox(this.safeBox(b));
51619         }
51620         if(west && west.isVisible()){
51621             var b = west.getBox();
51622             var m = west.getMargins();
51623             b.height = centerH - (m.top+m.bottom);
51624             b.x = m.left;
51625             b.y = centerY + m.top;
51626             var totalWidth = (b.width + m.left + m.right);
51627             centerX += totalWidth;
51628             centerW -= totalWidth;
51629             west.updateBox(this.safeBox(b));
51630         }
51631         if(east && east.isVisible()){
51632             var b = east.getBox();
51633             var m = east.getMargins();
51634             b.height = centerH - (m.top+m.bottom);
51635             var totalWidth = (b.width + m.left + m.right);
51636             b.x = w - totalWidth + m.left;
51637             b.y = centerY + m.top;
51638             centerW -= totalWidth;
51639             east.updateBox(this.safeBox(b));
51640         }
51641         if(center){
51642             var m = center.getMargins();
51643             var centerBox = {
51644                 x: centerX + m.left,
51645                 y: centerY + m.top,
51646                 width: centerW - (m.left+m.right),
51647                 height: centerH - (m.top+m.bottom)
51648             };
51649             //if(this.hideOnLayout){
51650                 //center.el.setStyle("display", "block");
51651             //}
51652             center.updateBox(this.safeBox(centerBox));
51653         }
51654         this.el.repaint();
51655         this.fireEvent("layout", this);
51656     },
51657
51658     // private
51659     safeBox : function(box){
51660         box.width = Math.max(0, box.width);
51661         box.height = Math.max(0, box.height);
51662         return box;
51663     },
51664
51665     /**
51666      * Adds a ContentPanel (or subclass) to this layout.
51667      * @param {String} target The target region key (north, south, east, west or center).
51668      * @param {Roo.ContentPanel} panel The panel to add
51669      * @return {Roo.ContentPanel} The added panel
51670      */
51671     add : function(target, panel){
51672          
51673         target = target.toLowerCase();
51674         return this.regions[target].add(panel);
51675     },
51676
51677     /**
51678      * Remove a ContentPanel (or subclass) to this layout.
51679      * @param {String} target The target region key (north, south, east, west or center).
51680      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
51681      * @return {Roo.ContentPanel} The removed panel
51682      */
51683     remove : function(target, panel){
51684         target = target.toLowerCase();
51685         return this.regions[target].remove(panel);
51686     },
51687
51688     /**
51689      * Searches all regions for a panel with the specified id
51690      * @param {String} panelId
51691      * @return {Roo.ContentPanel} The panel or null if it wasn't found
51692      */
51693     findPanel : function(panelId){
51694         var rs = this.regions;
51695         for(var target in rs){
51696             if(typeof rs[target] != "function"){
51697                 var p = rs[target].getPanel(panelId);
51698                 if(p){
51699                     return p;
51700                 }
51701             }
51702         }
51703         return null;
51704     },
51705
51706     /**
51707      * Searches all regions for a panel with the specified id and activates (shows) it.
51708      * @param {String/ContentPanel} panelId The panels id or the panel itself
51709      * @return {Roo.ContentPanel} The shown panel or null
51710      */
51711     showPanel : function(panelId) {
51712       var rs = this.regions;
51713       for(var target in rs){
51714          var r = rs[target];
51715          if(typeof r != "function"){
51716             if(r.hasPanel(panelId)){
51717                return r.showPanel(panelId);
51718             }
51719          }
51720       }
51721       return null;
51722    },
51723
51724    /**
51725      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
51726      * @param {Roo.state.Provider} provider (optional) An alternate state provider
51727      */
51728     restoreState : function(provider){
51729         if(!provider){
51730             provider = Roo.state.Manager;
51731         }
51732         var sm = new Roo.LayoutStateManager();
51733         sm.init(this, provider);
51734     },
51735
51736     /**
51737      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
51738      * object should contain properties for each region to add ContentPanels to, and each property's value should be
51739      * a valid ContentPanel config object.  Example:
51740      * <pre><code>
51741 // Create the main layout
51742 var layout = new Roo.BorderLayout('main-ct', {
51743     west: {
51744         split:true,
51745         minSize: 175,
51746         titlebar: true
51747     },
51748     center: {
51749         title:'Components'
51750     }
51751 }, 'main-ct');
51752
51753 // Create and add multiple ContentPanels at once via configs
51754 layout.batchAdd({
51755    west: {
51756        id: 'source-files',
51757        autoCreate:true,
51758        title:'Ext Source Files',
51759        autoScroll:true,
51760        fitToFrame:true
51761    },
51762    center : {
51763        el: cview,
51764        autoScroll:true,
51765        fitToFrame:true,
51766        toolbar: tb,
51767        resizeEl:'cbody'
51768    }
51769 });
51770 </code></pre>
51771      * @param {Object} regions An object containing ContentPanel configs by region name
51772      */
51773     batchAdd : function(regions){
51774         this.beginUpdate();
51775         for(var rname in regions){
51776             var lr = this.regions[rname];
51777             if(lr){
51778                 this.addTypedPanels(lr, regions[rname]);
51779             }
51780         }
51781         this.endUpdate();
51782     },
51783
51784     // private
51785     addTypedPanels : function(lr, ps){
51786         if(typeof ps == 'string'){
51787             lr.add(new Roo.ContentPanel(ps));
51788         }
51789         else if(ps instanceof Array){
51790             for(var i =0, len = ps.length; i < len; i++){
51791                 this.addTypedPanels(lr, ps[i]);
51792             }
51793         }
51794         else if(!ps.events){ // raw config?
51795             var el = ps.el;
51796             delete ps.el; // prevent conflict
51797             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
51798         }
51799         else {  // panel object assumed!
51800             lr.add(ps);
51801         }
51802     },
51803     /**
51804      * Adds a xtype elements to the layout.
51805      * <pre><code>
51806
51807 layout.addxtype({
51808        xtype : 'ContentPanel',
51809        region: 'west',
51810        items: [ .... ]
51811    }
51812 );
51813
51814 layout.addxtype({
51815         xtype : 'NestedLayoutPanel',
51816         region: 'west',
51817         layout: {
51818            center: { },
51819            west: { }   
51820         },
51821         items : [ ... list of content panels or nested layout panels.. ]
51822    }
51823 );
51824 </code></pre>
51825      * @param {Object} cfg Xtype definition of item to add.
51826      */
51827     addxtype : function(cfg)
51828     {
51829         // basically accepts a pannel...
51830         // can accept a layout region..!?!?
51831         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
51832         
51833         if (!cfg.xtype.match(/Panel$/)) {
51834             return false;
51835         }
51836         var ret = false;
51837         
51838         if (typeof(cfg.region) == 'undefined') {
51839             Roo.log("Failed to add Panel, region was not set");
51840             Roo.log(cfg);
51841             return false;
51842         }
51843         var region = cfg.region;
51844         delete cfg.region;
51845         
51846           
51847         var xitems = [];
51848         if (cfg.items) {
51849             xitems = cfg.items;
51850             delete cfg.items;
51851         }
51852         var nb = false;
51853         
51854         switch(cfg.xtype) 
51855         {
51856             case 'ContentPanel':  // ContentPanel (el, cfg)
51857             case 'ScrollPanel':  // ContentPanel (el, cfg)
51858             case 'ViewPanel': 
51859                 if(cfg.autoCreate) {
51860                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51861                 } else {
51862                     var el = this.el.createChild();
51863                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
51864                 }
51865                 
51866                 this.add(region, ret);
51867                 break;
51868             
51869             
51870             case 'TreePanel': // our new panel!
51871                 cfg.el = this.el.createChild();
51872                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51873                 this.add(region, ret);
51874                 break;
51875             
51876             case 'NestedLayoutPanel': 
51877                 // create a new Layout (which is  a Border Layout...
51878                 var el = this.el.createChild();
51879                 var clayout = cfg.layout;
51880                 delete cfg.layout;
51881                 clayout.items   = clayout.items  || [];
51882                 // replace this exitems with the clayout ones..
51883                 xitems = clayout.items;
51884                  
51885                 
51886                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
51887                     cfg.background = false;
51888                 }
51889                 var layout = new Roo.BorderLayout(el, clayout);
51890                 
51891                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
51892                 //console.log('adding nested layout panel '  + cfg.toSource());
51893                 this.add(region, ret);
51894                 nb = {}; /// find first...
51895                 break;
51896                 
51897             case 'GridPanel': 
51898             
51899                 // needs grid and region
51900                 
51901                 //var el = this.getRegion(region).el.createChild();
51902                 var el = this.el.createChild();
51903                 // create the grid first...
51904                 
51905                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
51906                 delete cfg.grid;
51907                 if (region == 'center' && this.active ) {
51908                     cfg.background = false;
51909                 }
51910                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
51911                 
51912                 this.add(region, ret);
51913                 if (cfg.background) {
51914                     ret.on('activate', function(gp) {
51915                         if (!gp.grid.rendered) {
51916                             gp.grid.render();
51917                         }
51918                     });
51919                 } else {
51920                     grid.render();
51921                 }
51922                 break;
51923            
51924            
51925            
51926                 
51927                 
51928                 
51929             default:
51930                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
51931                     
51932                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51933                     this.add(region, ret);
51934                 } else {
51935                 
51936                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
51937                     return null;
51938                 }
51939                 
51940              // GridPanel (grid, cfg)
51941             
51942         }
51943         this.beginUpdate();
51944         // add children..
51945         var region = '';
51946         var abn = {};
51947         Roo.each(xitems, function(i)  {
51948             region = nb && i.region ? i.region : false;
51949             
51950             var add = ret.addxtype(i);
51951            
51952             if (region) {
51953                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
51954                 if (!i.background) {
51955                     abn[region] = nb[region] ;
51956                 }
51957             }
51958             
51959         });
51960         this.endUpdate();
51961
51962         // make the last non-background panel active..
51963         //if (nb) { Roo.log(abn); }
51964         if (nb) {
51965             
51966             for(var r in abn) {
51967                 region = this.getRegion(r);
51968                 if (region) {
51969                     // tried using nb[r], but it does not work..
51970                      
51971                     region.showPanel(abn[r]);
51972                    
51973                 }
51974             }
51975         }
51976         return ret;
51977         
51978     }
51979 });
51980
51981 /**
51982  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
51983  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
51984  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
51985  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
51986  * <pre><code>
51987 // shorthand
51988 var CP = Roo.ContentPanel;
51989
51990 var layout = Roo.BorderLayout.create({
51991     north: {
51992         initialSize: 25,
51993         titlebar: false,
51994         panels: [new CP("north", "North")]
51995     },
51996     west: {
51997         split:true,
51998         initialSize: 200,
51999         minSize: 175,
52000         maxSize: 400,
52001         titlebar: true,
52002         collapsible: true,
52003         panels: [new CP("west", {title: "West"})]
52004     },
52005     east: {
52006         split:true,
52007         initialSize: 202,
52008         minSize: 175,
52009         maxSize: 400,
52010         titlebar: true,
52011         collapsible: true,
52012         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
52013     },
52014     south: {
52015         split:true,
52016         initialSize: 100,
52017         minSize: 100,
52018         maxSize: 200,
52019         titlebar: true,
52020         collapsible: true,
52021         panels: [new CP("south", {title: "South", closable: true})]
52022     },
52023     center: {
52024         titlebar: true,
52025         autoScroll:true,
52026         resizeTabs: true,
52027         minTabWidth: 50,
52028         preferredTabWidth: 150,
52029         panels: [
52030             new CP("center1", {title: "Close Me", closable: true}),
52031             new CP("center2", {title: "Center Panel", closable: false})
52032         ]
52033     }
52034 }, document.body);
52035
52036 layout.getRegion("center").showPanel("center1");
52037 </code></pre>
52038  * @param config
52039  * @param targetEl
52040  */
52041 Roo.BorderLayout.create = function(config, targetEl){
52042     var layout = new Roo.BorderLayout(targetEl || document.body, config);
52043     layout.beginUpdate();
52044     var regions = Roo.BorderLayout.RegionFactory.validRegions;
52045     for(var j = 0, jlen = regions.length; j < jlen; j++){
52046         var lr = regions[j];
52047         if(layout.regions[lr] && config[lr].panels){
52048             var r = layout.regions[lr];
52049             var ps = config[lr].panels;
52050             layout.addTypedPanels(r, ps);
52051         }
52052     }
52053     layout.endUpdate();
52054     return layout;
52055 };
52056
52057 // private
52058 Roo.BorderLayout.RegionFactory = {
52059     // private
52060     validRegions : ["north","south","east","west","center"],
52061
52062     // private
52063     create : function(target, mgr, config){
52064         target = target.toLowerCase();
52065         if(config.lightweight || config.basic){
52066             return new Roo.BasicLayoutRegion(mgr, config, target);
52067         }
52068         switch(target){
52069             case "north":
52070                 return new Roo.NorthLayoutRegion(mgr, config);
52071             case "south":
52072                 return new Roo.SouthLayoutRegion(mgr, config);
52073             case "east":
52074                 return new Roo.EastLayoutRegion(mgr, config);
52075             case "west":
52076                 return new Roo.WestLayoutRegion(mgr, config);
52077             case "center":
52078                 return new Roo.CenterLayoutRegion(mgr, config);
52079         }
52080         throw 'Layout region "'+target+'" not supported.';
52081     }
52082 };/*
52083  * Based on:
52084  * Ext JS Library 1.1.1
52085  * Copyright(c) 2006-2007, Ext JS, LLC.
52086  *
52087  * Originally Released Under LGPL - original licence link has changed is not relivant.
52088  *
52089  * Fork - LGPL
52090  * <script type="text/javascript">
52091  */
52092  
52093 /**
52094  * @class Roo.BasicLayoutRegion
52095  * @extends Roo.util.Observable
52096  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
52097  * and does not have a titlebar, tabs or any other features. All it does is size and position 
52098  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
52099  */
52100 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
52101     this.mgr = mgr;
52102     this.position  = pos;
52103     this.events = {
52104         /**
52105          * @scope Roo.BasicLayoutRegion
52106          */
52107         
52108         /**
52109          * @event beforeremove
52110          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
52111          * @param {Roo.LayoutRegion} this
52112          * @param {Roo.ContentPanel} panel The panel
52113          * @param {Object} e The cancel event object
52114          */
52115         "beforeremove" : true,
52116         /**
52117          * @event invalidated
52118          * Fires when the layout for this region is changed.
52119          * @param {Roo.LayoutRegion} this
52120          */
52121         "invalidated" : true,
52122         /**
52123          * @event visibilitychange
52124          * Fires when this region is shown or hidden 
52125          * @param {Roo.LayoutRegion} this
52126          * @param {Boolean} visibility true or false
52127          */
52128         "visibilitychange" : true,
52129         /**
52130          * @event paneladded
52131          * Fires when a panel is added. 
52132          * @param {Roo.LayoutRegion} this
52133          * @param {Roo.ContentPanel} panel The panel
52134          */
52135         "paneladded" : true,
52136         /**
52137          * @event panelremoved
52138          * Fires when a panel is removed. 
52139          * @param {Roo.LayoutRegion} this
52140          * @param {Roo.ContentPanel} panel The panel
52141          */
52142         "panelremoved" : true,
52143         /**
52144          * @event beforecollapse
52145          * Fires when this region before collapse.
52146          * @param {Roo.LayoutRegion} this
52147          */
52148         "beforecollapse" : true,
52149         /**
52150          * @event collapsed
52151          * Fires when this region is collapsed.
52152          * @param {Roo.LayoutRegion} this
52153          */
52154         "collapsed" : true,
52155         /**
52156          * @event expanded
52157          * Fires when this region is expanded.
52158          * @param {Roo.LayoutRegion} this
52159          */
52160         "expanded" : true,
52161         /**
52162          * @event slideshow
52163          * Fires when this region is slid into view.
52164          * @param {Roo.LayoutRegion} this
52165          */
52166         "slideshow" : true,
52167         /**
52168          * @event slidehide
52169          * Fires when this region slides out of view. 
52170          * @param {Roo.LayoutRegion} this
52171          */
52172         "slidehide" : true,
52173         /**
52174          * @event panelactivated
52175          * Fires when a panel is activated. 
52176          * @param {Roo.LayoutRegion} this
52177          * @param {Roo.ContentPanel} panel The activated panel
52178          */
52179         "panelactivated" : true,
52180         /**
52181          * @event resized
52182          * Fires when the user resizes this region. 
52183          * @param {Roo.LayoutRegion} this
52184          * @param {Number} newSize The new size (width for east/west, height for north/south)
52185          */
52186         "resized" : true
52187     };
52188     /** A collection of panels in this region. @type Roo.util.MixedCollection */
52189     this.panels = new Roo.util.MixedCollection();
52190     this.panels.getKey = this.getPanelId.createDelegate(this);
52191     this.box = null;
52192     this.activePanel = null;
52193     // ensure listeners are added...
52194     
52195     if (config.listeners || config.events) {
52196         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
52197             listeners : config.listeners || {},
52198             events : config.events || {}
52199         });
52200     }
52201     
52202     if(skipConfig !== true){
52203         this.applyConfig(config);
52204     }
52205 };
52206
52207 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
52208     getPanelId : function(p){
52209         return p.getId();
52210     },
52211     
52212     applyConfig : function(config){
52213         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
52214         this.config = config;
52215         
52216     },
52217     
52218     /**
52219      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
52220      * the width, for horizontal (north, south) the height.
52221      * @param {Number} newSize The new width or height
52222      */
52223     resizeTo : function(newSize){
52224         var el = this.el ? this.el :
52225                  (this.activePanel ? this.activePanel.getEl() : null);
52226         if(el){
52227             switch(this.position){
52228                 case "east":
52229                 case "west":
52230                     el.setWidth(newSize);
52231                     this.fireEvent("resized", this, newSize);
52232                 break;
52233                 case "north":
52234                 case "south":
52235                     el.setHeight(newSize);
52236                     this.fireEvent("resized", this, newSize);
52237                 break;                
52238             }
52239         }
52240     },
52241     
52242     getBox : function(){
52243         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
52244     },
52245     
52246     getMargins : function(){
52247         return this.margins;
52248     },
52249     
52250     updateBox : function(box){
52251         this.box = box;
52252         var el = this.activePanel.getEl();
52253         el.dom.style.left = box.x + "px";
52254         el.dom.style.top = box.y + "px";
52255         this.activePanel.setSize(box.width, box.height);
52256     },
52257     
52258     /**
52259      * Returns the container element for this region.
52260      * @return {Roo.Element}
52261      */
52262     getEl : function(){
52263         return this.activePanel;
52264     },
52265     
52266     /**
52267      * Returns true if this region is currently visible.
52268      * @return {Boolean}
52269      */
52270     isVisible : function(){
52271         return this.activePanel ? true : false;
52272     },
52273     
52274     setActivePanel : function(panel){
52275         panel = this.getPanel(panel);
52276         if(this.activePanel && this.activePanel != panel){
52277             this.activePanel.setActiveState(false);
52278             this.activePanel.getEl().setLeftTop(-10000,-10000);
52279         }
52280         this.activePanel = panel;
52281         panel.setActiveState(true);
52282         if(this.box){
52283             panel.setSize(this.box.width, this.box.height);
52284         }
52285         this.fireEvent("panelactivated", this, panel);
52286         this.fireEvent("invalidated");
52287     },
52288     
52289     /**
52290      * Show the specified panel.
52291      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
52292      * @return {Roo.ContentPanel} The shown panel or null
52293      */
52294     showPanel : function(panel){
52295         if(panel = this.getPanel(panel)){
52296             this.setActivePanel(panel);
52297         }
52298         return panel;
52299     },
52300     
52301     /**
52302      * Get the active panel for this region.
52303      * @return {Roo.ContentPanel} The active panel or null
52304      */
52305     getActivePanel : function(){
52306         return this.activePanel;
52307     },
52308     
52309     /**
52310      * Add the passed ContentPanel(s)
52311      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
52312      * @return {Roo.ContentPanel} The panel added (if only one was added)
52313      */
52314     add : function(panel){
52315         if(arguments.length > 1){
52316             for(var i = 0, len = arguments.length; i < len; i++) {
52317                 this.add(arguments[i]);
52318             }
52319             return null;
52320         }
52321         if(this.hasPanel(panel)){
52322             this.showPanel(panel);
52323             return panel;
52324         }
52325         var el = panel.getEl();
52326         if(el.dom.parentNode != this.mgr.el.dom){
52327             this.mgr.el.dom.appendChild(el.dom);
52328         }
52329         if(panel.setRegion){
52330             panel.setRegion(this);
52331         }
52332         this.panels.add(panel);
52333         el.setStyle("position", "absolute");
52334         if(!panel.background){
52335             this.setActivePanel(panel);
52336             if(this.config.initialSize && this.panels.getCount()==1){
52337                 this.resizeTo(this.config.initialSize);
52338             }
52339         }
52340         this.fireEvent("paneladded", this, panel);
52341         return panel;
52342     },
52343     
52344     /**
52345      * Returns true if the panel is in this region.
52346      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
52347      * @return {Boolean}
52348      */
52349     hasPanel : function(panel){
52350         if(typeof panel == "object"){ // must be panel obj
52351             panel = panel.getId();
52352         }
52353         return this.getPanel(panel) ? true : false;
52354     },
52355     
52356     /**
52357      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
52358      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
52359      * @param {Boolean} preservePanel Overrides the config preservePanel option
52360      * @return {Roo.ContentPanel} The panel that was removed
52361      */
52362     remove : function(panel, preservePanel){
52363         panel = this.getPanel(panel);
52364         if(!panel){
52365             return null;
52366         }
52367         var e = {};
52368         this.fireEvent("beforeremove", this, panel, e);
52369         if(e.cancel === true){
52370             return null;
52371         }
52372         var panelId = panel.getId();
52373         this.panels.removeKey(panelId);
52374         return panel;
52375     },
52376     
52377     /**
52378      * Returns the panel specified or null if it's not in this region.
52379      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
52380      * @return {Roo.ContentPanel}
52381      */
52382     getPanel : function(id){
52383         if(typeof id == "object"){ // must be panel obj
52384             return id;
52385         }
52386         return this.panels.get(id);
52387     },
52388     
52389     /**
52390      * Returns this regions position (north/south/east/west/center).
52391      * @return {String} 
52392      */
52393     getPosition: function(){
52394         return this.position;    
52395     }
52396 });/*
52397  * Based on:
52398  * Ext JS Library 1.1.1
52399  * Copyright(c) 2006-2007, Ext JS, LLC.
52400  *
52401  * Originally Released Under LGPL - original licence link has changed is not relivant.
52402  *
52403  * Fork - LGPL
52404  * <script type="text/javascript">
52405  */
52406  
52407 /**
52408  * @class Roo.LayoutRegion
52409  * @extends Roo.BasicLayoutRegion
52410  * This class represents a region in a layout manager.
52411  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
52412  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
52413  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
52414  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
52415  * @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})
52416  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
52417  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
52418  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
52419  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
52420  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
52421  * @cfg {String}    title           The title for the region (overrides panel titles)
52422  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
52423  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
52424  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
52425  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
52426  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
52427  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
52428  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
52429  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
52430  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
52431  * @cfg {Boolean}   showPin         True to show a pin button
52432  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
52433  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
52434  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
52435  * @cfg {Number}    width           For East/West panels
52436  * @cfg {Number}    height          For North/South panels
52437  * @cfg {Boolean}   split           To show the splitter
52438  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
52439  */
52440 Roo.LayoutRegion = function(mgr, config, pos){
52441     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
52442     var dh = Roo.DomHelper;
52443     /** This region's container element 
52444     * @type Roo.Element */
52445     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
52446     /** This region's title element 
52447     * @type Roo.Element */
52448
52449     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
52450         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
52451         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
52452     ]}, true);
52453     this.titleEl.enableDisplayMode();
52454     /** This region's title text element 
52455     * @type HTMLElement */
52456     this.titleTextEl = this.titleEl.dom.firstChild;
52457     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
52458     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
52459     this.closeBtn.enableDisplayMode();
52460     this.closeBtn.on("click", this.closeClicked, this);
52461     this.closeBtn.hide();
52462
52463     this.createBody(config);
52464     this.visible = true;
52465     this.collapsed = false;
52466
52467     if(config.hideWhenEmpty){
52468         this.hide();
52469         this.on("paneladded", this.validateVisibility, this);
52470         this.on("panelremoved", this.validateVisibility, this);
52471     }
52472     this.applyConfig(config);
52473 };
52474
52475 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
52476
52477     createBody : function(){
52478         /** This region's body element 
52479         * @type Roo.Element */
52480         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
52481     },
52482
52483     applyConfig : function(c){
52484         if(c.collapsible && this.position != "center" && !this.collapsedEl){
52485             var dh = Roo.DomHelper;
52486             if(c.titlebar !== false){
52487                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
52488                 this.collapseBtn.on("click", this.collapse, this);
52489                 this.collapseBtn.enableDisplayMode();
52490
52491                 if(c.showPin === true || this.showPin){
52492                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
52493                     this.stickBtn.enableDisplayMode();
52494                     this.stickBtn.on("click", this.expand, this);
52495                     this.stickBtn.hide();
52496                 }
52497             }
52498             /** This region's collapsed element
52499             * @type Roo.Element */
52500             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
52501                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
52502             ]}, true);
52503             if(c.floatable !== false){
52504                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
52505                this.collapsedEl.on("click", this.collapseClick, this);
52506             }
52507
52508             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
52509                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
52510                    id: "message", unselectable: "on", style:{"float":"left"}});
52511                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
52512              }
52513             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
52514             this.expandBtn.on("click", this.expand, this);
52515         }
52516         if(this.collapseBtn){
52517             this.collapseBtn.setVisible(c.collapsible == true);
52518         }
52519         this.cmargins = c.cmargins || this.cmargins ||
52520                          (this.position == "west" || this.position == "east" ?
52521                              {top: 0, left: 2, right:2, bottom: 0} :
52522                              {top: 2, left: 0, right:0, bottom: 2});
52523         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
52524         this.bottomTabs = c.tabPosition != "top";
52525         this.autoScroll = c.autoScroll || false;
52526         if(this.autoScroll){
52527             this.bodyEl.setStyle("overflow", "auto");
52528         }else{
52529             this.bodyEl.setStyle("overflow", "hidden");
52530         }
52531         //if(c.titlebar !== false){
52532             if((!c.titlebar && !c.title) || c.titlebar === false){
52533                 this.titleEl.hide();
52534             }else{
52535                 this.titleEl.show();
52536                 if(c.title){
52537                     this.titleTextEl.innerHTML = c.title;
52538                 }
52539             }
52540         //}
52541         this.duration = c.duration || .30;
52542         this.slideDuration = c.slideDuration || .45;
52543         this.config = c;
52544         if(c.collapsed){
52545             this.collapse(true);
52546         }
52547         if(c.hidden){
52548             this.hide();
52549         }
52550     },
52551     /**
52552      * Returns true if this region is currently visible.
52553      * @return {Boolean}
52554      */
52555     isVisible : function(){
52556         return this.visible;
52557     },
52558
52559     /**
52560      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
52561      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
52562      */
52563     setCollapsedTitle : function(title){
52564         title = title || "&#160;";
52565         if(this.collapsedTitleTextEl){
52566             this.collapsedTitleTextEl.innerHTML = title;
52567         }
52568     },
52569
52570     getBox : function(){
52571         var b;
52572         if(!this.collapsed){
52573             b = this.el.getBox(false, true);
52574         }else{
52575             b = this.collapsedEl.getBox(false, true);
52576         }
52577         return b;
52578     },
52579
52580     getMargins : function(){
52581         return this.collapsed ? this.cmargins : this.margins;
52582     },
52583
52584     highlight : function(){
52585         this.el.addClass("x-layout-panel-dragover");
52586     },
52587
52588     unhighlight : function(){
52589         this.el.removeClass("x-layout-panel-dragover");
52590     },
52591
52592     updateBox : function(box){
52593         this.box = box;
52594         if(!this.collapsed){
52595             this.el.dom.style.left = box.x + "px";
52596             this.el.dom.style.top = box.y + "px";
52597             this.updateBody(box.width, box.height);
52598         }else{
52599             this.collapsedEl.dom.style.left = box.x + "px";
52600             this.collapsedEl.dom.style.top = box.y + "px";
52601             this.collapsedEl.setSize(box.width, box.height);
52602         }
52603         if(this.tabs){
52604             this.tabs.autoSizeTabs();
52605         }
52606     },
52607
52608     updateBody : function(w, h){
52609         if(w !== null){
52610             this.el.setWidth(w);
52611             w -= this.el.getBorderWidth("rl");
52612             if(this.config.adjustments){
52613                 w += this.config.adjustments[0];
52614             }
52615         }
52616         if(h !== null){
52617             this.el.setHeight(h);
52618             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
52619             h -= this.el.getBorderWidth("tb");
52620             if(this.config.adjustments){
52621                 h += this.config.adjustments[1];
52622             }
52623             this.bodyEl.setHeight(h);
52624             if(this.tabs){
52625                 h = this.tabs.syncHeight(h);
52626             }
52627         }
52628         if(this.panelSize){
52629             w = w !== null ? w : this.panelSize.width;
52630             h = h !== null ? h : this.panelSize.height;
52631         }
52632         if(this.activePanel){
52633             var el = this.activePanel.getEl();
52634             w = w !== null ? w : el.getWidth();
52635             h = h !== null ? h : el.getHeight();
52636             this.panelSize = {width: w, height: h};
52637             this.activePanel.setSize(w, h);
52638         }
52639         if(Roo.isIE && this.tabs){
52640             this.tabs.el.repaint();
52641         }
52642     },
52643
52644     /**
52645      * Returns the container element for this region.
52646      * @return {Roo.Element}
52647      */
52648     getEl : function(){
52649         return this.el;
52650     },
52651
52652     /**
52653      * Hides this region.
52654      */
52655     hide : function(){
52656         if(!this.collapsed){
52657             this.el.dom.style.left = "-2000px";
52658             this.el.hide();
52659         }else{
52660             this.collapsedEl.dom.style.left = "-2000px";
52661             this.collapsedEl.hide();
52662         }
52663         this.visible = false;
52664         this.fireEvent("visibilitychange", this, false);
52665     },
52666
52667     /**
52668      * Shows this region if it was previously hidden.
52669      */
52670     show : function(){
52671         if(!this.collapsed){
52672             this.el.show();
52673         }else{
52674             this.collapsedEl.show();
52675         }
52676         this.visible = true;
52677         this.fireEvent("visibilitychange", this, true);
52678     },
52679
52680     closeClicked : function(){
52681         if(this.activePanel){
52682             this.remove(this.activePanel);
52683         }
52684     },
52685
52686     collapseClick : function(e){
52687         if(this.isSlid){
52688            e.stopPropagation();
52689            this.slideIn();
52690         }else{
52691            e.stopPropagation();
52692            this.slideOut();
52693         }
52694     },
52695
52696     /**
52697      * Collapses this region.
52698      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
52699      */
52700     collapse : function(skipAnim, skipCheck){
52701         if(this.collapsed) {
52702             return;
52703         }
52704         
52705         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
52706             
52707             this.collapsed = true;
52708             if(this.split){
52709                 this.split.el.hide();
52710             }
52711             if(this.config.animate && skipAnim !== true){
52712                 this.fireEvent("invalidated", this);
52713                 this.animateCollapse();
52714             }else{
52715                 this.el.setLocation(-20000,-20000);
52716                 this.el.hide();
52717                 this.collapsedEl.show();
52718                 this.fireEvent("collapsed", this);
52719                 this.fireEvent("invalidated", this);
52720             }
52721         }
52722         
52723     },
52724
52725     animateCollapse : function(){
52726         // overridden
52727     },
52728
52729     /**
52730      * Expands this region if it was previously collapsed.
52731      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
52732      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
52733      */
52734     expand : function(e, skipAnim){
52735         if(e) {
52736             e.stopPropagation();
52737         }
52738         if(!this.collapsed || this.el.hasActiveFx()) {
52739             return;
52740         }
52741         if(this.isSlid){
52742             this.afterSlideIn();
52743             skipAnim = true;
52744         }
52745         this.collapsed = false;
52746         if(this.config.animate && skipAnim !== true){
52747             this.animateExpand();
52748         }else{
52749             this.el.show();
52750             if(this.split){
52751                 this.split.el.show();
52752             }
52753             this.collapsedEl.setLocation(-2000,-2000);
52754             this.collapsedEl.hide();
52755             this.fireEvent("invalidated", this);
52756             this.fireEvent("expanded", this);
52757         }
52758     },
52759
52760     animateExpand : function(){
52761         // overridden
52762     },
52763
52764     initTabs : function()
52765     {
52766         this.bodyEl.setStyle("overflow", "hidden");
52767         var ts = new Roo.TabPanel(
52768                 this.bodyEl.dom,
52769                 {
52770                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
52771                     disableTooltips: this.config.disableTabTips,
52772                     toolbar : this.config.toolbar
52773                 }
52774         );
52775         if(this.config.hideTabs){
52776             ts.stripWrap.setDisplayed(false);
52777         }
52778         this.tabs = ts;
52779         ts.resizeTabs = this.config.resizeTabs === true;
52780         ts.minTabWidth = this.config.minTabWidth || 40;
52781         ts.maxTabWidth = this.config.maxTabWidth || 250;
52782         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
52783         ts.monitorResize = false;
52784         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
52785         ts.bodyEl.addClass('x-layout-tabs-body');
52786         this.panels.each(this.initPanelAsTab, this);
52787     },
52788
52789     initPanelAsTab : function(panel){
52790         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
52791                     this.config.closeOnTab && panel.isClosable());
52792         if(panel.tabTip !== undefined){
52793             ti.setTooltip(panel.tabTip);
52794         }
52795         ti.on("activate", function(){
52796               this.setActivePanel(panel);
52797         }, this);
52798         if(this.config.closeOnTab){
52799             ti.on("beforeclose", function(t, e){
52800                 e.cancel = true;
52801                 this.remove(panel);
52802             }, this);
52803         }
52804         return ti;
52805     },
52806
52807     updatePanelTitle : function(panel, title){
52808         if(this.activePanel == panel){
52809             this.updateTitle(title);
52810         }
52811         if(this.tabs){
52812             var ti = this.tabs.getTab(panel.getEl().id);
52813             ti.setText(title);
52814             if(panel.tabTip !== undefined){
52815                 ti.setTooltip(panel.tabTip);
52816             }
52817         }
52818     },
52819
52820     updateTitle : function(title){
52821         if(this.titleTextEl && !this.config.title){
52822             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
52823         }
52824     },
52825
52826     setActivePanel : function(panel){
52827         panel = this.getPanel(panel);
52828         if(this.activePanel && this.activePanel != panel){
52829             this.activePanel.setActiveState(false);
52830         }
52831         this.activePanel = panel;
52832         panel.setActiveState(true);
52833         if(this.panelSize){
52834             panel.setSize(this.panelSize.width, this.panelSize.height);
52835         }
52836         if(this.closeBtn){
52837             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
52838         }
52839         this.updateTitle(panel.getTitle());
52840         if(this.tabs){
52841             this.fireEvent("invalidated", this);
52842         }
52843         this.fireEvent("panelactivated", this, panel);
52844     },
52845
52846     /**
52847      * Shows the specified panel.
52848      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
52849      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
52850      */
52851     showPanel : function(panel)
52852     {
52853         panel = this.getPanel(panel);
52854         if(panel){
52855             if(this.tabs){
52856                 var tab = this.tabs.getTab(panel.getEl().id);
52857                 if(tab.isHidden()){
52858                     this.tabs.unhideTab(tab.id);
52859                 }
52860                 tab.activate();
52861             }else{
52862                 this.setActivePanel(panel);
52863             }
52864         }
52865         return panel;
52866     },
52867
52868     /**
52869      * Get the active panel for this region.
52870      * @return {Roo.ContentPanel} The active panel or null
52871      */
52872     getActivePanel : function(){
52873         return this.activePanel;
52874     },
52875
52876     validateVisibility : function(){
52877         if(this.panels.getCount() < 1){
52878             this.updateTitle("&#160;");
52879             this.closeBtn.hide();
52880             this.hide();
52881         }else{
52882             if(!this.isVisible()){
52883                 this.show();
52884             }
52885         }
52886     },
52887
52888     /**
52889      * Adds the passed ContentPanel(s) to this region.
52890      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
52891      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
52892      */
52893     add : function(panel){
52894         if(arguments.length > 1){
52895             for(var i = 0, len = arguments.length; i < len; i++) {
52896                 this.add(arguments[i]);
52897             }
52898             return null;
52899         }
52900         if(this.hasPanel(panel)){
52901             this.showPanel(panel);
52902             return panel;
52903         }
52904         panel.setRegion(this);
52905         this.panels.add(panel);
52906         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
52907             this.bodyEl.dom.appendChild(panel.getEl().dom);
52908             if(panel.background !== true){
52909                 this.setActivePanel(panel);
52910             }
52911             this.fireEvent("paneladded", this, panel);
52912             return panel;
52913         }
52914         if(!this.tabs){
52915             this.initTabs();
52916         }else{
52917             this.initPanelAsTab(panel);
52918         }
52919         if(panel.background !== true){
52920             this.tabs.activate(panel.getEl().id);
52921         }
52922         this.fireEvent("paneladded", this, panel);
52923         return panel;
52924     },
52925
52926     /**
52927      * Hides the tab for the specified panel.
52928      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52929      */
52930     hidePanel : function(panel){
52931         if(this.tabs && (panel = this.getPanel(panel))){
52932             this.tabs.hideTab(panel.getEl().id);
52933         }
52934     },
52935
52936     /**
52937      * Unhides the tab for a previously hidden panel.
52938      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52939      */
52940     unhidePanel : function(panel){
52941         if(this.tabs && (panel = this.getPanel(panel))){
52942             this.tabs.unhideTab(panel.getEl().id);
52943         }
52944     },
52945
52946     clearPanels : function(){
52947         while(this.panels.getCount() > 0){
52948              this.remove(this.panels.first());
52949         }
52950     },
52951
52952     /**
52953      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
52954      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52955      * @param {Boolean} preservePanel Overrides the config preservePanel option
52956      * @return {Roo.ContentPanel} The panel that was removed
52957      */
52958     remove : function(panel, preservePanel){
52959         panel = this.getPanel(panel);
52960         if(!panel){
52961             return null;
52962         }
52963         var e = {};
52964         this.fireEvent("beforeremove", this, panel, e);
52965         if(e.cancel === true){
52966             return null;
52967         }
52968         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
52969         var panelId = panel.getId();
52970         this.panels.removeKey(panelId);
52971         if(preservePanel){
52972             document.body.appendChild(panel.getEl().dom);
52973         }
52974         if(this.tabs){
52975             this.tabs.removeTab(panel.getEl().id);
52976         }else if (!preservePanel){
52977             this.bodyEl.dom.removeChild(panel.getEl().dom);
52978         }
52979         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
52980             var p = this.panels.first();
52981             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
52982             tempEl.appendChild(p.getEl().dom);
52983             this.bodyEl.update("");
52984             this.bodyEl.dom.appendChild(p.getEl().dom);
52985             tempEl = null;
52986             this.updateTitle(p.getTitle());
52987             this.tabs = null;
52988             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
52989             this.setActivePanel(p);
52990         }
52991         panel.setRegion(null);
52992         if(this.activePanel == panel){
52993             this.activePanel = null;
52994         }
52995         if(this.config.autoDestroy !== false && preservePanel !== true){
52996             try{panel.destroy();}catch(e){}
52997         }
52998         this.fireEvent("panelremoved", this, panel);
52999         return panel;
53000     },
53001
53002     /**
53003      * Returns the TabPanel component used by this region
53004      * @return {Roo.TabPanel}
53005      */
53006     getTabs : function(){
53007         return this.tabs;
53008     },
53009
53010     createTool : function(parentEl, className){
53011         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
53012             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
53013         btn.addClassOnOver("x-layout-tools-button-over");
53014         return btn;
53015     }
53016 });/*
53017  * Based on:
53018  * Ext JS Library 1.1.1
53019  * Copyright(c) 2006-2007, Ext JS, LLC.
53020  *
53021  * Originally Released Under LGPL - original licence link has changed is not relivant.
53022  *
53023  * Fork - LGPL
53024  * <script type="text/javascript">
53025  */
53026  
53027
53028
53029 /**
53030  * @class Roo.SplitLayoutRegion
53031  * @extends Roo.LayoutRegion
53032  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
53033  */
53034 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
53035     this.cursor = cursor;
53036     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
53037 };
53038
53039 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
53040     splitTip : "Drag to resize.",
53041     collapsibleSplitTip : "Drag to resize. Double click to hide.",
53042     useSplitTips : false,
53043
53044     applyConfig : function(config){
53045         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
53046         if(config.split){
53047             if(!this.split){
53048                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
53049                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
53050                 /** The SplitBar for this region 
53051                 * @type Roo.SplitBar */
53052                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
53053                 this.split.on("moved", this.onSplitMove, this);
53054                 this.split.useShim = config.useShim === true;
53055                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
53056                 if(this.useSplitTips){
53057                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
53058                 }
53059                 if(config.collapsible){
53060                     this.split.el.on("dblclick", this.collapse,  this);
53061                 }
53062             }
53063             if(typeof config.minSize != "undefined"){
53064                 this.split.minSize = config.minSize;
53065             }
53066             if(typeof config.maxSize != "undefined"){
53067                 this.split.maxSize = config.maxSize;
53068             }
53069             if(config.hideWhenEmpty || config.hidden || config.collapsed){
53070                 this.hideSplitter();
53071             }
53072         }
53073     },
53074
53075     getHMaxSize : function(){
53076          var cmax = this.config.maxSize || 10000;
53077          var center = this.mgr.getRegion("center");
53078          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
53079     },
53080
53081     getVMaxSize : function(){
53082          var cmax = this.config.maxSize || 10000;
53083          var center = this.mgr.getRegion("center");
53084          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
53085     },
53086
53087     onSplitMove : function(split, newSize){
53088         this.fireEvent("resized", this, newSize);
53089     },
53090     
53091     /** 
53092      * Returns the {@link Roo.SplitBar} for this region.
53093      * @return {Roo.SplitBar}
53094      */
53095     getSplitBar : function(){
53096         return this.split;
53097     },
53098     
53099     hide : function(){
53100         this.hideSplitter();
53101         Roo.SplitLayoutRegion.superclass.hide.call(this);
53102     },
53103
53104     hideSplitter : function(){
53105         if(this.split){
53106             this.split.el.setLocation(-2000,-2000);
53107             this.split.el.hide();
53108         }
53109     },
53110
53111     show : function(){
53112         if(this.split){
53113             this.split.el.show();
53114         }
53115         Roo.SplitLayoutRegion.superclass.show.call(this);
53116     },
53117     
53118     beforeSlide: function(){
53119         if(Roo.isGecko){// firefox overflow auto bug workaround
53120             this.bodyEl.clip();
53121             if(this.tabs) {
53122                 this.tabs.bodyEl.clip();
53123             }
53124             if(this.activePanel){
53125                 this.activePanel.getEl().clip();
53126                 
53127                 if(this.activePanel.beforeSlide){
53128                     this.activePanel.beforeSlide();
53129                 }
53130             }
53131         }
53132     },
53133     
53134     afterSlide : function(){
53135         if(Roo.isGecko){// firefox overflow auto bug workaround
53136             this.bodyEl.unclip();
53137             if(this.tabs) {
53138                 this.tabs.bodyEl.unclip();
53139             }
53140             if(this.activePanel){
53141                 this.activePanel.getEl().unclip();
53142                 if(this.activePanel.afterSlide){
53143                     this.activePanel.afterSlide();
53144                 }
53145             }
53146         }
53147     },
53148
53149     initAutoHide : function(){
53150         if(this.autoHide !== false){
53151             if(!this.autoHideHd){
53152                 var st = new Roo.util.DelayedTask(this.slideIn, this);
53153                 this.autoHideHd = {
53154                     "mouseout": function(e){
53155                         if(!e.within(this.el, true)){
53156                             st.delay(500);
53157                         }
53158                     },
53159                     "mouseover" : function(e){
53160                         st.cancel();
53161                     },
53162                     scope : this
53163                 };
53164             }
53165             this.el.on(this.autoHideHd);
53166         }
53167     },
53168
53169     clearAutoHide : function(){
53170         if(this.autoHide !== false){
53171             this.el.un("mouseout", this.autoHideHd.mouseout);
53172             this.el.un("mouseover", this.autoHideHd.mouseover);
53173         }
53174     },
53175
53176     clearMonitor : function(){
53177         Roo.get(document).un("click", this.slideInIf, this);
53178     },
53179
53180     // these names are backwards but not changed for compat
53181     slideOut : function(){
53182         if(this.isSlid || this.el.hasActiveFx()){
53183             return;
53184         }
53185         this.isSlid = true;
53186         if(this.collapseBtn){
53187             this.collapseBtn.hide();
53188         }
53189         this.closeBtnState = this.closeBtn.getStyle('display');
53190         this.closeBtn.hide();
53191         if(this.stickBtn){
53192             this.stickBtn.show();
53193         }
53194         this.el.show();
53195         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
53196         this.beforeSlide();
53197         this.el.setStyle("z-index", 10001);
53198         this.el.slideIn(this.getSlideAnchor(), {
53199             callback: function(){
53200                 this.afterSlide();
53201                 this.initAutoHide();
53202                 Roo.get(document).on("click", this.slideInIf, this);
53203                 this.fireEvent("slideshow", this);
53204             },
53205             scope: this,
53206             block: true
53207         });
53208     },
53209
53210     afterSlideIn : function(){
53211         this.clearAutoHide();
53212         this.isSlid = false;
53213         this.clearMonitor();
53214         this.el.setStyle("z-index", "");
53215         if(this.collapseBtn){
53216             this.collapseBtn.show();
53217         }
53218         this.closeBtn.setStyle('display', this.closeBtnState);
53219         if(this.stickBtn){
53220             this.stickBtn.hide();
53221         }
53222         this.fireEvent("slidehide", this);
53223     },
53224
53225     slideIn : function(cb){
53226         if(!this.isSlid || this.el.hasActiveFx()){
53227             Roo.callback(cb);
53228             return;
53229         }
53230         this.isSlid = false;
53231         this.beforeSlide();
53232         this.el.slideOut(this.getSlideAnchor(), {
53233             callback: function(){
53234                 this.el.setLeftTop(-10000, -10000);
53235                 this.afterSlide();
53236                 this.afterSlideIn();
53237                 Roo.callback(cb);
53238             },
53239             scope: this,
53240             block: true
53241         });
53242     },
53243     
53244     slideInIf : function(e){
53245         if(!e.within(this.el)){
53246             this.slideIn();
53247         }
53248     },
53249
53250     animateCollapse : function(){
53251         this.beforeSlide();
53252         this.el.setStyle("z-index", 20000);
53253         var anchor = this.getSlideAnchor();
53254         this.el.slideOut(anchor, {
53255             callback : function(){
53256                 this.el.setStyle("z-index", "");
53257                 this.collapsedEl.slideIn(anchor, {duration:.3});
53258                 this.afterSlide();
53259                 this.el.setLocation(-10000,-10000);
53260                 this.el.hide();
53261                 this.fireEvent("collapsed", this);
53262             },
53263             scope: this,
53264             block: true
53265         });
53266     },
53267
53268     animateExpand : function(){
53269         this.beforeSlide();
53270         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
53271         this.el.setStyle("z-index", 20000);
53272         this.collapsedEl.hide({
53273             duration:.1
53274         });
53275         this.el.slideIn(this.getSlideAnchor(), {
53276             callback : function(){
53277                 this.el.setStyle("z-index", "");
53278                 this.afterSlide();
53279                 if(this.split){
53280                     this.split.el.show();
53281                 }
53282                 this.fireEvent("invalidated", this);
53283                 this.fireEvent("expanded", this);
53284             },
53285             scope: this,
53286             block: true
53287         });
53288     },
53289
53290     anchors : {
53291         "west" : "left",
53292         "east" : "right",
53293         "north" : "top",
53294         "south" : "bottom"
53295     },
53296
53297     sanchors : {
53298         "west" : "l",
53299         "east" : "r",
53300         "north" : "t",
53301         "south" : "b"
53302     },
53303
53304     canchors : {
53305         "west" : "tl-tr",
53306         "east" : "tr-tl",
53307         "north" : "tl-bl",
53308         "south" : "bl-tl"
53309     },
53310
53311     getAnchor : function(){
53312         return this.anchors[this.position];
53313     },
53314
53315     getCollapseAnchor : function(){
53316         return this.canchors[this.position];
53317     },
53318
53319     getSlideAnchor : function(){
53320         return this.sanchors[this.position];
53321     },
53322
53323     getAlignAdj : function(){
53324         var cm = this.cmargins;
53325         switch(this.position){
53326             case "west":
53327                 return [0, 0];
53328             break;
53329             case "east":
53330                 return [0, 0];
53331             break;
53332             case "north":
53333                 return [0, 0];
53334             break;
53335             case "south":
53336                 return [0, 0];
53337             break;
53338         }
53339     },
53340
53341     getExpandAdj : function(){
53342         var c = this.collapsedEl, cm = this.cmargins;
53343         switch(this.position){
53344             case "west":
53345                 return [-(cm.right+c.getWidth()+cm.left), 0];
53346             break;
53347             case "east":
53348                 return [cm.right+c.getWidth()+cm.left, 0];
53349             break;
53350             case "north":
53351                 return [0, -(cm.top+cm.bottom+c.getHeight())];
53352             break;
53353             case "south":
53354                 return [0, cm.top+cm.bottom+c.getHeight()];
53355             break;
53356         }
53357     }
53358 });/*
53359  * Based on:
53360  * Ext JS Library 1.1.1
53361  * Copyright(c) 2006-2007, Ext JS, LLC.
53362  *
53363  * Originally Released Under LGPL - original licence link has changed is not relivant.
53364  *
53365  * Fork - LGPL
53366  * <script type="text/javascript">
53367  */
53368 /*
53369  * These classes are private internal classes
53370  */
53371 Roo.CenterLayoutRegion = function(mgr, config){
53372     Roo.LayoutRegion.call(this, mgr, config, "center");
53373     this.visible = true;
53374     this.minWidth = config.minWidth || 20;
53375     this.minHeight = config.minHeight || 20;
53376 };
53377
53378 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
53379     hide : function(){
53380         // center panel can't be hidden
53381     },
53382     
53383     show : function(){
53384         // center panel can't be hidden
53385     },
53386     
53387     getMinWidth: function(){
53388         return this.minWidth;
53389     },
53390     
53391     getMinHeight: function(){
53392         return this.minHeight;
53393     }
53394 });
53395
53396
53397 Roo.NorthLayoutRegion = function(mgr, config){
53398     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
53399     if(this.split){
53400         this.split.placement = Roo.SplitBar.TOP;
53401         this.split.orientation = Roo.SplitBar.VERTICAL;
53402         this.split.el.addClass("x-layout-split-v");
53403     }
53404     var size = config.initialSize || config.height;
53405     if(typeof size != "undefined"){
53406         this.el.setHeight(size);
53407     }
53408 };
53409 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
53410     orientation: Roo.SplitBar.VERTICAL,
53411     getBox : function(){
53412         if(this.collapsed){
53413             return this.collapsedEl.getBox();
53414         }
53415         var box = this.el.getBox();
53416         if(this.split){
53417             box.height += this.split.el.getHeight();
53418         }
53419         return box;
53420     },
53421     
53422     updateBox : function(box){
53423         if(this.split && !this.collapsed){
53424             box.height -= this.split.el.getHeight();
53425             this.split.el.setLeft(box.x);
53426             this.split.el.setTop(box.y+box.height);
53427             this.split.el.setWidth(box.width);
53428         }
53429         if(this.collapsed){
53430             this.updateBody(box.width, null);
53431         }
53432         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53433     }
53434 });
53435
53436 Roo.SouthLayoutRegion = function(mgr, config){
53437     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
53438     if(this.split){
53439         this.split.placement = Roo.SplitBar.BOTTOM;
53440         this.split.orientation = Roo.SplitBar.VERTICAL;
53441         this.split.el.addClass("x-layout-split-v");
53442     }
53443     var size = config.initialSize || config.height;
53444     if(typeof size != "undefined"){
53445         this.el.setHeight(size);
53446     }
53447 };
53448 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
53449     orientation: Roo.SplitBar.VERTICAL,
53450     getBox : function(){
53451         if(this.collapsed){
53452             return this.collapsedEl.getBox();
53453         }
53454         var box = this.el.getBox();
53455         if(this.split){
53456             var sh = this.split.el.getHeight();
53457             box.height += sh;
53458             box.y -= sh;
53459         }
53460         return box;
53461     },
53462     
53463     updateBox : function(box){
53464         if(this.split && !this.collapsed){
53465             var sh = this.split.el.getHeight();
53466             box.height -= sh;
53467             box.y += sh;
53468             this.split.el.setLeft(box.x);
53469             this.split.el.setTop(box.y-sh);
53470             this.split.el.setWidth(box.width);
53471         }
53472         if(this.collapsed){
53473             this.updateBody(box.width, null);
53474         }
53475         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53476     }
53477 });
53478
53479 Roo.EastLayoutRegion = function(mgr, config){
53480     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
53481     if(this.split){
53482         this.split.placement = Roo.SplitBar.RIGHT;
53483         this.split.orientation = Roo.SplitBar.HORIZONTAL;
53484         this.split.el.addClass("x-layout-split-h");
53485     }
53486     var size = config.initialSize || config.width;
53487     if(typeof size != "undefined"){
53488         this.el.setWidth(size);
53489     }
53490 };
53491 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
53492     orientation: Roo.SplitBar.HORIZONTAL,
53493     getBox : function(){
53494         if(this.collapsed){
53495             return this.collapsedEl.getBox();
53496         }
53497         var box = this.el.getBox();
53498         if(this.split){
53499             var sw = this.split.el.getWidth();
53500             box.width += sw;
53501             box.x -= sw;
53502         }
53503         return box;
53504     },
53505
53506     updateBox : function(box){
53507         if(this.split && !this.collapsed){
53508             var sw = this.split.el.getWidth();
53509             box.width -= sw;
53510             this.split.el.setLeft(box.x);
53511             this.split.el.setTop(box.y);
53512             this.split.el.setHeight(box.height);
53513             box.x += sw;
53514         }
53515         if(this.collapsed){
53516             this.updateBody(null, box.height);
53517         }
53518         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53519     }
53520 });
53521
53522 Roo.WestLayoutRegion = function(mgr, config){
53523     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
53524     if(this.split){
53525         this.split.placement = Roo.SplitBar.LEFT;
53526         this.split.orientation = Roo.SplitBar.HORIZONTAL;
53527         this.split.el.addClass("x-layout-split-h");
53528     }
53529     var size = config.initialSize || config.width;
53530     if(typeof size != "undefined"){
53531         this.el.setWidth(size);
53532     }
53533 };
53534 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
53535     orientation: Roo.SplitBar.HORIZONTAL,
53536     getBox : function(){
53537         if(this.collapsed){
53538             return this.collapsedEl.getBox();
53539         }
53540         var box = this.el.getBox();
53541         if(this.split){
53542             box.width += this.split.el.getWidth();
53543         }
53544         return box;
53545     },
53546     
53547     updateBox : function(box){
53548         if(this.split && !this.collapsed){
53549             var sw = this.split.el.getWidth();
53550             box.width -= sw;
53551             this.split.el.setLeft(box.x+box.width);
53552             this.split.el.setTop(box.y);
53553             this.split.el.setHeight(box.height);
53554         }
53555         if(this.collapsed){
53556             this.updateBody(null, box.height);
53557         }
53558         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53559     }
53560 });
53561 /*
53562  * Based on:
53563  * Ext JS Library 1.1.1
53564  * Copyright(c) 2006-2007, Ext JS, LLC.
53565  *
53566  * Originally Released Under LGPL - original licence link has changed is not relivant.
53567  *
53568  * Fork - LGPL
53569  * <script type="text/javascript">
53570  */
53571  
53572  
53573 /*
53574  * Private internal class for reading and applying state
53575  */
53576 Roo.LayoutStateManager = function(layout){
53577      // default empty state
53578      this.state = {
53579         north: {},
53580         south: {},
53581         east: {},
53582         west: {}       
53583     };
53584 };
53585
53586 Roo.LayoutStateManager.prototype = {
53587     init : function(layout, provider){
53588         this.provider = provider;
53589         var state = provider.get(layout.id+"-layout-state");
53590         if(state){
53591             var wasUpdating = layout.isUpdating();
53592             if(!wasUpdating){
53593                 layout.beginUpdate();
53594             }
53595             for(var key in state){
53596                 if(typeof state[key] != "function"){
53597                     var rstate = state[key];
53598                     var r = layout.getRegion(key);
53599                     if(r && rstate){
53600                         if(rstate.size){
53601                             r.resizeTo(rstate.size);
53602                         }
53603                         if(rstate.collapsed == true){
53604                             r.collapse(true);
53605                         }else{
53606                             r.expand(null, true);
53607                         }
53608                     }
53609                 }
53610             }
53611             if(!wasUpdating){
53612                 layout.endUpdate();
53613             }
53614             this.state = state; 
53615         }
53616         this.layout = layout;
53617         layout.on("regionresized", this.onRegionResized, this);
53618         layout.on("regioncollapsed", this.onRegionCollapsed, this);
53619         layout.on("regionexpanded", this.onRegionExpanded, this);
53620     },
53621     
53622     storeState : function(){
53623         this.provider.set(this.layout.id+"-layout-state", this.state);
53624     },
53625     
53626     onRegionResized : function(region, newSize){
53627         this.state[region.getPosition()].size = newSize;
53628         this.storeState();
53629     },
53630     
53631     onRegionCollapsed : function(region){
53632         this.state[region.getPosition()].collapsed = true;
53633         this.storeState();
53634     },
53635     
53636     onRegionExpanded : function(region){
53637         this.state[region.getPosition()].collapsed = false;
53638         this.storeState();
53639     }
53640 };/*
53641  * Based on:
53642  * Ext JS Library 1.1.1
53643  * Copyright(c) 2006-2007, Ext JS, LLC.
53644  *
53645  * Originally Released Under LGPL - original licence link has changed is not relivant.
53646  *
53647  * Fork - LGPL
53648  * <script type="text/javascript">
53649  */
53650 /**
53651  * @class Roo.ContentPanel
53652  * @extends Roo.util.Observable
53653  * A basic ContentPanel element.
53654  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
53655  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
53656  * @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
53657  * @cfg {Boolean}   closable      True if the panel can be closed/removed
53658  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
53659  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
53660  * @cfg {Toolbar}   toolbar       A toolbar for this panel
53661  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
53662  * @cfg {String} title          The title for this panel
53663  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
53664  * @cfg {String} url            Calls {@link #setUrl} with this value
53665  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
53666  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
53667  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
53668  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
53669
53670  * @constructor
53671  * Create a new ContentPanel.
53672  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
53673  * @param {String/Object} config A string to set only the title or a config object
53674  * @param {String} content (optional) Set the HTML content for this panel
53675  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
53676  */
53677 Roo.ContentPanel = function(el, config, content){
53678     
53679      
53680     /*
53681     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
53682         config = el;
53683         el = Roo.id();
53684     }
53685     if (config && config.parentLayout) { 
53686         el = config.parentLayout.el.createChild(); 
53687     }
53688     */
53689     if(el.autoCreate){ // xtype is available if this is called from factory
53690         config = el;
53691         el = Roo.id();
53692     }
53693     this.el = Roo.get(el);
53694     if(!this.el && config && config.autoCreate){
53695         if(typeof config.autoCreate == "object"){
53696             if(!config.autoCreate.id){
53697                 config.autoCreate.id = config.id||el;
53698             }
53699             this.el = Roo.DomHelper.append(document.body,
53700                         config.autoCreate, true);
53701         }else{
53702             this.el = Roo.DomHelper.append(document.body,
53703                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
53704         }
53705     }
53706     this.closable = false;
53707     this.loaded = false;
53708     this.active = false;
53709     if(typeof config == "string"){
53710         this.title = config;
53711     }else{
53712         Roo.apply(this, config);
53713     }
53714     
53715     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
53716         this.wrapEl = this.el.wrap();
53717         this.toolbar.container = this.el.insertSibling(false, 'before');
53718         this.toolbar = new Roo.Toolbar(this.toolbar);
53719     }
53720     
53721     // xtype created footer. - not sure if will work as we normally have to render first..
53722     if (this.footer && !this.footer.el && this.footer.xtype) {
53723         if (!this.wrapEl) {
53724             this.wrapEl = this.el.wrap();
53725         }
53726     
53727         this.footer.container = this.wrapEl.createChild();
53728          
53729         this.footer = Roo.factory(this.footer, Roo);
53730         
53731     }
53732     
53733     if(this.resizeEl){
53734         this.resizeEl = Roo.get(this.resizeEl, true);
53735     }else{
53736         this.resizeEl = this.el;
53737     }
53738     // handle view.xtype
53739     
53740  
53741     
53742     
53743     this.addEvents({
53744         /**
53745          * @event activate
53746          * Fires when this panel is activated. 
53747          * @param {Roo.ContentPanel} this
53748          */
53749         "activate" : true,
53750         /**
53751          * @event deactivate
53752          * Fires when this panel is activated. 
53753          * @param {Roo.ContentPanel} this
53754          */
53755         "deactivate" : true,
53756
53757         /**
53758          * @event resize
53759          * Fires when this panel is resized if fitToFrame is true.
53760          * @param {Roo.ContentPanel} this
53761          * @param {Number} width The width after any component adjustments
53762          * @param {Number} height The height after any component adjustments
53763          */
53764         "resize" : true,
53765         
53766          /**
53767          * @event render
53768          * Fires when this tab is created
53769          * @param {Roo.ContentPanel} this
53770          */
53771         "render" : true
53772          
53773         
53774     });
53775     
53776
53777     
53778     
53779     if(this.autoScroll){
53780         this.resizeEl.setStyle("overflow", "auto");
53781     } else {
53782         // fix randome scrolling
53783         this.el.on('scroll', function() {
53784             Roo.log('fix random scolling');
53785             this.scrollTo('top',0); 
53786         });
53787     }
53788     content = content || this.content;
53789     if(content){
53790         this.setContent(content);
53791     }
53792     if(config && config.url){
53793         this.setUrl(this.url, this.params, this.loadOnce);
53794     }
53795     
53796     
53797     
53798     Roo.ContentPanel.superclass.constructor.call(this);
53799     
53800     if (this.view && typeof(this.view.xtype) != 'undefined') {
53801         this.view.el = this.el.appendChild(document.createElement("div"));
53802         this.view = Roo.factory(this.view); 
53803         this.view.render  &&  this.view.render(false, '');  
53804     }
53805     
53806     
53807     this.fireEvent('render', this);
53808 };
53809
53810 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
53811     tabTip:'',
53812     setRegion : function(region){
53813         this.region = region;
53814         if(region){
53815            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
53816         }else{
53817            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
53818         } 
53819     },
53820     
53821     /**
53822      * Returns the toolbar for this Panel if one was configured. 
53823      * @return {Roo.Toolbar} 
53824      */
53825     getToolbar : function(){
53826         return this.toolbar;
53827     },
53828     
53829     setActiveState : function(active){
53830         this.active = active;
53831         if(!active){
53832             this.fireEvent("deactivate", this);
53833         }else{
53834             this.fireEvent("activate", this);
53835         }
53836     },
53837     /**
53838      * Updates this panel's element
53839      * @param {String} content The new content
53840      * @param {Boolean} loadScripts (optional) true to look for and process scripts
53841     */
53842     setContent : function(content, loadScripts){
53843         this.el.update(content, loadScripts);
53844     },
53845
53846     ignoreResize : function(w, h){
53847         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
53848             return true;
53849         }else{
53850             this.lastSize = {width: w, height: h};
53851             return false;
53852         }
53853     },
53854     /**
53855      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
53856      * @return {Roo.UpdateManager} The UpdateManager
53857      */
53858     getUpdateManager : function(){
53859         return this.el.getUpdateManager();
53860     },
53861      /**
53862      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
53863      * @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:
53864 <pre><code>
53865 panel.load({
53866     url: "your-url.php",
53867     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
53868     callback: yourFunction,
53869     scope: yourObject, //(optional scope)
53870     discardUrl: false,
53871     nocache: false,
53872     text: "Loading...",
53873     timeout: 30,
53874     scripts: false
53875 });
53876 </code></pre>
53877      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
53878      * 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.
53879      * @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}
53880      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
53881      * @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.
53882      * @return {Roo.ContentPanel} this
53883      */
53884     load : function(){
53885         var um = this.el.getUpdateManager();
53886         um.update.apply(um, arguments);
53887         return this;
53888     },
53889
53890
53891     /**
53892      * 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.
53893      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
53894      * @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)
53895      * @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)
53896      * @return {Roo.UpdateManager} The UpdateManager
53897      */
53898     setUrl : function(url, params, loadOnce){
53899         if(this.refreshDelegate){
53900             this.removeListener("activate", this.refreshDelegate);
53901         }
53902         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
53903         this.on("activate", this.refreshDelegate);
53904         return this.el.getUpdateManager();
53905     },
53906     
53907     _handleRefresh : function(url, params, loadOnce){
53908         if(!loadOnce || !this.loaded){
53909             var updater = this.el.getUpdateManager();
53910             updater.update(url, params, this._setLoaded.createDelegate(this));
53911         }
53912     },
53913     
53914     _setLoaded : function(){
53915         this.loaded = true;
53916     }, 
53917     
53918     /**
53919      * Returns this panel's id
53920      * @return {String} 
53921      */
53922     getId : function(){
53923         return this.el.id;
53924     },
53925     
53926     /** 
53927      * Returns this panel's element - used by regiosn to add.
53928      * @return {Roo.Element} 
53929      */
53930     getEl : function(){
53931         return this.wrapEl || this.el;
53932     },
53933     
53934     adjustForComponents : function(width, height)
53935     {
53936         //Roo.log('adjustForComponents ');
53937         if(this.resizeEl != this.el){
53938             width -= this.el.getFrameWidth('lr');
53939             height -= this.el.getFrameWidth('tb');
53940         }
53941         if(this.toolbar){
53942             var te = this.toolbar.getEl();
53943             height -= te.getHeight();
53944             te.setWidth(width);
53945         }
53946         if(this.footer){
53947             var te = this.footer.getEl();
53948             //Roo.log("footer:" + te.getHeight());
53949             
53950             height -= te.getHeight();
53951             te.setWidth(width);
53952         }
53953         
53954         
53955         if(this.adjustments){
53956             width += this.adjustments[0];
53957             height += this.adjustments[1];
53958         }
53959         return {"width": width, "height": height};
53960     },
53961     
53962     setSize : function(width, height){
53963         if(this.fitToFrame && !this.ignoreResize(width, height)){
53964             if(this.fitContainer && this.resizeEl != this.el){
53965                 this.el.setSize(width, height);
53966             }
53967             var size = this.adjustForComponents(width, height);
53968             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
53969             this.fireEvent('resize', this, size.width, size.height);
53970         }
53971     },
53972     
53973     /**
53974      * Returns this panel's title
53975      * @return {String} 
53976      */
53977     getTitle : function(){
53978         return this.title;
53979     },
53980     
53981     /**
53982      * Set this panel's title
53983      * @param {String} title
53984      */
53985     setTitle : function(title){
53986         this.title = title;
53987         if(this.region){
53988             this.region.updatePanelTitle(this, title);
53989         }
53990     },
53991     
53992     /**
53993      * Returns true is this panel was configured to be closable
53994      * @return {Boolean} 
53995      */
53996     isClosable : function(){
53997         return this.closable;
53998     },
53999     
54000     beforeSlide : function(){
54001         this.el.clip();
54002         this.resizeEl.clip();
54003     },
54004     
54005     afterSlide : function(){
54006         this.el.unclip();
54007         this.resizeEl.unclip();
54008     },
54009     
54010     /**
54011      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
54012      *   Will fail silently if the {@link #setUrl} method has not been called.
54013      *   This does not activate the panel, just updates its content.
54014      */
54015     refresh : function(){
54016         if(this.refreshDelegate){
54017            this.loaded = false;
54018            this.refreshDelegate();
54019         }
54020     },
54021     
54022     /**
54023      * Destroys this panel
54024      */
54025     destroy : function(){
54026         this.el.removeAllListeners();
54027         var tempEl = document.createElement("span");
54028         tempEl.appendChild(this.el.dom);
54029         tempEl.innerHTML = "";
54030         this.el.remove();
54031         this.el = null;
54032     },
54033     
54034     /**
54035      * form - if the content panel contains a form - this is a reference to it.
54036      * @type {Roo.form.Form}
54037      */
54038     form : false,
54039     /**
54040      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
54041      *    This contains a reference to it.
54042      * @type {Roo.View}
54043      */
54044     view : false,
54045     
54046       /**
54047      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
54048      * <pre><code>
54049
54050 layout.addxtype({
54051        xtype : 'Form',
54052        items: [ .... ]
54053    }
54054 );
54055
54056 </code></pre>
54057      * @param {Object} cfg Xtype definition of item to add.
54058      */
54059     
54060     addxtype : function(cfg) {
54061         // add form..
54062         if (cfg.xtype.match(/^Form$/)) {
54063             
54064             var el;
54065             //if (this.footer) {
54066             //    el = this.footer.container.insertSibling(false, 'before');
54067             //} else {
54068                 el = this.el.createChild();
54069             //}
54070
54071             this.form = new  Roo.form.Form(cfg);
54072             
54073             
54074             if ( this.form.allItems.length) {
54075                 this.form.render(el.dom);
54076             }
54077             return this.form;
54078         }
54079         // should only have one of theses..
54080         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
54081             // views.. should not be just added - used named prop 'view''
54082             
54083             cfg.el = this.el.appendChild(document.createElement("div"));
54084             // factory?
54085             
54086             var ret = new Roo.factory(cfg);
54087              
54088              ret.render && ret.render(false, ''); // render blank..
54089             this.view = ret;
54090             return ret;
54091         }
54092         return false;
54093     }
54094 });
54095
54096 /**
54097  * @class Roo.GridPanel
54098  * @extends Roo.ContentPanel
54099  * @constructor
54100  * Create a new GridPanel.
54101  * @param {Roo.grid.Grid} grid The grid for this panel
54102  * @param {String/Object} config A string to set only the panel's title, or a config object
54103  */
54104 Roo.GridPanel = function(grid, config){
54105     
54106   
54107     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
54108         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
54109         
54110     this.wrapper.dom.appendChild(grid.getGridEl().dom);
54111     
54112     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
54113     
54114     if(this.toolbar){
54115         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
54116     }
54117     // xtype created footer. - not sure if will work as we normally have to render first..
54118     if (this.footer && !this.footer.el && this.footer.xtype) {
54119         
54120         this.footer.container = this.grid.getView().getFooterPanel(true);
54121         this.footer.dataSource = this.grid.dataSource;
54122         this.footer = Roo.factory(this.footer, Roo);
54123         
54124     }
54125     
54126     grid.monitorWindowResize = false; // turn off autosizing
54127     grid.autoHeight = false;
54128     grid.autoWidth = false;
54129     this.grid = grid;
54130     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
54131 };
54132
54133 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
54134     getId : function(){
54135         return this.grid.id;
54136     },
54137     
54138     /**
54139      * Returns the grid for this panel
54140      * @return {Roo.grid.Grid} 
54141      */
54142     getGrid : function(){
54143         return this.grid;    
54144     },
54145     
54146     setSize : function(width, height){
54147         if(!this.ignoreResize(width, height)){
54148             var grid = this.grid;
54149             var size = this.adjustForComponents(width, height);
54150             grid.getGridEl().setSize(size.width, size.height);
54151             grid.autoSize();
54152         }
54153     },
54154     
54155     beforeSlide : function(){
54156         this.grid.getView().scroller.clip();
54157     },
54158     
54159     afterSlide : function(){
54160         this.grid.getView().scroller.unclip();
54161     },
54162     
54163     destroy : function(){
54164         this.grid.destroy();
54165         delete this.grid;
54166         Roo.GridPanel.superclass.destroy.call(this); 
54167     }
54168 });
54169
54170
54171 /**
54172  * @class Roo.NestedLayoutPanel
54173  * @extends Roo.ContentPanel
54174  * @constructor
54175  * Create a new NestedLayoutPanel.
54176  * 
54177  * 
54178  * @param {Roo.BorderLayout} layout The layout for this panel
54179  * @param {String/Object} config A string to set only the title or a config object
54180  */
54181 Roo.NestedLayoutPanel = function(layout, config)
54182 {
54183     // construct with only one argument..
54184     /* FIXME - implement nicer consturctors
54185     if (layout.layout) {
54186         config = layout;
54187         layout = config.layout;
54188         delete config.layout;
54189     }
54190     if (layout.xtype && !layout.getEl) {
54191         // then layout needs constructing..
54192         layout = Roo.factory(layout, Roo);
54193     }
54194     */
54195     
54196     
54197     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
54198     
54199     layout.monitorWindowResize = false; // turn off autosizing
54200     this.layout = layout;
54201     this.layout.getEl().addClass("x-layout-nested-layout");
54202     
54203     
54204     
54205     
54206 };
54207
54208 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
54209
54210     setSize : function(width, height){
54211         if(!this.ignoreResize(width, height)){
54212             var size = this.adjustForComponents(width, height);
54213             var el = this.layout.getEl();
54214             el.setSize(size.width, size.height);
54215             var touch = el.dom.offsetWidth;
54216             this.layout.layout();
54217             // ie requires a double layout on the first pass
54218             if(Roo.isIE && !this.initialized){
54219                 this.initialized = true;
54220                 this.layout.layout();
54221             }
54222         }
54223     },
54224     
54225     // activate all subpanels if not currently active..
54226     
54227     setActiveState : function(active){
54228         this.active = active;
54229         if(!active){
54230             this.fireEvent("deactivate", this);
54231             return;
54232         }
54233         
54234         this.fireEvent("activate", this);
54235         // not sure if this should happen before or after..
54236         if (!this.layout) {
54237             return; // should not happen..
54238         }
54239         var reg = false;
54240         for (var r in this.layout.regions) {
54241             reg = this.layout.getRegion(r);
54242             if (reg.getActivePanel()) {
54243                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
54244                 reg.setActivePanel(reg.getActivePanel());
54245                 continue;
54246             }
54247             if (!reg.panels.length) {
54248                 continue;
54249             }
54250             reg.showPanel(reg.getPanel(0));
54251         }
54252         
54253         
54254         
54255         
54256     },
54257     
54258     /**
54259      * Returns the nested BorderLayout for this panel
54260      * @return {Roo.BorderLayout} 
54261      */
54262     getLayout : function(){
54263         return this.layout;
54264     },
54265     
54266      /**
54267      * Adds a xtype elements to the layout of the nested panel
54268      * <pre><code>
54269
54270 panel.addxtype({
54271        xtype : 'ContentPanel',
54272        region: 'west',
54273        items: [ .... ]
54274    }
54275 );
54276
54277 panel.addxtype({
54278         xtype : 'NestedLayoutPanel',
54279         region: 'west',
54280         layout: {
54281            center: { },
54282            west: { }   
54283         },
54284         items : [ ... list of content panels or nested layout panels.. ]
54285    }
54286 );
54287 </code></pre>
54288      * @param {Object} cfg Xtype definition of item to add.
54289      */
54290     addxtype : function(cfg) {
54291         return this.layout.addxtype(cfg);
54292     
54293     }
54294 });
54295
54296 Roo.ScrollPanel = function(el, config, content){
54297     config = config || {};
54298     config.fitToFrame = true;
54299     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
54300     
54301     this.el.dom.style.overflow = "hidden";
54302     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
54303     this.el.removeClass("x-layout-inactive-content");
54304     this.el.on("mousewheel", this.onWheel, this);
54305
54306     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
54307     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
54308     up.unselectable(); down.unselectable();
54309     up.on("click", this.scrollUp, this);
54310     down.on("click", this.scrollDown, this);
54311     up.addClassOnOver("x-scroller-btn-over");
54312     down.addClassOnOver("x-scroller-btn-over");
54313     up.addClassOnClick("x-scroller-btn-click");
54314     down.addClassOnClick("x-scroller-btn-click");
54315     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
54316
54317     this.resizeEl = this.el;
54318     this.el = wrap; this.up = up; this.down = down;
54319 };
54320
54321 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
54322     increment : 100,
54323     wheelIncrement : 5,
54324     scrollUp : function(){
54325         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
54326     },
54327
54328     scrollDown : function(){
54329         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
54330     },
54331
54332     afterScroll : function(){
54333         var el = this.resizeEl;
54334         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
54335         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
54336         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
54337     },
54338
54339     setSize : function(){
54340         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
54341         this.afterScroll();
54342     },
54343
54344     onWheel : function(e){
54345         var d = e.getWheelDelta();
54346         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
54347         this.afterScroll();
54348         e.stopEvent();
54349     },
54350
54351     setContent : function(content, loadScripts){
54352         this.resizeEl.update(content, loadScripts);
54353     }
54354
54355 });
54356
54357
54358
54359
54360
54361
54362
54363
54364
54365 /**
54366  * @class Roo.TreePanel
54367  * @extends Roo.ContentPanel
54368  * @constructor
54369  * Create a new TreePanel. - defaults to fit/scoll contents.
54370  * @param {String/Object} config A string to set only the panel's title, or a config object
54371  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
54372  */
54373 Roo.TreePanel = function(config){
54374     var el = config.el;
54375     var tree = config.tree;
54376     delete config.tree; 
54377     delete config.el; // hopefull!
54378     
54379     // wrapper for IE7 strict & safari scroll issue
54380     
54381     var treeEl = el.createChild();
54382     config.resizeEl = treeEl;
54383     
54384     
54385     
54386     Roo.TreePanel.superclass.constructor.call(this, el, config);
54387  
54388  
54389     this.tree = new Roo.tree.TreePanel(treeEl , tree);
54390     //console.log(tree);
54391     this.on('activate', function()
54392     {
54393         if (this.tree.rendered) {
54394             return;
54395         }
54396         //console.log('render tree');
54397         this.tree.render();
54398     });
54399     // this should not be needed.. - it's actually the 'el' that resizes?
54400     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
54401     
54402     //this.on('resize',  function (cp, w, h) {
54403     //        this.tree.innerCt.setWidth(w);
54404     //        this.tree.innerCt.setHeight(h);
54405     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
54406     //});
54407
54408         
54409     
54410 };
54411
54412 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
54413     fitToFrame : true,
54414     autoScroll : true
54415 });
54416
54417
54418
54419
54420
54421
54422
54423
54424
54425
54426
54427 /*
54428  * Based on:
54429  * Ext JS Library 1.1.1
54430  * Copyright(c) 2006-2007, Ext JS, LLC.
54431  *
54432  * Originally Released Under LGPL - original licence link has changed is not relivant.
54433  *
54434  * Fork - LGPL
54435  * <script type="text/javascript">
54436  */
54437  
54438
54439 /**
54440  * @class Roo.ReaderLayout
54441  * @extends Roo.BorderLayout
54442  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
54443  * center region containing two nested regions (a top one for a list view and one for item preview below),
54444  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
54445  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
54446  * expedites the setup of the overall layout and regions for this common application style.
54447  * Example:
54448  <pre><code>
54449 var reader = new Roo.ReaderLayout();
54450 var CP = Roo.ContentPanel;  // shortcut for adding
54451
54452 reader.beginUpdate();
54453 reader.add("north", new CP("north", "North"));
54454 reader.add("west", new CP("west", {title: "West"}));
54455 reader.add("east", new CP("east", {title: "East"}));
54456
54457 reader.regions.listView.add(new CP("listView", "List"));
54458 reader.regions.preview.add(new CP("preview", "Preview"));
54459 reader.endUpdate();
54460 </code></pre>
54461 * @constructor
54462 * Create a new ReaderLayout
54463 * @param {Object} config Configuration options
54464 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
54465 * document.body if omitted)
54466 */
54467 Roo.ReaderLayout = function(config, renderTo){
54468     var c = config || {size:{}};
54469     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
54470         north: c.north !== false ? Roo.apply({
54471             split:false,
54472             initialSize: 32,
54473             titlebar: false
54474         }, c.north) : false,
54475         west: c.west !== false ? Roo.apply({
54476             split:true,
54477             initialSize: 200,
54478             minSize: 175,
54479             maxSize: 400,
54480             titlebar: true,
54481             collapsible: true,
54482             animate: true,
54483             margins:{left:5,right:0,bottom:5,top:5},
54484             cmargins:{left:5,right:5,bottom:5,top:5}
54485         }, c.west) : false,
54486         east: c.east !== false ? Roo.apply({
54487             split:true,
54488             initialSize: 200,
54489             minSize: 175,
54490             maxSize: 400,
54491             titlebar: true,
54492             collapsible: true,
54493             animate: true,
54494             margins:{left:0,right:5,bottom:5,top:5},
54495             cmargins:{left:5,right:5,bottom:5,top:5}
54496         }, c.east) : false,
54497         center: Roo.apply({
54498             tabPosition: 'top',
54499             autoScroll:false,
54500             closeOnTab: true,
54501             titlebar:false,
54502             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
54503         }, c.center)
54504     });
54505
54506     this.el.addClass('x-reader');
54507
54508     this.beginUpdate();
54509
54510     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
54511         south: c.preview !== false ? Roo.apply({
54512             split:true,
54513             initialSize: 200,
54514             minSize: 100,
54515             autoScroll:true,
54516             collapsible:true,
54517             titlebar: true,
54518             cmargins:{top:5,left:0, right:0, bottom:0}
54519         }, c.preview) : false,
54520         center: Roo.apply({
54521             autoScroll:false,
54522             titlebar:false,
54523             minHeight:200
54524         }, c.listView)
54525     });
54526     this.add('center', new Roo.NestedLayoutPanel(inner,
54527             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
54528
54529     this.endUpdate();
54530
54531     this.regions.preview = inner.getRegion('south');
54532     this.regions.listView = inner.getRegion('center');
54533 };
54534
54535 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
54536  * Based on:
54537  * Ext JS Library 1.1.1
54538  * Copyright(c) 2006-2007, Ext JS, LLC.
54539  *
54540  * Originally Released Under LGPL - original licence link has changed is not relivant.
54541  *
54542  * Fork - LGPL
54543  * <script type="text/javascript">
54544  */
54545  
54546 /**
54547  * @class Roo.grid.Grid
54548  * @extends Roo.util.Observable
54549  * This class represents the primary interface of a component based grid control.
54550  * <br><br>Usage:<pre><code>
54551  var grid = new Roo.grid.Grid("my-container-id", {
54552      ds: myDataStore,
54553      cm: myColModel,
54554      selModel: mySelectionModel,
54555      autoSizeColumns: true,
54556      monitorWindowResize: false,
54557      trackMouseOver: true
54558  });
54559  // set any options
54560  grid.render();
54561  * </code></pre>
54562  * <b>Common Problems:</b><br/>
54563  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
54564  * element will correct this<br/>
54565  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
54566  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
54567  * are unpredictable.<br/>
54568  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
54569  * grid to calculate dimensions/offsets.<br/>
54570   * @constructor
54571  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
54572  * The container MUST have some type of size defined for the grid to fill. The container will be
54573  * automatically set to position relative if it isn't already.
54574  * @param {Object} config A config object that sets properties on this grid.
54575  */
54576 Roo.grid.Grid = function(container, config){
54577         // initialize the container
54578         this.container = Roo.get(container);
54579         this.container.update("");
54580         this.container.setStyle("overflow", "hidden");
54581     this.container.addClass('x-grid-container');
54582
54583     this.id = this.container.id;
54584
54585     Roo.apply(this, config);
54586     // check and correct shorthanded configs
54587     if(this.ds){
54588         this.dataSource = this.ds;
54589         delete this.ds;
54590     }
54591     if(this.cm){
54592         this.colModel = this.cm;
54593         delete this.cm;
54594     }
54595     if(this.sm){
54596         this.selModel = this.sm;
54597         delete this.sm;
54598     }
54599
54600     if (this.selModel) {
54601         this.selModel = Roo.factory(this.selModel, Roo.grid);
54602         this.sm = this.selModel;
54603         this.sm.xmodule = this.xmodule || false;
54604     }
54605     if (typeof(this.colModel.config) == 'undefined') {
54606         this.colModel = new Roo.grid.ColumnModel(this.colModel);
54607         this.cm = this.colModel;
54608         this.cm.xmodule = this.xmodule || false;
54609     }
54610     if (this.dataSource) {
54611         this.dataSource= Roo.factory(this.dataSource, Roo.data);
54612         this.ds = this.dataSource;
54613         this.ds.xmodule = this.xmodule || false;
54614          
54615     }
54616     
54617     
54618     
54619     if(this.width){
54620         this.container.setWidth(this.width);
54621     }
54622
54623     if(this.height){
54624         this.container.setHeight(this.height);
54625     }
54626     /** @private */
54627         this.addEvents({
54628         // raw events
54629         /**
54630          * @event click
54631          * The raw click event for the entire grid.
54632          * @param {Roo.EventObject} e
54633          */
54634         "click" : true,
54635         /**
54636          * @event dblclick
54637          * The raw dblclick event for the entire grid.
54638          * @param {Roo.EventObject} e
54639          */
54640         "dblclick" : true,
54641         /**
54642          * @event contextmenu
54643          * The raw contextmenu event for the entire grid.
54644          * @param {Roo.EventObject} e
54645          */
54646         "contextmenu" : true,
54647         /**
54648          * @event mousedown
54649          * The raw mousedown event for the entire grid.
54650          * @param {Roo.EventObject} e
54651          */
54652         "mousedown" : true,
54653         /**
54654          * @event mouseup
54655          * The raw mouseup event for the entire grid.
54656          * @param {Roo.EventObject} e
54657          */
54658         "mouseup" : true,
54659         /**
54660          * @event mouseover
54661          * The raw mouseover event for the entire grid.
54662          * @param {Roo.EventObject} e
54663          */
54664         "mouseover" : true,
54665         /**
54666          * @event mouseout
54667          * The raw mouseout event for the entire grid.
54668          * @param {Roo.EventObject} e
54669          */
54670         "mouseout" : true,
54671         /**
54672          * @event keypress
54673          * The raw keypress event for the entire grid.
54674          * @param {Roo.EventObject} e
54675          */
54676         "keypress" : true,
54677         /**
54678          * @event keydown
54679          * The raw keydown event for the entire grid.
54680          * @param {Roo.EventObject} e
54681          */
54682         "keydown" : true,
54683
54684         // custom events
54685
54686         /**
54687          * @event cellclick
54688          * Fires when a cell is clicked
54689          * @param {Grid} this
54690          * @param {Number} rowIndex
54691          * @param {Number} columnIndex
54692          * @param {Roo.EventObject} e
54693          */
54694         "cellclick" : true,
54695         /**
54696          * @event celldblclick
54697          * Fires when a cell is double clicked
54698          * @param {Grid} this
54699          * @param {Number} rowIndex
54700          * @param {Number} columnIndex
54701          * @param {Roo.EventObject} e
54702          */
54703         "celldblclick" : true,
54704         /**
54705          * @event rowclick
54706          * Fires when a row is clicked
54707          * @param {Grid} this
54708          * @param {Number} rowIndex
54709          * @param {Roo.EventObject} e
54710          */
54711         "rowclick" : true,
54712         /**
54713          * @event rowdblclick
54714          * Fires when a row is double clicked
54715          * @param {Grid} this
54716          * @param {Number} rowIndex
54717          * @param {Roo.EventObject} e
54718          */
54719         "rowdblclick" : true,
54720         /**
54721          * @event headerclick
54722          * Fires when a header is clicked
54723          * @param {Grid} this
54724          * @param {Number} columnIndex
54725          * @param {Roo.EventObject} e
54726          */
54727         "headerclick" : true,
54728         /**
54729          * @event headerdblclick
54730          * Fires when a header cell is double clicked
54731          * @param {Grid} this
54732          * @param {Number} columnIndex
54733          * @param {Roo.EventObject} e
54734          */
54735         "headerdblclick" : true,
54736         /**
54737          * @event rowcontextmenu
54738          * Fires when a row is right clicked
54739          * @param {Grid} this
54740          * @param {Number} rowIndex
54741          * @param {Roo.EventObject} e
54742          */
54743         "rowcontextmenu" : true,
54744         /**
54745          * @event cellcontextmenu
54746          * Fires when a cell is right clicked
54747          * @param {Grid} this
54748          * @param {Number} rowIndex
54749          * @param {Number} cellIndex
54750          * @param {Roo.EventObject} e
54751          */
54752          "cellcontextmenu" : true,
54753         /**
54754          * @event headercontextmenu
54755          * Fires when a header is right clicked
54756          * @param {Grid} this
54757          * @param {Number} columnIndex
54758          * @param {Roo.EventObject} e
54759          */
54760         "headercontextmenu" : true,
54761         /**
54762          * @event bodyscroll
54763          * Fires when the body element is scrolled
54764          * @param {Number} scrollLeft
54765          * @param {Number} scrollTop
54766          */
54767         "bodyscroll" : true,
54768         /**
54769          * @event columnresize
54770          * Fires when the user resizes a column
54771          * @param {Number} columnIndex
54772          * @param {Number} newSize
54773          */
54774         "columnresize" : true,
54775         /**
54776          * @event columnmove
54777          * Fires when the user moves a column
54778          * @param {Number} oldIndex
54779          * @param {Number} newIndex
54780          */
54781         "columnmove" : true,
54782         /**
54783          * @event startdrag
54784          * Fires when row(s) start being dragged
54785          * @param {Grid} this
54786          * @param {Roo.GridDD} dd The drag drop object
54787          * @param {event} e The raw browser event
54788          */
54789         "startdrag" : true,
54790         /**
54791          * @event enddrag
54792          * Fires when a drag operation is complete
54793          * @param {Grid} this
54794          * @param {Roo.GridDD} dd The drag drop object
54795          * @param {event} e The raw browser event
54796          */
54797         "enddrag" : true,
54798         /**
54799          * @event dragdrop
54800          * Fires when dragged row(s) are dropped on a valid DD target
54801          * @param {Grid} this
54802          * @param {Roo.GridDD} dd The drag drop object
54803          * @param {String} targetId The target drag drop object
54804          * @param {event} e The raw browser event
54805          */
54806         "dragdrop" : true,
54807         /**
54808          * @event dragover
54809          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
54810          * @param {Grid} this
54811          * @param {Roo.GridDD} dd The drag drop object
54812          * @param {String} targetId The target drag drop object
54813          * @param {event} e The raw browser event
54814          */
54815         "dragover" : true,
54816         /**
54817          * @event dragenter
54818          *  Fires when the dragged row(s) first cross another DD target while being dragged
54819          * @param {Grid} this
54820          * @param {Roo.GridDD} dd The drag drop object
54821          * @param {String} targetId The target drag drop object
54822          * @param {event} e The raw browser event
54823          */
54824         "dragenter" : true,
54825         /**
54826          * @event dragout
54827          * Fires when the dragged row(s) leave another DD target while being dragged
54828          * @param {Grid} this
54829          * @param {Roo.GridDD} dd The drag drop object
54830          * @param {String} targetId The target drag drop object
54831          * @param {event} e The raw browser event
54832          */
54833         "dragout" : true,
54834         /**
54835          * @event rowclass
54836          * Fires when a row is rendered, so you can change add a style to it.
54837          * @param {GridView} gridview   The grid view
54838          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
54839          */
54840         'rowclass' : true,
54841
54842         /**
54843          * @event render
54844          * Fires when the grid is rendered
54845          * @param {Grid} grid
54846          */
54847         'render' : true
54848     });
54849
54850     Roo.grid.Grid.superclass.constructor.call(this);
54851 };
54852 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
54853     
54854     /**
54855      * @cfg {String} ddGroup - drag drop group.
54856      */
54857
54858     /**
54859      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
54860      */
54861     minColumnWidth : 25,
54862
54863     /**
54864      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
54865      * <b>on initial render.</b> It is more efficient to explicitly size the columns
54866      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
54867      */
54868     autoSizeColumns : false,
54869
54870     /**
54871      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
54872      */
54873     autoSizeHeaders : true,
54874
54875     /**
54876      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
54877      */
54878     monitorWindowResize : true,
54879
54880     /**
54881      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
54882      * rows measured to get a columns size. Default is 0 (all rows).
54883      */
54884     maxRowsToMeasure : 0,
54885
54886     /**
54887      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
54888      */
54889     trackMouseOver : true,
54890
54891     /**
54892     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
54893     */
54894     
54895     /**
54896     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
54897     */
54898     enableDragDrop : false,
54899     
54900     /**
54901     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
54902     */
54903     enableColumnMove : true,
54904     
54905     /**
54906     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
54907     */
54908     enableColumnHide : true,
54909     
54910     /**
54911     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
54912     */
54913     enableRowHeightSync : false,
54914     
54915     /**
54916     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
54917     */
54918     stripeRows : true,
54919     
54920     /**
54921     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
54922     */
54923     autoHeight : false,
54924
54925     /**
54926      * @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.
54927      */
54928     autoExpandColumn : false,
54929
54930     /**
54931     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
54932     * Default is 50.
54933     */
54934     autoExpandMin : 50,
54935
54936     /**
54937     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
54938     */
54939     autoExpandMax : 1000,
54940
54941     /**
54942     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
54943     */
54944     view : null,
54945
54946     /**
54947     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
54948     */
54949     loadMask : false,
54950     /**
54951     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
54952     */
54953     dropTarget: false,
54954     
54955    
54956     
54957     // private
54958     rendered : false,
54959
54960     /**
54961     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
54962     * of a fixed width. Default is false.
54963     */
54964     /**
54965     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
54966     */
54967     /**
54968      * Called once after all setup has been completed and the grid is ready to be rendered.
54969      * @return {Roo.grid.Grid} this
54970      */
54971     render : function()
54972     {
54973         var c = this.container;
54974         // try to detect autoHeight/width mode
54975         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
54976             this.autoHeight = true;
54977         }
54978         var view = this.getView();
54979         view.init(this);
54980
54981         c.on("click", this.onClick, this);
54982         c.on("dblclick", this.onDblClick, this);
54983         c.on("contextmenu", this.onContextMenu, this);
54984         c.on("keydown", this.onKeyDown, this);
54985         if (Roo.isTouch) {
54986             c.on("touchstart", this.onTouchStart, this);
54987         }
54988
54989         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
54990
54991         this.getSelectionModel().init(this);
54992
54993         view.render();
54994
54995         if(this.loadMask){
54996             this.loadMask = new Roo.LoadMask(this.container,
54997                     Roo.apply({store:this.dataSource}, this.loadMask));
54998         }
54999         
55000         
55001         if (this.toolbar && this.toolbar.xtype) {
55002             this.toolbar.container = this.getView().getHeaderPanel(true);
55003             this.toolbar = new Roo.Toolbar(this.toolbar);
55004         }
55005         if (this.footer && this.footer.xtype) {
55006             this.footer.dataSource = this.getDataSource();
55007             this.footer.container = this.getView().getFooterPanel(true);
55008             this.footer = Roo.factory(this.footer, Roo);
55009         }
55010         if (this.dropTarget && this.dropTarget.xtype) {
55011             delete this.dropTarget.xtype;
55012             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
55013         }
55014         
55015         
55016         this.rendered = true;
55017         this.fireEvent('render', this);
55018         return this;
55019     },
55020
55021     /**
55022      * Reconfigures the grid to use a different Store and Column Model.
55023      * The View will be bound to the new objects and refreshed.
55024      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
55025      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
55026      */
55027     reconfigure : function(dataSource, colModel){
55028         if(this.loadMask){
55029             this.loadMask.destroy();
55030             this.loadMask = new Roo.LoadMask(this.container,
55031                     Roo.apply({store:dataSource}, this.loadMask));
55032         }
55033         this.view.bind(dataSource, colModel);
55034         this.dataSource = dataSource;
55035         this.colModel = colModel;
55036         this.view.refresh(true);
55037     },
55038     /**
55039      * addColumns
55040      * Add's a column, default at the end..
55041      
55042      * @param {int} position to add (default end)
55043      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
55044      */
55045     addColumns : function(pos, ar)
55046     {
55047         
55048         for (var i =0;i< ar.length;i++) {
55049             var cfg = ar[i];
55050             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
55051             this.cm.lookup[cfg.id] = cfg;
55052         }
55053         
55054         
55055         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
55056             pos = this.cm.config.length; //this.cm.config.push(cfg);
55057         } 
55058         pos = Math.max(0,pos);
55059         ar.unshift(0);
55060         ar.unshift(pos);
55061         this.cm.config.splice.apply(this.cm.config, ar);
55062         
55063         
55064         
55065         this.view.generateRules(this.cm);
55066         this.view.refresh(true);
55067         
55068     },
55069     
55070     
55071     
55072     
55073     // private
55074     onKeyDown : function(e){
55075         this.fireEvent("keydown", e);
55076     },
55077
55078     /**
55079      * Destroy this grid.
55080      * @param {Boolean} removeEl True to remove the element
55081      */
55082     destroy : function(removeEl, keepListeners){
55083         if(this.loadMask){
55084             this.loadMask.destroy();
55085         }
55086         var c = this.container;
55087         c.removeAllListeners();
55088         this.view.destroy();
55089         this.colModel.purgeListeners();
55090         if(!keepListeners){
55091             this.purgeListeners();
55092         }
55093         c.update("");
55094         if(removeEl === true){
55095             c.remove();
55096         }
55097     },
55098
55099     // private
55100     processEvent : function(name, e){
55101         // does this fire select???
55102         //Roo.log('grid:processEvent '  + name);
55103         
55104         if (name != 'touchstart' ) {
55105             this.fireEvent(name, e);    
55106         }
55107         
55108         var t = e.getTarget();
55109         var v = this.view;
55110         var header = v.findHeaderIndex(t);
55111         if(header !== false){
55112             var ename = name == 'touchstart' ? 'click' : name;
55113              
55114             this.fireEvent("header" + ename, this, header, e);
55115         }else{
55116             var row = v.findRowIndex(t);
55117             var cell = v.findCellIndex(t);
55118             if (name == 'touchstart') {
55119                 // first touch is always a click.
55120                 // hopefull this happens after selection is updated.?
55121                 name = false;
55122                 
55123                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
55124                     var cs = this.selModel.getSelectedCell();
55125                     if (row == cs[0] && cell == cs[1]){
55126                         name = 'dblclick';
55127                     }
55128                 }
55129                 if (typeof(this.selModel.getSelections) != 'undefined') {
55130                     var cs = this.selModel.getSelections();
55131                     var ds = this.dataSource;
55132                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
55133                         name = 'dblclick';
55134                     }
55135                 }
55136                 if (!name) {
55137                     return;
55138                 }
55139             }
55140             
55141             
55142             if(row !== false){
55143                 this.fireEvent("row" + name, this, row, e);
55144                 if(cell !== false){
55145                     this.fireEvent("cell" + name, this, row, cell, e);
55146                 }
55147             }
55148         }
55149     },
55150
55151     // private
55152     onClick : function(e){
55153         this.processEvent("click", e);
55154     },
55155    // private
55156     onTouchStart : function(e){
55157         this.processEvent("touchstart", e);
55158     },
55159
55160     // private
55161     onContextMenu : function(e, t){
55162         this.processEvent("contextmenu", e);
55163     },
55164
55165     // private
55166     onDblClick : function(e){
55167         this.processEvent("dblclick", e);
55168     },
55169
55170     // private
55171     walkCells : function(row, col, step, fn, scope){
55172         var cm = this.colModel, clen = cm.getColumnCount();
55173         var ds = this.dataSource, rlen = ds.getCount(), first = true;
55174         if(step < 0){
55175             if(col < 0){
55176                 row--;
55177                 first = false;
55178             }
55179             while(row >= 0){
55180                 if(!first){
55181                     col = clen-1;
55182                 }
55183                 first = false;
55184                 while(col >= 0){
55185                     if(fn.call(scope || this, row, col, cm) === true){
55186                         return [row, col];
55187                     }
55188                     col--;
55189                 }
55190                 row--;
55191             }
55192         } else {
55193             if(col >= clen){
55194                 row++;
55195                 first = false;
55196             }
55197             while(row < rlen){
55198                 if(!first){
55199                     col = 0;
55200                 }
55201                 first = false;
55202                 while(col < clen){
55203                     if(fn.call(scope || this, row, col, cm) === true){
55204                         return [row, col];
55205                     }
55206                     col++;
55207                 }
55208                 row++;
55209             }
55210         }
55211         return null;
55212     },
55213
55214     // private
55215     getSelections : function(){
55216         return this.selModel.getSelections();
55217     },
55218
55219     /**
55220      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
55221      * but if manual update is required this method will initiate it.
55222      */
55223     autoSize : function(){
55224         if(this.rendered){
55225             this.view.layout();
55226             if(this.view.adjustForScroll){
55227                 this.view.adjustForScroll();
55228             }
55229         }
55230     },
55231
55232     /**
55233      * Returns the grid's underlying element.
55234      * @return {Element} The element
55235      */
55236     getGridEl : function(){
55237         return this.container;
55238     },
55239
55240     // private for compatibility, overridden by editor grid
55241     stopEditing : function(){},
55242
55243     /**
55244      * Returns the grid's SelectionModel.
55245      * @return {SelectionModel}
55246      */
55247     getSelectionModel : function(){
55248         if(!this.selModel){
55249             this.selModel = new Roo.grid.RowSelectionModel();
55250         }
55251         return this.selModel;
55252     },
55253
55254     /**
55255      * Returns the grid's DataSource.
55256      * @return {DataSource}
55257      */
55258     getDataSource : function(){
55259         return this.dataSource;
55260     },
55261
55262     /**
55263      * Returns the grid's ColumnModel.
55264      * @return {ColumnModel}
55265      */
55266     getColumnModel : function(){
55267         return this.colModel;
55268     },
55269
55270     /**
55271      * Returns the grid's GridView object.
55272      * @return {GridView}
55273      */
55274     getView : function(){
55275         if(!this.view){
55276             this.view = new Roo.grid.GridView(this.viewConfig);
55277         }
55278         return this.view;
55279     },
55280     /**
55281      * Called to get grid's drag proxy text, by default returns this.ddText.
55282      * @return {String}
55283      */
55284     getDragDropText : function(){
55285         var count = this.selModel.getCount();
55286         return String.format(this.ddText, count, count == 1 ? '' : 's');
55287     }
55288 });
55289 /**
55290  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
55291  * %0 is replaced with the number of selected rows.
55292  * @type String
55293  */
55294 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
55295  * Based on:
55296  * Ext JS Library 1.1.1
55297  * Copyright(c) 2006-2007, Ext JS, LLC.
55298  *
55299  * Originally Released Under LGPL - original licence link has changed is not relivant.
55300  *
55301  * Fork - LGPL
55302  * <script type="text/javascript">
55303  */
55304  
55305 Roo.grid.AbstractGridView = function(){
55306         this.grid = null;
55307         
55308         this.events = {
55309             "beforerowremoved" : true,
55310             "beforerowsinserted" : true,
55311             "beforerefresh" : true,
55312             "rowremoved" : true,
55313             "rowsinserted" : true,
55314             "rowupdated" : true,
55315             "refresh" : true
55316         };
55317     Roo.grid.AbstractGridView.superclass.constructor.call(this);
55318 };
55319
55320 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
55321     rowClass : "x-grid-row",
55322     cellClass : "x-grid-cell",
55323     tdClass : "x-grid-td",
55324     hdClass : "x-grid-hd",
55325     splitClass : "x-grid-hd-split",
55326     
55327     init: function(grid){
55328         this.grid = grid;
55329                 var cid = this.grid.getGridEl().id;
55330         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
55331         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
55332         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
55333         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
55334         },
55335         
55336     getColumnRenderers : function(){
55337         var renderers = [];
55338         var cm = this.grid.colModel;
55339         var colCount = cm.getColumnCount();
55340         for(var i = 0; i < colCount; i++){
55341             renderers[i] = cm.getRenderer(i);
55342         }
55343         return renderers;
55344     },
55345     
55346     getColumnIds : function(){
55347         var ids = [];
55348         var cm = this.grid.colModel;
55349         var colCount = cm.getColumnCount();
55350         for(var i = 0; i < colCount; i++){
55351             ids[i] = cm.getColumnId(i);
55352         }
55353         return ids;
55354     },
55355     
55356     getDataIndexes : function(){
55357         if(!this.indexMap){
55358             this.indexMap = this.buildIndexMap();
55359         }
55360         return this.indexMap.colToData;
55361     },
55362     
55363     getColumnIndexByDataIndex : function(dataIndex){
55364         if(!this.indexMap){
55365             this.indexMap = this.buildIndexMap();
55366         }
55367         return this.indexMap.dataToCol[dataIndex];
55368     },
55369     
55370     /**
55371      * Set a css style for a column dynamically. 
55372      * @param {Number} colIndex The index of the column
55373      * @param {String} name The css property name
55374      * @param {String} value The css value
55375      */
55376     setCSSStyle : function(colIndex, name, value){
55377         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
55378         Roo.util.CSS.updateRule(selector, name, value);
55379     },
55380     
55381     generateRules : function(cm){
55382         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
55383         Roo.util.CSS.removeStyleSheet(rulesId);
55384         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55385             var cid = cm.getColumnId(i);
55386             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
55387                          this.tdSelector, cid, " {\n}\n",
55388                          this.hdSelector, cid, " {\n}\n",
55389                          this.splitSelector, cid, " {\n}\n");
55390         }
55391         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
55392     }
55393 });/*
55394  * Based on:
55395  * Ext JS Library 1.1.1
55396  * Copyright(c) 2006-2007, Ext JS, LLC.
55397  *
55398  * Originally Released Under LGPL - original licence link has changed is not relivant.
55399  *
55400  * Fork - LGPL
55401  * <script type="text/javascript">
55402  */
55403
55404 // private
55405 // This is a support class used internally by the Grid components
55406 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
55407     this.grid = grid;
55408     this.view = grid.getView();
55409     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
55410     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
55411     if(hd2){
55412         this.setHandleElId(Roo.id(hd));
55413         this.setOuterHandleElId(Roo.id(hd2));
55414     }
55415     this.scroll = false;
55416 };
55417 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
55418     maxDragWidth: 120,
55419     getDragData : function(e){
55420         var t = Roo.lib.Event.getTarget(e);
55421         var h = this.view.findHeaderCell(t);
55422         if(h){
55423             return {ddel: h.firstChild, header:h};
55424         }
55425         return false;
55426     },
55427
55428     onInitDrag : function(e){
55429         this.view.headersDisabled = true;
55430         var clone = this.dragData.ddel.cloneNode(true);
55431         clone.id = Roo.id();
55432         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
55433         this.proxy.update(clone);
55434         return true;
55435     },
55436
55437     afterValidDrop : function(){
55438         var v = this.view;
55439         setTimeout(function(){
55440             v.headersDisabled = false;
55441         }, 50);
55442     },
55443
55444     afterInvalidDrop : function(){
55445         var v = this.view;
55446         setTimeout(function(){
55447             v.headersDisabled = false;
55448         }, 50);
55449     }
55450 });
55451 /*
55452  * Based on:
55453  * Ext JS Library 1.1.1
55454  * Copyright(c) 2006-2007, Ext JS, LLC.
55455  *
55456  * Originally Released Under LGPL - original licence link has changed is not relivant.
55457  *
55458  * Fork - LGPL
55459  * <script type="text/javascript">
55460  */
55461 // private
55462 // This is a support class used internally by the Grid components
55463 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
55464     this.grid = grid;
55465     this.view = grid.getView();
55466     // split the proxies so they don't interfere with mouse events
55467     this.proxyTop = Roo.DomHelper.append(document.body, {
55468         cls:"col-move-top", html:"&#160;"
55469     }, true);
55470     this.proxyBottom = Roo.DomHelper.append(document.body, {
55471         cls:"col-move-bottom", html:"&#160;"
55472     }, true);
55473     this.proxyTop.hide = this.proxyBottom.hide = function(){
55474         this.setLeftTop(-100,-100);
55475         this.setStyle("visibility", "hidden");
55476     };
55477     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
55478     // temporarily disabled
55479     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
55480     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
55481 };
55482 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
55483     proxyOffsets : [-4, -9],
55484     fly: Roo.Element.fly,
55485
55486     getTargetFromEvent : function(e){
55487         var t = Roo.lib.Event.getTarget(e);
55488         var cindex = this.view.findCellIndex(t);
55489         if(cindex !== false){
55490             return this.view.getHeaderCell(cindex);
55491         }
55492         return null;
55493     },
55494
55495     nextVisible : function(h){
55496         var v = this.view, cm = this.grid.colModel;
55497         h = h.nextSibling;
55498         while(h){
55499             if(!cm.isHidden(v.getCellIndex(h))){
55500                 return h;
55501             }
55502             h = h.nextSibling;
55503         }
55504         return null;
55505     },
55506
55507     prevVisible : function(h){
55508         var v = this.view, cm = this.grid.colModel;
55509         h = h.prevSibling;
55510         while(h){
55511             if(!cm.isHidden(v.getCellIndex(h))){
55512                 return h;
55513             }
55514             h = h.prevSibling;
55515         }
55516         return null;
55517     },
55518
55519     positionIndicator : function(h, n, e){
55520         var x = Roo.lib.Event.getPageX(e);
55521         var r = Roo.lib.Dom.getRegion(n.firstChild);
55522         var px, pt, py = r.top + this.proxyOffsets[1];
55523         if((r.right - x) <= (r.right-r.left)/2){
55524             px = r.right+this.view.borderWidth;
55525             pt = "after";
55526         }else{
55527             px = r.left;
55528             pt = "before";
55529         }
55530         var oldIndex = this.view.getCellIndex(h);
55531         var newIndex = this.view.getCellIndex(n);
55532
55533         if(this.grid.colModel.isFixed(newIndex)){
55534             return false;
55535         }
55536
55537         var locked = this.grid.colModel.isLocked(newIndex);
55538
55539         if(pt == "after"){
55540             newIndex++;
55541         }
55542         if(oldIndex < newIndex){
55543             newIndex--;
55544         }
55545         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
55546             return false;
55547         }
55548         px +=  this.proxyOffsets[0];
55549         this.proxyTop.setLeftTop(px, py);
55550         this.proxyTop.show();
55551         if(!this.bottomOffset){
55552             this.bottomOffset = this.view.mainHd.getHeight();
55553         }
55554         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
55555         this.proxyBottom.show();
55556         return pt;
55557     },
55558
55559     onNodeEnter : function(n, dd, e, data){
55560         if(data.header != n){
55561             this.positionIndicator(data.header, n, e);
55562         }
55563     },
55564
55565     onNodeOver : function(n, dd, e, data){
55566         var result = false;
55567         if(data.header != n){
55568             result = this.positionIndicator(data.header, n, e);
55569         }
55570         if(!result){
55571             this.proxyTop.hide();
55572             this.proxyBottom.hide();
55573         }
55574         return result ? this.dropAllowed : this.dropNotAllowed;
55575     },
55576
55577     onNodeOut : function(n, dd, e, data){
55578         this.proxyTop.hide();
55579         this.proxyBottom.hide();
55580     },
55581
55582     onNodeDrop : function(n, dd, e, data){
55583         var h = data.header;
55584         if(h != n){
55585             var cm = this.grid.colModel;
55586             var x = Roo.lib.Event.getPageX(e);
55587             var r = Roo.lib.Dom.getRegion(n.firstChild);
55588             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
55589             var oldIndex = this.view.getCellIndex(h);
55590             var newIndex = this.view.getCellIndex(n);
55591             var locked = cm.isLocked(newIndex);
55592             if(pt == "after"){
55593                 newIndex++;
55594             }
55595             if(oldIndex < newIndex){
55596                 newIndex--;
55597             }
55598             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
55599                 return false;
55600             }
55601             cm.setLocked(oldIndex, locked, true);
55602             cm.moveColumn(oldIndex, newIndex);
55603             this.grid.fireEvent("columnmove", oldIndex, newIndex);
55604             return true;
55605         }
55606         return false;
55607     }
55608 });
55609 /*
55610  * Based on:
55611  * Ext JS Library 1.1.1
55612  * Copyright(c) 2006-2007, Ext JS, LLC.
55613  *
55614  * Originally Released Under LGPL - original licence link has changed is not relivant.
55615  *
55616  * Fork - LGPL
55617  * <script type="text/javascript">
55618  */
55619   
55620 /**
55621  * @class Roo.grid.GridView
55622  * @extends Roo.util.Observable
55623  *
55624  * @constructor
55625  * @param {Object} config
55626  */
55627 Roo.grid.GridView = function(config){
55628     Roo.grid.GridView.superclass.constructor.call(this);
55629     this.el = null;
55630
55631     Roo.apply(this, config);
55632 };
55633
55634 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
55635
55636     unselectable :  'unselectable="on"',
55637     unselectableCls :  'x-unselectable',
55638     
55639     
55640     rowClass : "x-grid-row",
55641
55642     cellClass : "x-grid-col",
55643
55644     tdClass : "x-grid-td",
55645
55646     hdClass : "x-grid-hd",
55647
55648     splitClass : "x-grid-split",
55649
55650     sortClasses : ["sort-asc", "sort-desc"],
55651
55652     enableMoveAnim : false,
55653
55654     hlColor: "C3DAF9",
55655
55656     dh : Roo.DomHelper,
55657
55658     fly : Roo.Element.fly,
55659
55660     css : Roo.util.CSS,
55661
55662     borderWidth: 1,
55663
55664     splitOffset: 3,
55665
55666     scrollIncrement : 22,
55667
55668     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
55669
55670     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
55671
55672     bind : function(ds, cm){
55673         if(this.ds){
55674             this.ds.un("load", this.onLoad, this);
55675             this.ds.un("datachanged", this.onDataChange, this);
55676             this.ds.un("add", this.onAdd, this);
55677             this.ds.un("remove", this.onRemove, this);
55678             this.ds.un("update", this.onUpdate, this);
55679             this.ds.un("clear", this.onClear, this);
55680         }
55681         if(ds){
55682             ds.on("load", this.onLoad, this);
55683             ds.on("datachanged", this.onDataChange, this);
55684             ds.on("add", this.onAdd, this);
55685             ds.on("remove", this.onRemove, this);
55686             ds.on("update", this.onUpdate, this);
55687             ds.on("clear", this.onClear, this);
55688         }
55689         this.ds = ds;
55690
55691         if(this.cm){
55692             this.cm.un("widthchange", this.onColWidthChange, this);
55693             this.cm.un("headerchange", this.onHeaderChange, this);
55694             this.cm.un("hiddenchange", this.onHiddenChange, this);
55695             this.cm.un("columnmoved", this.onColumnMove, this);
55696             this.cm.un("columnlockchange", this.onColumnLock, this);
55697         }
55698         if(cm){
55699             this.generateRules(cm);
55700             cm.on("widthchange", this.onColWidthChange, this);
55701             cm.on("headerchange", this.onHeaderChange, this);
55702             cm.on("hiddenchange", this.onHiddenChange, this);
55703             cm.on("columnmoved", this.onColumnMove, this);
55704             cm.on("columnlockchange", this.onColumnLock, this);
55705         }
55706         this.cm = cm;
55707     },
55708
55709     init: function(grid){
55710         Roo.grid.GridView.superclass.init.call(this, grid);
55711
55712         this.bind(grid.dataSource, grid.colModel);
55713
55714         grid.on("headerclick", this.handleHeaderClick, this);
55715
55716         if(grid.trackMouseOver){
55717             grid.on("mouseover", this.onRowOver, this);
55718             grid.on("mouseout", this.onRowOut, this);
55719         }
55720         grid.cancelTextSelection = function(){};
55721         this.gridId = grid.id;
55722
55723         var tpls = this.templates || {};
55724
55725         if(!tpls.master){
55726             tpls.master = new Roo.Template(
55727                '<div class="x-grid" hidefocus="true">',
55728                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
55729                   '<div class="x-grid-topbar"></div>',
55730                   '<div class="x-grid-scroller"><div></div></div>',
55731                   '<div class="x-grid-locked">',
55732                       '<div class="x-grid-header">{lockedHeader}</div>',
55733                       '<div class="x-grid-body">{lockedBody}</div>',
55734                   "</div>",
55735                   '<div class="x-grid-viewport">',
55736                       '<div class="x-grid-header">{header}</div>',
55737                       '<div class="x-grid-body">{body}</div>',
55738                   "</div>",
55739                   '<div class="x-grid-bottombar"></div>',
55740                  
55741                   '<div class="x-grid-resize-proxy">&#160;</div>',
55742                "</div>"
55743             );
55744             tpls.master.disableformats = true;
55745         }
55746
55747         if(!tpls.header){
55748             tpls.header = new Roo.Template(
55749                '<table border="0" cellspacing="0" cellpadding="0">',
55750                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
55751                "</table>{splits}"
55752             );
55753             tpls.header.disableformats = true;
55754         }
55755         tpls.header.compile();
55756
55757         if(!tpls.hcell){
55758             tpls.hcell = new Roo.Template(
55759                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
55760                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
55761                 "</div></td>"
55762              );
55763              tpls.hcell.disableFormats = true;
55764         }
55765         tpls.hcell.compile();
55766
55767         if(!tpls.hsplit){
55768             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
55769                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
55770             tpls.hsplit.disableFormats = true;
55771         }
55772         tpls.hsplit.compile();
55773
55774         if(!tpls.body){
55775             tpls.body = new Roo.Template(
55776                '<table border="0" cellspacing="0" cellpadding="0">',
55777                "<tbody>{rows}</tbody>",
55778                "</table>"
55779             );
55780             tpls.body.disableFormats = true;
55781         }
55782         tpls.body.compile();
55783
55784         if(!tpls.row){
55785             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
55786             tpls.row.disableFormats = true;
55787         }
55788         tpls.row.compile();
55789
55790         if(!tpls.cell){
55791             tpls.cell = new Roo.Template(
55792                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
55793                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
55794                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
55795                 "</td>"
55796             );
55797             tpls.cell.disableFormats = true;
55798         }
55799         tpls.cell.compile();
55800
55801         this.templates = tpls;
55802     },
55803
55804     // remap these for backwards compat
55805     onColWidthChange : function(){
55806         this.updateColumns.apply(this, arguments);
55807     },
55808     onHeaderChange : function(){
55809         this.updateHeaders.apply(this, arguments);
55810     }, 
55811     onHiddenChange : function(){
55812         this.handleHiddenChange.apply(this, arguments);
55813     },
55814     onColumnMove : function(){
55815         this.handleColumnMove.apply(this, arguments);
55816     },
55817     onColumnLock : function(){
55818         this.handleLockChange.apply(this, arguments);
55819     },
55820
55821     onDataChange : function(){
55822         this.refresh();
55823         this.updateHeaderSortState();
55824     },
55825
55826     onClear : function(){
55827         this.refresh();
55828     },
55829
55830     onUpdate : function(ds, record){
55831         this.refreshRow(record);
55832     },
55833
55834     refreshRow : function(record){
55835         var ds = this.ds, index;
55836         if(typeof record == 'number'){
55837             index = record;
55838             record = ds.getAt(index);
55839         }else{
55840             index = ds.indexOf(record);
55841         }
55842         this.insertRows(ds, index, index, true);
55843         this.onRemove(ds, record, index+1, true);
55844         this.syncRowHeights(index, index);
55845         this.layout();
55846         this.fireEvent("rowupdated", this, index, record);
55847     },
55848
55849     onAdd : function(ds, records, index){
55850         this.insertRows(ds, index, index + (records.length-1));
55851     },
55852
55853     onRemove : function(ds, record, index, isUpdate){
55854         if(isUpdate !== true){
55855             this.fireEvent("beforerowremoved", this, index, record);
55856         }
55857         var bt = this.getBodyTable(), lt = this.getLockedTable();
55858         if(bt.rows[index]){
55859             bt.firstChild.removeChild(bt.rows[index]);
55860         }
55861         if(lt.rows[index]){
55862             lt.firstChild.removeChild(lt.rows[index]);
55863         }
55864         if(isUpdate !== true){
55865             this.stripeRows(index);
55866             this.syncRowHeights(index, index);
55867             this.layout();
55868             this.fireEvent("rowremoved", this, index, record);
55869         }
55870     },
55871
55872     onLoad : function(){
55873         this.scrollToTop();
55874     },
55875
55876     /**
55877      * Scrolls the grid to the top
55878      */
55879     scrollToTop : function(){
55880         if(this.scroller){
55881             this.scroller.dom.scrollTop = 0;
55882             this.syncScroll();
55883         }
55884     },
55885
55886     /**
55887      * Gets a panel in the header of the grid that can be used for toolbars etc.
55888      * After modifying the contents of this panel a call to grid.autoSize() may be
55889      * required to register any changes in size.
55890      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
55891      * @return Roo.Element
55892      */
55893     getHeaderPanel : function(doShow){
55894         if(doShow){
55895             this.headerPanel.show();
55896         }
55897         return this.headerPanel;
55898     },
55899
55900     /**
55901      * Gets a panel in the footer of the grid that can be used for toolbars etc.
55902      * After modifying the contents of this panel a call to grid.autoSize() may be
55903      * required to register any changes in size.
55904      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
55905      * @return Roo.Element
55906      */
55907     getFooterPanel : function(doShow){
55908         if(doShow){
55909             this.footerPanel.show();
55910         }
55911         return this.footerPanel;
55912     },
55913
55914     initElements : function(){
55915         var E = Roo.Element;
55916         var el = this.grid.getGridEl().dom.firstChild;
55917         var cs = el.childNodes;
55918
55919         this.el = new E(el);
55920         
55921          this.focusEl = new E(el.firstChild);
55922         this.focusEl.swallowEvent("click", true);
55923         
55924         this.headerPanel = new E(cs[1]);
55925         this.headerPanel.enableDisplayMode("block");
55926
55927         this.scroller = new E(cs[2]);
55928         this.scrollSizer = new E(this.scroller.dom.firstChild);
55929
55930         this.lockedWrap = new E(cs[3]);
55931         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
55932         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
55933
55934         this.mainWrap = new E(cs[4]);
55935         this.mainHd = new E(this.mainWrap.dom.firstChild);
55936         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
55937
55938         this.footerPanel = new E(cs[5]);
55939         this.footerPanel.enableDisplayMode("block");
55940
55941         this.resizeProxy = new E(cs[6]);
55942
55943         this.headerSelector = String.format(
55944            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
55945            this.lockedHd.id, this.mainHd.id
55946         );
55947
55948         this.splitterSelector = String.format(
55949            '#{0} div.x-grid-split, #{1} div.x-grid-split',
55950            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
55951         );
55952     },
55953     idToCssName : function(s)
55954     {
55955         return s.replace(/[^a-z0-9]+/ig, '-');
55956     },
55957
55958     getHeaderCell : function(index){
55959         return Roo.DomQuery.select(this.headerSelector)[index];
55960     },
55961
55962     getHeaderCellMeasure : function(index){
55963         return this.getHeaderCell(index).firstChild;
55964     },
55965
55966     getHeaderCellText : function(index){
55967         return this.getHeaderCell(index).firstChild.firstChild;
55968     },
55969
55970     getLockedTable : function(){
55971         return this.lockedBody.dom.firstChild;
55972     },
55973
55974     getBodyTable : function(){
55975         return this.mainBody.dom.firstChild;
55976     },
55977
55978     getLockedRow : function(index){
55979         return this.getLockedTable().rows[index];
55980     },
55981
55982     getRow : function(index){
55983         return this.getBodyTable().rows[index];
55984     },
55985
55986     getRowComposite : function(index){
55987         if(!this.rowEl){
55988             this.rowEl = new Roo.CompositeElementLite();
55989         }
55990         var els = [], lrow, mrow;
55991         if(lrow = this.getLockedRow(index)){
55992             els.push(lrow);
55993         }
55994         if(mrow = this.getRow(index)){
55995             els.push(mrow);
55996         }
55997         this.rowEl.elements = els;
55998         return this.rowEl;
55999     },
56000     /**
56001      * Gets the 'td' of the cell
56002      * 
56003      * @param {Integer} rowIndex row to select
56004      * @param {Integer} colIndex column to select
56005      * 
56006      * @return {Object} 
56007      */
56008     getCell : function(rowIndex, colIndex){
56009         var locked = this.cm.getLockedCount();
56010         var source;
56011         if(colIndex < locked){
56012             source = this.lockedBody.dom.firstChild;
56013         }else{
56014             source = this.mainBody.dom.firstChild;
56015             colIndex -= locked;
56016         }
56017         return source.rows[rowIndex].childNodes[colIndex];
56018     },
56019
56020     getCellText : function(rowIndex, colIndex){
56021         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
56022     },
56023
56024     getCellBox : function(cell){
56025         var b = this.fly(cell).getBox();
56026         if(Roo.isOpera){ // opera fails to report the Y
56027             b.y = cell.offsetTop + this.mainBody.getY();
56028         }
56029         return b;
56030     },
56031
56032     getCellIndex : function(cell){
56033         var id = String(cell.className).match(this.cellRE);
56034         if(id){
56035             return parseInt(id[1], 10);
56036         }
56037         return 0;
56038     },
56039
56040     findHeaderIndex : function(n){
56041         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
56042         return r ? this.getCellIndex(r) : false;
56043     },
56044
56045     findHeaderCell : function(n){
56046         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
56047         return r ? r : false;
56048     },
56049
56050     findRowIndex : function(n){
56051         if(!n){
56052             return false;
56053         }
56054         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
56055         return r ? r.rowIndex : false;
56056     },
56057
56058     findCellIndex : function(node){
56059         var stop = this.el.dom;
56060         while(node && node != stop){
56061             if(this.findRE.test(node.className)){
56062                 return this.getCellIndex(node);
56063             }
56064             node = node.parentNode;
56065         }
56066         return false;
56067     },
56068
56069     getColumnId : function(index){
56070         return this.cm.getColumnId(index);
56071     },
56072
56073     getSplitters : function()
56074     {
56075         if(this.splitterSelector){
56076            return Roo.DomQuery.select(this.splitterSelector);
56077         }else{
56078             return null;
56079       }
56080     },
56081
56082     getSplitter : function(index){
56083         return this.getSplitters()[index];
56084     },
56085
56086     onRowOver : function(e, t){
56087         var row;
56088         if((row = this.findRowIndex(t)) !== false){
56089             this.getRowComposite(row).addClass("x-grid-row-over");
56090         }
56091     },
56092
56093     onRowOut : function(e, t){
56094         var row;
56095         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
56096             this.getRowComposite(row).removeClass("x-grid-row-over");
56097         }
56098     },
56099
56100     renderHeaders : function(){
56101         var cm = this.cm;
56102         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
56103         var cb = [], lb = [], sb = [], lsb = [], p = {};
56104         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56105             p.cellId = "x-grid-hd-0-" + i;
56106             p.splitId = "x-grid-csplit-0-" + i;
56107             p.id = cm.getColumnId(i);
56108             p.value = cm.getColumnHeader(i) || "";
56109             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
56110             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
56111             if(!cm.isLocked(i)){
56112                 cb[cb.length] = ct.apply(p);
56113                 sb[sb.length] = st.apply(p);
56114             }else{
56115                 lb[lb.length] = ct.apply(p);
56116                 lsb[lsb.length] = st.apply(p);
56117             }
56118         }
56119         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
56120                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
56121     },
56122
56123     updateHeaders : function(){
56124         var html = this.renderHeaders();
56125         this.lockedHd.update(html[0]);
56126         this.mainHd.update(html[1]);
56127     },
56128
56129     /**
56130      * Focuses the specified row.
56131      * @param {Number} row The row index
56132      */
56133     focusRow : function(row)
56134     {
56135         //Roo.log('GridView.focusRow');
56136         var x = this.scroller.dom.scrollLeft;
56137         this.focusCell(row, 0, false);
56138         this.scroller.dom.scrollLeft = x;
56139     },
56140
56141     /**
56142      * Focuses the specified cell.
56143      * @param {Number} row The row index
56144      * @param {Number} col The column index
56145      * @param {Boolean} hscroll false to disable horizontal scrolling
56146      */
56147     focusCell : function(row, col, hscroll)
56148     {
56149         //Roo.log('GridView.focusCell');
56150         var el = this.ensureVisible(row, col, hscroll);
56151         this.focusEl.alignTo(el, "tl-tl");
56152         if(Roo.isGecko){
56153             this.focusEl.focus();
56154         }else{
56155             this.focusEl.focus.defer(1, this.focusEl);
56156         }
56157     },
56158
56159     /**
56160      * Scrolls the specified cell into view
56161      * @param {Number} row The row index
56162      * @param {Number} col The column index
56163      * @param {Boolean} hscroll false to disable horizontal scrolling
56164      */
56165     ensureVisible : function(row, col, hscroll)
56166     {
56167         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
56168         //return null; //disable for testing.
56169         if(typeof row != "number"){
56170             row = row.rowIndex;
56171         }
56172         if(row < 0 && row >= this.ds.getCount()){
56173             return  null;
56174         }
56175         col = (col !== undefined ? col : 0);
56176         var cm = this.grid.colModel;
56177         while(cm.isHidden(col)){
56178             col++;
56179         }
56180
56181         var el = this.getCell(row, col);
56182         if(!el){
56183             return null;
56184         }
56185         var c = this.scroller.dom;
56186
56187         var ctop = parseInt(el.offsetTop, 10);
56188         var cleft = parseInt(el.offsetLeft, 10);
56189         var cbot = ctop + el.offsetHeight;
56190         var cright = cleft + el.offsetWidth;
56191         
56192         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
56193         var stop = parseInt(c.scrollTop, 10);
56194         var sleft = parseInt(c.scrollLeft, 10);
56195         var sbot = stop + ch;
56196         var sright = sleft + c.clientWidth;
56197         /*
56198         Roo.log('GridView.ensureVisible:' +
56199                 ' ctop:' + ctop +
56200                 ' c.clientHeight:' + c.clientHeight +
56201                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
56202                 ' stop:' + stop +
56203                 ' cbot:' + cbot +
56204                 ' sbot:' + sbot +
56205                 ' ch:' + ch  
56206                 );
56207         */
56208         if(ctop < stop){
56209              c.scrollTop = ctop;
56210             //Roo.log("set scrolltop to ctop DISABLE?");
56211         }else if(cbot > sbot){
56212             //Roo.log("set scrolltop to cbot-ch");
56213             c.scrollTop = cbot-ch;
56214         }
56215         
56216         if(hscroll !== false){
56217             if(cleft < sleft){
56218                 c.scrollLeft = cleft;
56219             }else if(cright > sright){
56220                 c.scrollLeft = cright-c.clientWidth;
56221             }
56222         }
56223          
56224         return el;
56225     },
56226
56227     updateColumns : function(){
56228         this.grid.stopEditing();
56229         var cm = this.grid.colModel, colIds = this.getColumnIds();
56230         //var totalWidth = cm.getTotalWidth();
56231         var pos = 0;
56232         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56233             //if(cm.isHidden(i)) continue;
56234             var w = cm.getColumnWidth(i);
56235             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
56236             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
56237         }
56238         this.updateSplitters();
56239     },
56240
56241     generateRules : function(cm){
56242         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
56243         Roo.util.CSS.removeStyleSheet(rulesId);
56244         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56245             var cid = cm.getColumnId(i);
56246             var align = '';
56247             if(cm.config[i].align){
56248                 align = 'text-align:'+cm.config[i].align+';';
56249             }
56250             var hidden = '';
56251             if(cm.isHidden(i)){
56252                 hidden = 'display:none;';
56253             }
56254             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
56255             ruleBuf.push(
56256                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
56257                     this.hdSelector, cid, " {\n", align, width, "}\n",
56258                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
56259                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
56260         }
56261         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
56262     },
56263
56264     updateSplitters : function(){
56265         var cm = this.cm, s = this.getSplitters();
56266         if(s){ // splitters not created yet
56267             var pos = 0, locked = true;
56268             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56269                 if(cm.isHidden(i)) {
56270                     continue;
56271                 }
56272                 var w = cm.getColumnWidth(i); // make sure it's a number
56273                 if(!cm.isLocked(i) && locked){
56274                     pos = 0;
56275                     locked = false;
56276                 }
56277                 pos += w;
56278                 s[i].style.left = (pos-this.splitOffset) + "px";
56279             }
56280         }
56281     },
56282
56283     handleHiddenChange : function(colModel, colIndex, hidden){
56284         if(hidden){
56285             this.hideColumn(colIndex);
56286         }else{
56287             this.unhideColumn(colIndex);
56288         }
56289     },
56290
56291     hideColumn : function(colIndex){
56292         var cid = this.getColumnId(colIndex);
56293         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
56294         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
56295         if(Roo.isSafari){
56296             this.updateHeaders();
56297         }
56298         this.updateSplitters();
56299         this.layout();
56300     },
56301
56302     unhideColumn : function(colIndex){
56303         var cid = this.getColumnId(colIndex);
56304         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
56305         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
56306
56307         if(Roo.isSafari){
56308             this.updateHeaders();
56309         }
56310         this.updateSplitters();
56311         this.layout();
56312     },
56313
56314     insertRows : function(dm, firstRow, lastRow, isUpdate){
56315         if(firstRow == 0 && lastRow == dm.getCount()-1){
56316             this.refresh();
56317         }else{
56318             if(!isUpdate){
56319                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
56320             }
56321             var s = this.getScrollState();
56322             var markup = this.renderRows(firstRow, lastRow);
56323             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
56324             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
56325             this.restoreScroll(s);
56326             if(!isUpdate){
56327                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
56328                 this.syncRowHeights(firstRow, lastRow);
56329                 this.stripeRows(firstRow);
56330                 this.layout();
56331             }
56332         }
56333     },
56334
56335     bufferRows : function(markup, target, index){
56336         var before = null, trows = target.rows, tbody = target.tBodies[0];
56337         if(index < trows.length){
56338             before = trows[index];
56339         }
56340         var b = document.createElement("div");
56341         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
56342         var rows = b.firstChild.rows;
56343         for(var i = 0, len = rows.length; i < len; i++){
56344             if(before){
56345                 tbody.insertBefore(rows[0], before);
56346             }else{
56347                 tbody.appendChild(rows[0]);
56348             }
56349         }
56350         b.innerHTML = "";
56351         b = null;
56352     },
56353
56354     deleteRows : function(dm, firstRow, lastRow){
56355         if(dm.getRowCount()<1){
56356             this.fireEvent("beforerefresh", this);
56357             this.mainBody.update("");
56358             this.lockedBody.update("");
56359             this.fireEvent("refresh", this);
56360         }else{
56361             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
56362             var bt = this.getBodyTable();
56363             var tbody = bt.firstChild;
56364             var rows = bt.rows;
56365             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
56366                 tbody.removeChild(rows[firstRow]);
56367             }
56368             this.stripeRows(firstRow);
56369             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
56370         }
56371     },
56372
56373     updateRows : function(dataSource, firstRow, lastRow){
56374         var s = this.getScrollState();
56375         this.refresh();
56376         this.restoreScroll(s);
56377     },
56378
56379     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
56380         if(!noRefresh){
56381            this.refresh();
56382         }
56383         this.updateHeaderSortState();
56384     },
56385
56386     getScrollState : function(){
56387         
56388         var sb = this.scroller.dom;
56389         return {left: sb.scrollLeft, top: sb.scrollTop};
56390     },
56391
56392     stripeRows : function(startRow){
56393         if(!this.grid.stripeRows || this.ds.getCount() < 1){
56394             return;
56395         }
56396         startRow = startRow || 0;
56397         var rows = this.getBodyTable().rows;
56398         var lrows = this.getLockedTable().rows;
56399         var cls = ' x-grid-row-alt ';
56400         for(var i = startRow, len = rows.length; i < len; i++){
56401             var row = rows[i], lrow = lrows[i];
56402             var isAlt = ((i+1) % 2 == 0);
56403             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
56404             if(isAlt == hasAlt){
56405                 continue;
56406             }
56407             if(isAlt){
56408                 row.className += " x-grid-row-alt";
56409             }else{
56410                 row.className = row.className.replace("x-grid-row-alt", "");
56411             }
56412             if(lrow){
56413                 lrow.className = row.className;
56414             }
56415         }
56416     },
56417
56418     restoreScroll : function(state){
56419         //Roo.log('GridView.restoreScroll');
56420         var sb = this.scroller.dom;
56421         sb.scrollLeft = state.left;
56422         sb.scrollTop = state.top;
56423         this.syncScroll();
56424     },
56425
56426     syncScroll : function(){
56427         //Roo.log('GridView.syncScroll');
56428         var sb = this.scroller.dom;
56429         var sh = this.mainHd.dom;
56430         var bs = this.mainBody.dom;
56431         var lv = this.lockedBody.dom;
56432         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
56433         lv.scrollTop = bs.scrollTop = sb.scrollTop;
56434     },
56435
56436     handleScroll : function(e){
56437         this.syncScroll();
56438         var sb = this.scroller.dom;
56439         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
56440         e.stopEvent();
56441     },
56442
56443     handleWheel : function(e){
56444         var d = e.getWheelDelta();
56445         this.scroller.dom.scrollTop -= d*22;
56446         // set this here to prevent jumpy scrolling on large tables
56447         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
56448         e.stopEvent();
56449     },
56450
56451     renderRows : function(startRow, endRow){
56452         // pull in all the crap needed to render rows
56453         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
56454         var colCount = cm.getColumnCount();
56455
56456         if(ds.getCount() < 1){
56457             return ["", ""];
56458         }
56459
56460         // build a map for all the columns
56461         var cs = [];
56462         for(var i = 0; i < colCount; i++){
56463             var name = cm.getDataIndex(i);
56464             cs[i] = {
56465                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
56466                 renderer : cm.getRenderer(i),
56467                 id : cm.getColumnId(i),
56468                 locked : cm.isLocked(i),
56469                 has_editor : cm.isCellEditable(i)
56470             };
56471         }
56472
56473         startRow = startRow || 0;
56474         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
56475
56476         // records to render
56477         var rs = ds.getRange(startRow, endRow);
56478
56479         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
56480     },
56481
56482     // As much as I hate to duplicate code, this was branched because FireFox really hates
56483     // [].join("") on strings. The performance difference was substantial enough to
56484     // branch this function
56485     doRender : Roo.isGecko ?
56486             function(cs, rs, ds, startRow, colCount, stripe){
56487                 var ts = this.templates, ct = ts.cell, rt = ts.row;
56488                 // buffers
56489                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
56490                 
56491                 var hasListener = this.grid.hasListener('rowclass');
56492                 var rowcfg = {};
56493                 for(var j = 0, len = rs.length; j < len; j++){
56494                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
56495                     for(var i = 0; i < colCount; i++){
56496                         c = cs[i];
56497                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
56498                         p.id = c.id;
56499                         p.css = p.attr = "";
56500                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
56501                         if(p.value == undefined || p.value === "") {
56502                             p.value = "&#160;";
56503                         }
56504                         if(c.has_editor){
56505                             p.css += ' x-grid-editable-cell';
56506                         }
56507                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
56508                             p.css +=  ' x-grid-dirty-cell';
56509                         }
56510                         var markup = ct.apply(p);
56511                         if(!c.locked){
56512                             cb+= markup;
56513                         }else{
56514                             lcb+= markup;
56515                         }
56516                     }
56517                     var alt = [];
56518                     if(stripe && ((rowIndex+1) % 2 == 0)){
56519                         alt.push("x-grid-row-alt")
56520                     }
56521                     if(r.dirty){
56522                         alt.push(  " x-grid-dirty-row");
56523                     }
56524                     rp.cells = lcb;
56525                     if(this.getRowClass){
56526                         alt.push(this.getRowClass(r, rowIndex));
56527                     }
56528                     if (hasListener) {
56529                         rowcfg = {
56530                              
56531                             record: r,
56532                             rowIndex : rowIndex,
56533                             rowClass : ''
56534                         };
56535                         this.grid.fireEvent('rowclass', this, rowcfg);
56536                         alt.push(rowcfg.rowClass);
56537                     }
56538                     rp.alt = alt.join(" ");
56539                     lbuf+= rt.apply(rp);
56540                     rp.cells = cb;
56541                     buf+=  rt.apply(rp);
56542                 }
56543                 return [lbuf, buf];
56544             } :
56545             function(cs, rs, ds, startRow, colCount, stripe){
56546                 var ts = this.templates, ct = ts.cell, rt = ts.row;
56547                 // buffers
56548                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
56549                 var hasListener = this.grid.hasListener('rowclass');
56550  
56551                 var rowcfg = {};
56552                 for(var j = 0, len = rs.length; j < len; j++){
56553                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
56554                     for(var i = 0; i < colCount; i++){
56555                         c = cs[i];
56556                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
56557                         p.id = c.id;
56558                         p.css = p.attr = "";
56559                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
56560                         if(p.value == undefined || p.value === "") {
56561                             p.value = "&#160;";
56562                         }
56563                         //Roo.log(c);
56564                          if(c.has_editor){
56565                             p.css += ' x-grid-editable-cell';
56566                         }
56567                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
56568                             p.css += ' x-grid-dirty-cell' 
56569                         }
56570                         
56571                         var markup = ct.apply(p);
56572                         if(!c.locked){
56573                             cb[cb.length] = markup;
56574                         }else{
56575                             lcb[lcb.length] = markup;
56576                         }
56577                     }
56578                     var alt = [];
56579                     if(stripe && ((rowIndex+1) % 2 == 0)){
56580                         alt.push( "x-grid-row-alt");
56581                     }
56582                     if(r.dirty){
56583                         alt.push(" x-grid-dirty-row");
56584                     }
56585                     rp.cells = lcb;
56586                     if(this.getRowClass){
56587                         alt.push( this.getRowClass(r, rowIndex));
56588                     }
56589                     if (hasListener) {
56590                         rowcfg = {
56591                              
56592                             record: r,
56593                             rowIndex : rowIndex,
56594                             rowClass : ''
56595                         };
56596                         this.grid.fireEvent('rowclass', this, rowcfg);
56597                         alt.push(rowcfg.rowClass);
56598                     }
56599                     
56600                     rp.alt = alt.join(" ");
56601                     rp.cells = lcb.join("");
56602                     lbuf[lbuf.length] = rt.apply(rp);
56603                     rp.cells = cb.join("");
56604                     buf[buf.length] =  rt.apply(rp);
56605                 }
56606                 return [lbuf.join(""), buf.join("")];
56607             },
56608
56609     renderBody : function(){
56610         var markup = this.renderRows();
56611         var bt = this.templates.body;
56612         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
56613     },
56614
56615     /**
56616      * Refreshes the grid
56617      * @param {Boolean} headersToo
56618      */
56619     refresh : function(headersToo){
56620         this.fireEvent("beforerefresh", this);
56621         this.grid.stopEditing();
56622         var result = this.renderBody();
56623         this.lockedBody.update(result[0]);
56624         this.mainBody.update(result[1]);
56625         if(headersToo === true){
56626             this.updateHeaders();
56627             this.updateColumns();
56628             this.updateSplitters();
56629             this.updateHeaderSortState();
56630         }
56631         this.syncRowHeights();
56632         this.layout();
56633         this.fireEvent("refresh", this);
56634     },
56635
56636     handleColumnMove : function(cm, oldIndex, newIndex){
56637         this.indexMap = null;
56638         var s = this.getScrollState();
56639         this.refresh(true);
56640         this.restoreScroll(s);
56641         this.afterMove(newIndex);
56642     },
56643
56644     afterMove : function(colIndex){
56645         if(this.enableMoveAnim && Roo.enableFx){
56646             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
56647         }
56648         // if multisort - fix sortOrder, and reload..
56649         if (this.grid.dataSource.multiSort) {
56650             // the we can call sort again..
56651             var dm = this.grid.dataSource;
56652             var cm = this.grid.colModel;
56653             var so = [];
56654             for(var i = 0; i < cm.config.length; i++ ) {
56655                 
56656                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
56657                     continue; // dont' bother, it's not in sort list or being set.
56658                 }
56659                 
56660                 so.push(cm.config[i].dataIndex);
56661             };
56662             dm.sortOrder = so;
56663             dm.load(dm.lastOptions);
56664             
56665             
56666         }
56667         
56668     },
56669
56670     updateCell : function(dm, rowIndex, dataIndex){
56671         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
56672         if(typeof colIndex == "undefined"){ // not present in grid
56673             return;
56674         }
56675         var cm = this.grid.colModel;
56676         var cell = this.getCell(rowIndex, colIndex);
56677         var cellText = this.getCellText(rowIndex, colIndex);
56678
56679         var p = {
56680             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
56681             id : cm.getColumnId(colIndex),
56682             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
56683         };
56684         var renderer = cm.getRenderer(colIndex);
56685         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
56686         if(typeof val == "undefined" || val === "") {
56687             val = "&#160;";
56688         }
56689         cellText.innerHTML = val;
56690         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
56691         this.syncRowHeights(rowIndex, rowIndex);
56692     },
56693
56694     calcColumnWidth : function(colIndex, maxRowsToMeasure){
56695         var maxWidth = 0;
56696         if(this.grid.autoSizeHeaders){
56697             var h = this.getHeaderCellMeasure(colIndex);
56698             maxWidth = Math.max(maxWidth, h.scrollWidth);
56699         }
56700         var tb, index;
56701         if(this.cm.isLocked(colIndex)){
56702             tb = this.getLockedTable();
56703             index = colIndex;
56704         }else{
56705             tb = this.getBodyTable();
56706             index = colIndex - this.cm.getLockedCount();
56707         }
56708         if(tb && tb.rows){
56709             var rows = tb.rows;
56710             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
56711             for(var i = 0; i < stopIndex; i++){
56712                 var cell = rows[i].childNodes[index].firstChild;
56713                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
56714             }
56715         }
56716         return maxWidth + /*margin for error in IE*/ 5;
56717     },
56718     /**
56719      * Autofit a column to its content.
56720      * @param {Number} colIndex
56721      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
56722      */
56723      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
56724          if(this.cm.isHidden(colIndex)){
56725              return; // can't calc a hidden column
56726          }
56727         if(forceMinSize){
56728             var cid = this.cm.getColumnId(colIndex);
56729             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
56730            if(this.grid.autoSizeHeaders){
56731                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
56732            }
56733         }
56734         var newWidth = this.calcColumnWidth(colIndex);
56735         this.cm.setColumnWidth(colIndex,
56736             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
56737         if(!suppressEvent){
56738             this.grid.fireEvent("columnresize", colIndex, newWidth);
56739         }
56740     },
56741
56742     /**
56743      * Autofits all columns to their content and then expands to fit any extra space in the grid
56744      */
56745      autoSizeColumns : function(){
56746         var cm = this.grid.colModel;
56747         var colCount = cm.getColumnCount();
56748         for(var i = 0; i < colCount; i++){
56749             this.autoSizeColumn(i, true, true);
56750         }
56751         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
56752             this.fitColumns();
56753         }else{
56754             this.updateColumns();
56755             this.layout();
56756         }
56757     },
56758
56759     /**
56760      * Autofits all columns to the grid's width proportionate with their current size
56761      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
56762      */
56763     fitColumns : function(reserveScrollSpace){
56764         var cm = this.grid.colModel;
56765         var colCount = cm.getColumnCount();
56766         var cols = [];
56767         var width = 0;
56768         var i, w;
56769         for (i = 0; i < colCount; i++){
56770             if(!cm.isHidden(i) && !cm.isFixed(i)){
56771                 w = cm.getColumnWidth(i);
56772                 cols.push(i);
56773                 cols.push(w);
56774                 width += w;
56775             }
56776         }
56777         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
56778         if(reserveScrollSpace){
56779             avail -= 17;
56780         }
56781         var frac = (avail - cm.getTotalWidth())/width;
56782         while (cols.length){
56783             w = cols.pop();
56784             i = cols.pop();
56785             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
56786         }
56787         this.updateColumns();
56788         this.layout();
56789     },
56790
56791     onRowSelect : function(rowIndex){
56792         var row = this.getRowComposite(rowIndex);
56793         row.addClass("x-grid-row-selected");
56794     },
56795
56796     onRowDeselect : function(rowIndex){
56797         var row = this.getRowComposite(rowIndex);
56798         row.removeClass("x-grid-row-selected");
56799     },
56800
56801     onCellSelect : function(row, col){
56802         var cell = this.getCell(row, col);
56803         if(cell){
56804             Roo.fly(cell).addClass("x-grid-cell-selected");
56805         }
56806     },
56807
56808     onCellDeselect : function(row, col){
56809         var cell = this.getCell(row, col);
56810         if(cell){
56811             Roo.fly(cell).removeClass("x-grid-cell-selected");
56812         }
56813     },
56814
56815     updateHeaderSortState : function(){
56816         
56817         // sort state can be single { field: xxx, direction : yyy}
56818         // or   { xxx=>ASC , yyy : DESC ..... }
56819         
56820         var mstate = {};
56821         if (!this.ds.multiSort) { 
56822             var state = this.ds.getSortState();
56823             if(!state){
56824                 return;
56825             }
56826             mstate[state.field] = state.direction;
56827             // FIXME... - this is not used here.. but might be elsewhere..
56828             this.sortState = state;
56829             
56830         } else {
56831             mstate = this.ds.sortToggle;
56832         }
56833         //remove existing sort classes..
56834         
56835         var sc = this.sortClasses;
56836         var hds = this.el.select(this.headerSelector).removeClass(sc);
56837         
56838         for(var f in mstate) {
56839         
56840             var sortColumn = this.cm.findColumnIndex(f);
56841             
56842             if(sortColumn != -1){
56843                 var sortDir = mstate[f];        
56844                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
56845             }
56846         }
56847         
56848          
56849         
56850     },
56851
56852
56853     handleHeaderClick : function(g, index,e){
56854         
56855         Roo.log("header click");
56856         
56857         if (Roo.isTouch) {
56858             // touch events on header are handled by context
56859             this.handleHdCtx(g,index,e);
56860             return;
56861         }
56862         
56863         
56864         if(this.headersDisabled){
56865             return;
56866         }
56867         var dm = g.dataSource, cm = g.colModel;
56868         if(!cm.isSortable(index)){
56869             return;
56870         }
56871         g.stopEditing();
56872         
56873         if (dm.multiSort) {
56874             // update the sortOrder
56875             var so = [];
56876             for(var i = 0; i < cm.config.length; i++ ) {
56877                 
56878                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
56879                     continue; // dont' bother, it's not in sort list or being set.
56880                 }
56881                 
56882                 so.push(cm.config[i].dataIndex);
56883             };
56884             dm.sortOrder = so;
56885         }
56886         
56887         
56888         dm.sort(cm.getDataIndex(index));
56889     },
56890
56891
56892     destroy : function(){
56893         if(this.colMenu){
56894             this.colMenu.removeAll();
56895             Roo.menu.MenuMgr.unregister(this.colMenu);
56896             this.colMenu.getEl().remove();
56897             delete this.colMenu;
56898         }
56899         if(this.hmenu){
56900             this.hmenu.removeAll();
56901             Roo.menu.MenuMgr.unregister(this.hmenu);
56902             this.hmenu.getEl().remove();
56903             delete this.hmenu;
56904         }
56905         if(this.grid.enableColumnMove){
56906             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
56907             if(dds){
56908                 for(var dd in dds){
56909                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
56910                         var elid = dds[dd].dragElId;
56911                         dds[dd].unreg();
56912                         Roo.get(elid).remove();
56913                     } else if(dds[dd].config.isTarget){
56914                         dds[dd].proxyTop.remove();
56915                         dds[dd].proxyBottom.remove();
56916                         dds[dd].unreg();
56917                     }
56918                     if(Roo.dd.DDM.locationCache[dd]){
56919                         delete Roo.dd.DDM.locationCache[dd];
56920                     }
56921                 }
56922                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
56923             }
56924         }
56925         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
56926         this.bind(null, null);
56927         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
56928     },
56929
56930     handleLockChange : function(){
56931         this.refresh(true);
56932     },
56933
56934     onDenyColumnLock : function(){
56935
56936     },
56937
56938     onDenyColumnHide : function(){
56939
56940     },
56941
56942     handleHdMenuClick : function(item){
56943         var index = this.hdCtxIndex;
56944         var cm = this.cm, ds = this.ds;
56945         switch(item.id){
56946             case "asc":
56947                 ds.sort(cm.getDataIndex(index), "ASC");
56948                 break;
56949             case "desc":
56950                 ds.sort(cm.getDataIndex(index), "DESC");
56951                 break;
56952             case "lock":
56953                 var lc = cm.getLockedCount();
56954                 if(cm.getColumnCount(true) <= lc+1){
56955                     this.onDenyColumnLock();
56956                     return;
56957                 }
56958                 if(lc != index){
56959                     cm.setLocked(index, true, true);
56960                     cm.moveColumn(index, lc);
56961                     this.grid.fireEvent("columnmove", index, lc);
56962                 }else{
56963                     cm.setLocked(index, true);
56964                 }
56965             break;
56966             case "unlock":
56967                 var lc = cm.getLockedCount();
56968                 if((lc-1) != index){
56969                     cm.setLocked(index, false, true);
56970                     cm.moveColumn(index, lc-1);
56971                     this.grid.fireEvent("columnmove", index, lc-1);
56972                 }else{
56973                     cm.setLocked(index, false);
56974                 }
56975             break;
56976             case 'wider': // used to expand cols on touch..
56977             case 'narrow':
56978                 var cw = cm.getColumnWidth(index);
56979                 cw += (item.id == 'wider' ? 1 : -1) * 50;
56980                 cw = Math.max(0, cw);
56981                 cw = Math.min(cw,4000);
56982                 cm.setColumnWidth(index, cw);
56983                 break;
56984                 
56985             default:
56986                 index = cm.getIndexById(item.id.substr(4));
56987                 if(index != -1){
56988                     if(item.checked && cm.getColumnCount(true) <= 1){
56989                         this.onDenyColumnHide();
56990                         return false;
56991                     }
56992                     cm.setHidden(index, item.checked);
56993                 }
56994         }
56995         return true;
56996     },
56997
56998     beforeColMenuShow : function(){
56999         var cm = this.cm,  colCount = cm.getColumnCount();
57000         this.colMenu.removeAll();
57001         for(var i = 0; i < colCount; i++){
57002             this.colMenu.add(new Roo.menu.CheckItem({
57003                 id: "col-"+cm.getColumnId(i),
57004                 text: cm.getColumnHeader(i),
57005                 checked: !cm.isHidden(i),
57006                 hideOnClick:false
57007             }));
57008         }
57009     },
57010
57011     handleHdCtx : function(g, index, e){
57012         e.stopEvent();
57013         var hd = this.getHeaderCell(index);
57014         this.hdCtxIndex = index;
57015         var ms = this.hmenu.items, cm = this.cm;
57016         ms.get("asc").setDisabled(!cm.isSortable(index));
57017         ms.get("desc").setDisabled(!cm.isSortable(index));
57018         if(this.grid.enableColLock !== false){
57019             ms.get("lock").setDisabled(cm.isLocked(index));
57020             ms.get("unlock").setDisabled(!cm.isLocked(index));
57021         }
57022         this.hmenu.show(hd, "tl-bl");
57023     },
57024
57025     handleHdOver : function(e){
57026         var hd = this.findHeaderCell(e.getTarget());
57027         if(hd && !this.headersDisabled){
57028             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
57029                this.fly(hd).addClass("x-grid-hd-over");
57030             }
57031         }
57032     },
57033
57034     handleHdOut : function(e){
57035         var hd = this.findHeaderCell(e.getTarget());
57036         if(hd){
57037             this.fly(hd).removeClass("x-grid-hd-over");
57038         }
57039     },
57040
57041     handleSplitDblClick : function(e, t){
57042         var i = this.getCellIndex(t);
57043         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
57044             this.autoSizeColumn(i, true);
57045             this.layout();
57046         }
57047     },
57048
57049     render : function(){
57050
57051         var cm = this.cm;
57052         var colCount = cm.getColumnCount();
57053
57054         if(this.grid.monitorWindowResize === true){
57055             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
57056         }
57057         var header = this.renderHeaders();
57058         var body = this.templates.body.apply({rows:""});
57059         var html = this.templates.master.apply({
57060             lockedBody: body,
57061             body: body,
57062             lockedHeader: header[0],
57063             header: header[1]
57064         });
57065
57066         //this.updateColumns();
57067
57068         this.grid.getGridEl().dom.innerHTML = html;
57069
57070         this.initElements();
57071         
57072         // a kludge to fix the random scolling effect in webkit
57073         this.el.on("scroll", function() {
57074             this.el.dom.scrollTop=0; // hopefully not recursive..
57075         },this);
57076
57077         this.scroller.on("scroll", this.handleScroll, this);
57078         this.lockedBody.on("mousewheel", this.handleWheel, this);
57079         this.mainBody.on("mousewheel", this.handleWheel, this);
57080
57081         this.mainHd.on("mouseover", this.handleHdOver, this);
57082         this.mainHd.on("mouseout", this.handleHdOut, this);
57083         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
57084                 {delegate: "."+this.splitClass});
57085
57086         this.lockedHd.on("mouseover", this.handleHdOver, this);
57087         this.lockedHd.on("mouseout", this.handleHdOut, this);
57088         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
57089                 {delegate: "."+this.splitClass});
57090
57091         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
57092             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57093         }
57094
57095         this.updateSplitters();
57096
57097         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
57098             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57099             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57100         }
57101
57102         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
57103             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
57104             this.hmenu.add(
57105                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
57106                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
57107             );
57108             if(this.grid.enableColLock !== false){
57109                 this.hmenu.add('-',
57110                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
57111                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
57112                 );
57113             }
57114             if (Roo.isTouch) {
57115                  this.hmenu.add('-',
57116                     {id:"wider", text: this.columnsWiderText},
57117                     {id:"narrow", text: this.columnsNarrowText }
57118                 );
57119                 
57120                  
57121             }
57122             
57123             if(this.grid.enableColumnHide !== false){
57124
57125                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
57126                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
57127                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
57128
57129                 this.hmenu.add('-',
57130                     {id:"columns", text: this.columnsText, menu: this.colMenu}
57131                 );
57132             }
57133             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
57134
57135             this.grid.on("headercontextmenu", this.handleHdCtx, this);
57136         }
57137
57138         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
57139             this.dd = new Roo.grid.GridDragZone(this.grid, {
57140                 ddGroup : this.grid.ddGroup || 'GridDD'
57141             });
57142             
57143         }
57144
57145         /*
57146         for(var i = 0; i < colCount; i++){
57147             if(cm.isHidden(i)){
57148                 this.hideColumn(i);
57149             }
57150             if(cm.config[i].align){
57151                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
57152                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
57153             }
57154         }*/
57155         
57156         this.updateHeaderSortState();
57157
57158         this.beforeInitialResize();
57159         this.layout(true);
57160
57161         // two part rendering gives faster view to the user
57162         this.renderPhase2.defer(1, this);
57163     },
57164
57165     renderPhase2 : function(){
57166         // render the rows now
57167         this.refresh();
57168         if(this.grid.autoSizeColumns){
57169             this.autoSizeColumns();
57170         }
57171     },
57172
57173     beforeInitialResize : function(){
57174
57175     },
57176
57177     onColumnSplitterMoved : function(i, w){
57178         this.userResized = true;
57179         var cm = this.grid.colModel;
57180         cm.setColumnWidth(i, w, true);
57181         var cid = cm.getColumnId(i);
57182         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
57183         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
57184         this.updateSplitters();
57185         this.layout();
57186         this.grid.fireEvent("columnresize", i, w);
57187     },
57188
57189     syncRowHeights : function(startIndex, endIndex){
57190         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
57191             startIndex = startIndex || 0;
57192             var mrows = this.getBodyTable().rows;
57193             var lrows = this.getLockedTable().rows;
57194             var len = mrows.length-1;
57195             endIndex = Math.min(endIndex || len, len);
57196             for(var i = startIndex; i <= endIndex; i++){
57197                 var m = mrows[i], l = lrows[i];
57198                 var h = Math.max(m.offsetHeight, l.offsetHeight);
57199                 m.style.height = l.style.height = h + "px";
57200             }
57201         }
57202     },
57203
57204     layout : function(initialRender, is2ndPass){
57205         var g = this.grid;
57206         var auto = g.autoHeight;
57207         var scrollOffset = 16;
57208         var c = g.getGridEl(), cm = this.cm,
57209                 expandCol = g.autoExpandColumn,
57210                 gv = this;
57211         //c.beginMeasure();
57212
57213         if(!c.dom.offsetWidth){ // display:none?
57214             if(initialRender){
57215                 this.lockedWrap.show();
57216                 this.mainWrap.show();
57217             }
57218             return;
57219         }
57220
57221         var hasLock = this.cm.isLocked(0);
57222
57223         var tbh = this.headerPanel.getHeight();
57224         var bbh = this.footerPanel.getHeight();
57225
57226         if(auto){
57227             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
57228             var newHeight = ch + c.getBorderWidth("tb");
57229             if(g.maxHeight){
57230                 newHeight = Math.min(g.maxHeight, newHeight);
57231             }
57232             c.setHeight(newHeight);
57233         }
57234
57235         if(g.autoWidth){
57236             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
57237         }
57238
57239         var s = this.scroller;
57240
57241         var csize = c.getSize(true);
57242
57243         this.el.setSize(csize.width, csize.height);
57244
57245         this.headerPanel.setWidth(csize.width);
57246         this.footerPanel.setWidth(csize.width);
57247
57248         var hdHeight = this.mainHd.getHeight();
57249         var vw = csize.width;
57250         var vh = csize.height - (tbh + bbh);
57251
57252         s.setSize(vw, vh);
57253
57254         var bt = this.getBodyTable();
57255         
57256         if(cm.getLockedCount() == cm.config.length){
57257             bt = this.getLockedTable();
57258         }
57259         
57260         var ltWidth = hasLock ?
57261                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
57262
57263         var scrollHeight = bt.offsetHeight;
57264         var scrollWidth = ltWidth + bt.offsetWidth;
57265         var vscroll = false, hscroll = false;
57266
57267         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
57268
57269         var lw = this.lockedWrap, mw = this.mainWrap;
57270         var lb = this.lockedBody, mb = this.mainBody;
57271
57272         setTimeout(function(){
57273             var t = s.dom.offsetTop;
57274             var w = s.dom.clientWidth,
57275                 h = s.dom.clientHeight;
57276
57277             lw.setTop(t);
57278             lw.setSize(ltWidth, h);
57279
57280             mw.setLeftTop(ltWidth, t);
57281             mw.setSize(w-ltWidth, h);
57282
57283             lb.setHeight(h-hdHeight);
57284             mb.setHeight(h-hdHeight);
57285
57286             if(is2ndPass !== true && !gv.userResized && expandCol){
57287                 // high speed resize without full column calculation
57288                 
57289                 var ci = cm.getIndexById(expandCol);
57290                 if (ci < 0) {
57291                     ci = cm.findColumnIndex(expandCol);
57292                 }
57293                 ci = Math.max(0, ci); // make sure it's got at least the first col.
57294                 var expandId = cm.getColumnId(ci);
57295                 var  tw = cm.getTotalWidth(false);
57296                 var currentWidth = cm.getColumnWidth(ci);
57297                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
57298                 if(currentWidth != cw){
57299                     cm.setColumnWidth(ci, cw, true);
57300                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
57301                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
57302                     gv.updateSplitters();
57303                     gv.layout(false, true);
57304                 }
57305             }
57306
57307             if(initialRender){
57308                 lw.show();
57309                 mw.show();
57310             }
57311             //c.endMeasure();
57312         }, 10);
57313     },
57314
57315     onWindowResize : function(){
57316         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
57317             return;
57318         }
57319         this.layout();
57320     },
57321
57322     appendFooter : function(parentEl){
57323         return null;
57324     },
57325
57326     sortAscText : "Sort Ascending",
57327     sortDescText : "Sort Descending",
57328     lockText : "Lock Column",
57329     unlockText : "Unlock Column",
57330     columnsText : "Columns",
57331  
57332     columnsWiderText : "Wider",
57333     columnsNarrowText : "Thinner"
57334 });
57335
57336
57337 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
57338     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
57339     this.proxy.el.addClass('x-grid3-col-dd');
57340 };
57341
57342 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
57343     handleMouseDown : function(e){
57344
57345     },
57346
57347     callHandleMouseDown : function(e){
57348         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
57349     }
57350 });
57351 /*
57352  * Based on:
57353  * Ext JS Library 1.1.1
57354  * Copyright(c) 2006-2007, Ext JS, LLC.
57355  *
57356  * Originally Released Under LGPL - original licence link has changed is not relivant.
57357  *
57358  * Fork - LGPL
57359  * <script type="text/javascript">
57360  */
57361  
57362 // private
57363 // This is a support class used internally by the Grid components
57364 Roo.grid.SplitDragZone = function(grid, hd, hd2){
57365     this.grid = grid;
57366     this.view = grid.getView();
57367     this.proxy = this.view.resizeProxy;
57368     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
57369         "gridSplitters" + this.grid.getGridEl().id, {
57370         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
57371     });
57372     this.setHandleElId(Roo.id(hd));
57373     this.setOuterHandleElId(Roo.id(hd2));
57374     this.scroll = false;
57375 };
57376 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
57377     fly: Roo.Element.fly,
57378
57379     b4StartDrag : function(x, y){
57380         this.view.headersDisabled = true;
57381         this.proxy.setHeight(this.view.mainWrap.getHeight());
57382         var w = this.cm.getColumnWidth(this.cellIndex);
57383         var minw = Math.max(w-this.grid.minColumnWidth, 0);
57384         this.resetConstraints();
57385         this.setXConstraint(minw, 1000);
57386         this.setYConstraint(0, 0);
57387         this.minX = x - minw;
57388         this.maxX = x + 1000;
57389         this.startPos = x;
57390         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
57391     },
57392
57393
57394     handleMouseDown : function(e){
57395         ev = Roo.EventObject.setEvent(e);
57396         var t = this.fly(ev.getTarget());
57397         if(t.hasClass("x-grid-split")){
57398             this.cellIndex = this.view.getCellIndex(t.dom);
57399             this.split = t.dom;
57400             this.cm = this.grid.colModel;
57401             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
57402                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
57403             }
57404         }
57405     },
57406
57407     endDrag : function(e){
57408         this.view.headersDisabled = false;
57409         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
57410         var diff = endX - this.startPos;
57411         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
57412     },
57413
57414     autoOffset : function(){
57415         this.setDelta(0,0);
57416     }
57417 });/*
57418  * Based on:
57419  * Ext JS Library 1.1.1
57420  * Copyright(c) 2006-2007, Ext JS, LLC.
57421  *
57422  * Originally Released Under LGPL - original licence link has changed is not relivant.
57423  *
57424  * Fork - LGPL
57425  * <script type="text/javascript">
57426  */
57427  
57428 // private
57429 // This is a support class used internally by the Grid components
57430 Roo.grid.GridDragZone = function(grid, config){
57431     this.view = grid.getView();
57432     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
57433     if(this.view.lockedBody){
57434         this.setHandleElId(Roo.id(this.view.mainBody.dom));
57435         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
57436     }
57437     this.scroll = false;
57438     this.grid = grid;
57439     this.ddel = document.createElement('div');
57440     this.ddel.className = 'x-grid-dd-wrap';
57441 };
57442
57443 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
57444     ddGroup : "GridDD",
57445
57446     getDragData : function(e){
57447         var t = Roo.lib.Event.getTarget(e);
57448         var rowIndex = this.view.findRowIndex(t);
57449         var sm = this.grid.selModel;
57450             
57451         //Roo.log(rowIndex);
57452         
57453         if (sm.getSelectedCell) {
57454             // cell selection..
57455             if (!sm.getSelectedCell()) {
57456                 return false;
57457             }
57458             if (rowIndex != sm.getSelectedCell()[0]) {
57459                 return false;
57460             }
57461         
57462         }
57463         
57464         if(rowIndex !== false){
57465             
57466             // if editorgrid.. 
57467             
57468             
57469             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
57470                
57471             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
57472               //  
57473             //}
57474             if (e.hasModifier()){
57475                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
57476             }
57477             
57478             Roo.log("getDragData");
57479             
57480             return {
57481                 grid: this.grid,
57482                 ddel: this.ddel,
57483                 rowIndex: rowIndex,
57484                 selections:sm.getSelections ? sm.getSelections() : (
57485                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
57486                 )
57487             };
57488         }
57489         return false;
57490     },
57491
57492     onInitDrag : function(e){
57493         var data = this.dragData;
57494         this.ddel.innerHTML = this.grid.getDragDropText();
57495         this.proxy.update(this.ddel);
57496         // fire start drag?
57497     },
57498
57499     afterRepair : function(){
57500         this.dragging = false;
57501     },
57502
57503     getRepairXY : function(e, data){
57504         return false;
57505     },
57506
57507     onEndDrag : function(data, e){
57508         // fire end drag?
57509     },
57510
57511     onValidDrop : function(dd, e, id){
57512         // fire drag drop?
57513         this.hideProxy();
57514     },
57515
57516     beforeInvalidDrop : function(e, id){
57517
57518     }
57519 });/*
57520  * Based on:
57521  * Ext JS Library 1.1.1
57522  * Copyright(c) 2006-2007, Ext JS, LLC.
57523  *
57524  * Originally Released Under LGPL - original licence link has changed is not relivant.
57525  *
57526  * Fork - LGPL
57527  * <script type="text/javascript">
57528  */
57529  
57530
57531 /**
57532  * @class Roo.grid.ColumnModel
57533  * @extends Roo.util.Observable
57534  * This is the default implementation of a ColumnModel used by the Grid. It defines
57535  * the columns in the grid.
57536  * <br>Usage:<br>
57537  <pre><code>
57538  var colModel = new Roo.grid.ColumnModel([
57539         {header: "Ticker", width: 60, sortable: true, locked: true},
57540         {header: "Company Name", width: 150, sortable: true},
57541         {header: "Market Cap.", width: 100, sortable: true},
57542         {header: "$ Sales", width: 100, sortable: true, renderer: money},
57543         {header: "Employees", width: 100, sortable: true, resizable: false}
57544  ]);
57545  </code></pre>
57546  * <p>
57547  
57548  * The config options listed for this class are options which may appear in each
57549  * individual column definition.
57550  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
57551  * @constructor
57552  * @param {Object} config An Array of column config objects. See this class's
57553  * config objects for details.
57554 */
57555 Roo.grid.ColumnModel = function(config){
57556         /**
57557      * The config passed into the constructor
57558      */
57559     this.config = config;
57560     this.lookup = {};
57561
57562     // if no id, create one
57563     // if the column does not have a dataIndex mapping,
57564     // map it to the order it is in the config
57565     for(var i = 0, len = config.length; i < len; i++){
57566         var c = config[i];
57567         if(typeof c.dataIndex == "undefined"){
57568             c.dataIndex = i;
57569         }
57570         if(typeof c.renderer == "string"){
57571             c.renderer = Roo.util.Format[c.renderer];
57572         }
57573         if(typeof c.id == "undefined"){
57574             c.id = Roo.id();
57575         }
57576         if(c.editor && c.editor.xtype){
57577             c.editor  = Roo.factory(c.editor, Roo.grid);
57578         }
57579         if(c.editor && c.editor.isFormField){
57580             c.editor = new Roo.grid.GridEditor(c.editor);
57581         }
57582         this.lookup[c.id] = c;
57583     }
57584
57585     /**
57586      * The width of columns which have no width specified (defaults to 100)
57587      * @type Number
57588      */
57589     this.defaultWidth = 100;
57590
57591     /**
57592      * Default sortable of columns which have no sortable specified (defaults to false)
57593      * @type Boolean
57594      */
57595     this.defaultSortable = false;
57596
57597     this.addEvents({
57598         /**
57599              * @event widthchange
57600              * Fires when the width of a column changes.
57601              * @param {ColumnModel} this
57602              * @param {Number} columnIndex The column index
57603              * @param {Number} newWidth The new width
57604              */
57605             "widthchange": true,
57606         /**
57607              * @event headerchange
57608              * Fires when the text of a header changes.
57609              * @param {ColumnModel} this
57610              * @param {Number} columnIndex The column index
57611              * @param {Number} newText The new header text
57612              */
57613             "headerchange": true,
57614         /**
57615              * @event hiddenchange
57616              * Fires when a column is hidden or "unhidden".
57617              * @param {ColumnModel} this
57618              * @param {Number} columnIndex The column index
57619              * @param {Boolean} hidden true if hidden, false otherwise
57620              */
57621             "hiddenchange": true,
57622             /**
57623          * @event columnmoved
57624          * Fires when a column is moved.
57625          * @param {ColumnModel} this
57626          * @param {Number} oldIndex
57627          * @param {Number} newIndex
57628          */
57629         "columnmoved" : true,
57630         /**
57631          * @event columlockchange
57632          * Fires when a column's locked state is changed
57633          * @param {ColumnModel} this
57634          * @param {Number} colIndex
57635          * @param {Boolean} locked true if locked
57636          */
57637         "columnlockchange" : true
57638     });
57639     Roo.grid.ColumnModel.superclass.constructor.call(this);
57640 };
57641 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
57642     /**
57643      * @cfg {String} header The header text to display in the Grid view.
57644      */
57645     /**
57646      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
57647      * {@link Roo.data.Record} definition from which to draw the column's value. If not
57648      * specified, the column's index is used as an index into the Record's data Array.
57649      */
57650     /**
57651      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
57652      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
57653      */
57654     /**
57655      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
57656      * Defaults to the value of the {@link #defaultSortable} property.
57657      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
57658      */
57659     /**
57660      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
57661      */
57662     /**
57663      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
57664      */
57665     /**
57666      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
57667      */
57668     /**
57669      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
57670      */
57671     /**
57672      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
57673      * given the cell's data value. See {@link #setRenderer}. If not specified, the
57674      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
57675      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
57676      */
57677        /**
57678      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
57679      */
57680     /**
57681      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
57682      */
57683     /**
57684      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
57685      */
57686     /**
57687      * @cfg {String} cursor (Optional)
57688      */
57689     /**
57690      * @cfg {String} tooltip (Optional)
57691      */
57692     /**
57693      * @cfg {Number} xs (Optional)
57694      */
57695     /**
57696      * @cfg {Number} sm (Optional)
57697      */
57698     /**
57699      * @cfg {Number} md (Optional)
57700      */
57701     /**
57702      * @cfg {Number} lg (Optional)
57703      */
57704     /**
57705      * Returns the id of the column at the specified index.
57706      * @param {Number} index The column index
57707      * @return {String} the id
57708      */
57709     getColumnId : function(index){
57710         return this.config[index].id;
57711     },
57712
57713     /**
57714      * Returns the column for a specified id.
57715      * @param {String} id The column id
57716      * @return {Object} the column
57717      */
57718     getColumnById : function(id){
57719         return this.lookup[id];
57720     },
57721
57722     
57723     /**
57724      * Returns the column for a specified dataIndex.
57725      * @param {String} dataIndex The column dataIndex
57726      * @return {Object|Boolean} the column or false if not found
57727      */
57728     getColumnByDataIndex: function(dataIndex){
57729         var index = this.findColumnIndex(dataIndex);
57730         return index > -1 ? this.config[index] : false;
57731     },
57732     
57733     /**
57734      * Returns the index for a specified column id.
57735      * @param {String} id The column id
57736      * @return {Number} the index, or -1 if not found
57737      */
57738     getIndexById : function(id){
57739         for(var i = 0, len = this.config.length; i < len; i++){
57740             if(this.config[i].id == id){
57741                 return i;
57742             }
57743         }
57744         return -1;
57745     },
57746     
57747     /**
57748      * Returns the index for a specified column dataIndex.
57749      * @param {String} dataIndex The column dataIndex
57750      * @return {Number} the index, or -1 if not found
57751      */
57752     
57753     findColumnIndex : function(dataIndex){
57754         for(var i = 0, len = this.config.length; i < len; i++){
57755             if(this.config[i].dataIndex == dataIndex){
57756                 return i;
57757             }
57758         }
57759         return -1;
57760     },
57761     
57762     
57763     moveColumn : function(oldIndex, newIndex){
57764         var c = this.config[oldIndex];
57765         this.config.splice(oldIndex, 1);
57766         this.config.splice(newIndex, 0, c);
57767         this.dataMap = null;
57768         this.fireEvent("columnmoved", this, oldIndex, newIndex);
57769     },
57770
57771     isLocked : function(colIndex){
57772         return this.config[colIndex].locked === true;
57773     },
57774
57775     setLocked : function(colIndex, value, suppressEvent){
57776         if(this.isLocked(colIndex) == value){
57777             return;
57778         }
57779         this.config[colIndex].locked = value;
57780         if(!suppressEvent){
57781             this.fireEvent("columnlockchange", this, colIndex, value);
57782         }
57783     },
57784
57785     getTotalLockedWidth : function(){
57786         var totalWidth = 0;
57787         for(var i = 0; i < this.config.length; i++){
57788             if(this.isLocked(i) && !this.isHidden(i)){
57789                 this.totalWidth += this.getColumnWidth(i);
57790             }
57791         }
57792         return totalWidth;
57793     },
57794
57795     getLockedCount : function(){
57796         for(var i = 0, len = this.config.length; i < len; i++){
57797             if(!this.isLocked(i)){
57798                 return i;
57799             }
57800         }
57801         
57802         return this.config.length;
57803     },
57804
57805     /**
57806      * Returns the number of columns.
57807      * @return {Number}
57808      */
57809     getColumnCount : function(visibleOnly){
57810         if(visibleOnly === true){
57811             var c = 0;
57812             for(var i = 0, len = this.config.length; i < len; i++){
57813                 if(!this.isHidden(i)){
57814                     c++;
57815                 }
57816             }
57817             return c;
57818         }
57819         return this.config.length;
57820     },
57821
57822     /**
57823      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
57824      * @param {Function} fn
57825      * @param {Object} scope (optional)
57826      * @return {Array} result
57827      */
57828     getColumnsBy : function(fn, scope){
57829         var r = [];
57830         for(var i = 0, len = this.config.length; i < len; i++){
57831             var c = this.config[i];
57832             if(fn.call(scope||this, c, i) === true){
57833                 r[r.length] = c;
57834             }
57835         }
57836         return r;
57837     },
57838
57839     /**
57840      * Returns true if the specified column is sortable.
57841      * @param {Number} col The column index
57842      * @return {Boolean}
57843      */
57844     isSortable : function(col){
57845         if(typeof this.config[col].sortable == "undefined"){
57846             return this.defaultSortable;
57847         }
57848         return this.config[col].sortable;
57849     },
57850
57851     /**
57852      * Returns the rendering (formatting) function defined for the column.
57853      * @param {Number} col The column index.
57854      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
57855      */
57856     getRenderer : function(col){
57857         if(!this.config[col].renderer){
57858             return Roo.grid.ColumnModel.defaultRenderer;
57859         }
57860         return this.config[col].renderer;
57861     },
57862
57863     /**
57864      * Sets the rendering (formatting) function for a column.
57865      * @param {Number} col The column index
57866      * @param {Function} fn The function to use to process the cell's raw data
57867      * to return HTML markup for the grid view. The render function is called with
57868      * the following parameters:<ul>
57869      * <li>Data value.</li>
57870      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
57871      * <li>css A CSS style string to apply to the table cell.</li>
57872      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
57873      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
57874      * <li>Row index</li>
57875      * <li>Column index</li>
57876      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
57877      */
57878     setRenderer : function(col, fn){
57879         this.config[col].renderer = fn;
57880     },
57881
57882     /**
57883      * Returns the width for the specified column.
57884      * @param {Number} col The column index
57885      * @return {Number}
57886      */
57887     getColumnWidth : function(col){
57888         return this.config[col].width * 1 || this.defaultWidth;
57889     },
57890
57891     /**
57892      * Sets the width for a column.
57893      * @param {Number} col The column index
57894      * @param {Number} width The new width
57895      */
57896     setColumnWidth : function(col, width, suppressEvent){
57897         this.config[col].width = width;
57898         this.totalWidth = null;
57899         if(!suppressEvent){
57900              this.fireEvent("widthchange", this, col, width);
57901         }
57902     },
57903
57904     /**
57905      * Returns the total width of all columns.
57906      * @param {Boolean} includeHidden True to include hidden column widths
57907      * @return {Number}
57908      */
57909     getTotalWidth : function(includeHidden){
57910         if(!this.totalWidth){
57911             this.totalWidth = 0;
57912             for(var i = 0, len = this.config.length; i < len; i++){
57913                 if(includeHidden || !this.isHidden(i)){
57914                     this.totalWidth += this.getColumnWidth(i);
57915                 }
57916             }
57917         }
57918         return this.totalWidth;
57919     },
57920
57921     /**
57922      * Returns the header for the specified column.
57923      * @param {Number} col The column index
57924      * @return {String}
57925      */
57926     getColumnHeader : function(col){
57927         return this.config[col].header;
57928     },
57929
57930     /**
57931      * Sets the header for a column.
57932      * @param {Number} col The column index
57933      * @param {String} header The new header
57934      */
57935     setColumnHeader : function(col, header){
57936         this.config[col].header = header;
57937         this.fireEvent("headerchange", this, col, header);
57938     },
57939
57940     /**
57941      * Returns the tooltip for the specified column.
57942      * @param {Number} col The column index
57943      * @return {String}
57944      */
57945     getColumnTooltip : function(col){
57946             return this.config[col].tooltip;
57947     },
57948     /**
57949      * Sets the tooltip for a column.
57950      * @param {Number} col The column index
57951      * @param {String} tooltip The new tooltip
57952      */
57953     setColumnTooltip : function(col, tooltip){
57954             this.config[col].tooltip = tooltip;
57955     },
57956
57957     /**
57958      * Returns the dataIndex for the specified column.
57959      * @param {Number} col The column index
57960      * @return {Number}
57961      */
57962     getDataIndex : function(col){
57963         return this.config[col].dataIndex;
57964     },
57965
57966     /**
57967      * Sets the dataIndex for a column.
57968      * @param {Number} col The column index
57969      * @param {Number} dataIndex The new dataIndex
57970      */
57971     setDataIndex : function(col, dataIndex){
57972         this.config[col].dataIndex = dataIndex;
57973     },
57974
57975     
57976     
57977     /**
57978      * Returns true if the cell is editable.
57979      * @param {Number} colIndex The column index
57980      * @param {Number} rowIndex The row index - this is nto actually used..?
57981      * @return {Boolean}
57982      */
57983     isCellEditable : function(colIndex, rowIndex){
57984         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
57985     },
57986
57987     /**
57988      * Returns the editor defined for the cell/column.
57989      * return false or null to disable editing.
57990      * @param {Number} colIndex The column index
57991      * @param {Number} rowIndex The row index
57992      * @return {Object}
57993      */
57994     getCellEditor : function(colIndex, rowIndex){
57995         return this.config[colIndex].editor;
57996     },
57997
57998     /**
57999      * Sets if a column is editable.
58000      * @param {Number} col The column index
58001      * @param {Boolean} editable True if the column is editable
58002      */
58003     setEditable : function(col, editable){
58004         this.config[col].editable = editable;
58005     },
58006
58007
58008     /**
58009      * Returns true if the column is hidden.
58010      * @param {Number} colIndex The column index
58011      * @return {Boolean}
58012      */
58013     isHidden : function(colIndex){
58014         return this.config[colIndex].hidden;
58015     },
58016
58017
58018     /**
58019      * Returns true if the column width cannot be changed
58020      */
58021     isFixed : function(colIndex){
58022         return this.config[colIndex].fixed;
58023     },
58024
58025     /**
58026      * Returns true if the column can be resized
58027      * @return {Boolean}
58028      */
58029     isResizable : function(colIndex){
58030         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
58031     },
58032     /**
58033      * Sets if a column is hidden.
58034      * @param {Number} colIndex The column index
58035      * @param {Boolean} hidden True if the column is hidden
58036      */
58037     setHidden : function(colIndex, hidden){
58038         this.config[colIndex].hidden = hidden;
58039         this.totalWidth = null;
58040         this.fireEvent("hiddenchange", this, colIndex, hidden);
58041     },
58042
58043     /**
58044      * Sets the editor for a column.
58045      * @param {Number} col The column index
58046      * @param {Object} editor The editor object
58047      */
58048     setEditor : function(col, editor){
58049         this.config[col].editor = editor;
58050     }
58051 });
58052
58053 Roo.grid.ColumnModel.defaultRenderer = function(value)
58054 {
58055     if(typeof value == "object") {
58056         return value;
58057     }
58058         if(typeof value == "string" && value.length < 1){
58059             return "&#160;";
58060         }
58061     
58062         return String.format("{0}", value);
58063 };
58064
58065 // Alias for backwards compatibility
58066 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
58067 /*
58068  * Based on:
58069  * Ext JS Library 1.1.1
58070  * Copyright(c) 2006-2007, Ext JS, LLC.
58071  *
58072  * Originally Released Under LGPL - original licence link has changed is not relivant.
58073  *
58074  * Fork - LGPL
58075  * <script type="text/javascript">
58076  */
58077
58078 /**
58079  * @class Roo.grid.AbstractSelectionModel
58080  * @extends Roo.util.Observable
58081  * Abstract base class for grid SelectionModels.  It provides the interface that should be
58082  * implemented by descendant classes.  This class should not be directly instantiated.
58083  * @constructor
58084  */
58085 Roo.grid.AbstractSelectionModel = function(){
58086     this.locked = false;
58087     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
58088 };
58089
58090 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
58091     /** @ignore Called by the grid automatically. Do not call directly. */
58092     init : function(grid){
58093         this.grid = grid;
58094         this.initEvents();
58095     },
58096
58097     /**
58098      * Locks the selections.
58099      */
58100     lock : function(){
58101         this.locked = true;
58102     },
58103
58104     /**
58105      * Unlocks the selections.
58106      */
58107     unlock : function(){
58108         this.locked = false;
58109     },
58110
58111     /**
58112      * Returns true if the selections are locked.
58113      * @return {Boolean}
58114      */
58115     isLocked : function(){
58116         return this.locked;
58117     }
58118 });/*
58119  * Based on:
58120  * Ext JS Library 1.1.1
58121  * Copyright(c) 2006-2007, Ext JS, LLC.
58122  *
58123  * Originally Released Under LGPL - original licence link has changed is not relivant.
58124  *
58125  * Fork - LGPL
58126  * <script type="text/javascript">
58127  */
58128 /**
58129  * @extends Roo.grid.AbstractSelectionModel
58130  * @class Roo.grid.RowSelectionModel
58131  * The default SelectionModel used by {@link Roo.grid.Grid}.
58132  * It supports multiple selections and keyboard selection/navigation. 
58133  * @constructor
58134  * @param {Object} config
58135  */
58136 Roo.grid.RowSelectionModel = function(config){
58137     Roo.apply(this, config);
58138     this.selections = new Roo.util.MixedCollection(false, function(o){
58139         return o.id;
58140     });
58141
58142     this.last = false;
58143     this.lastActive = false;
58144
58145     this.addEvents({
58146         /**
58147              * @event selectionchange
58148              * Fires when the selection changes
58149              * @param {SelectionModel} this
58150              */
58151             "selectionchange" : true,
58152         /**
58153              * @event afterselectionchange
58154              * Fires after the selection changes (eg. by key press or clicking)
58155              * @param {SelectionModel} this
58156              */
58157             "afterselectionchange" : true,
58158         /**
58159              * @event beforerowselect
58160              * Fires when a row is selected being selected, return false to cancel.
58161              * @param {SelectionModel} this
58162              * @param {Number} rowIndex The selected index
58163              * @param {Boolean} keepExisting False if other selections will be cleared
58164              */
58165             "beforerowselect" : true,
58166         /**
58167              * @event rowselect
58168              * Fires when a row is selected.
58169              * @param {SelectionModel} this
58170              * @param {Number} rowIndex The selected index
58171              * @param {Roo.data.Record} r The record
58172              */
58173             "rowselect" : true,
58174         /**
58175              * @event rowdeselect
58176              * Fires when a row is deselected.
58177              * @param {SelectionModel} this
58178              * @param {Number} rowIndex The selected index
58179              */
58180         "rowdeselect" : true
58181     });
58182     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
58183     this.locked = false;
58184 };
58185
58186 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
58187     /**
58188      * @cfg {Boolean} singleSelect
58189      * True to allow selection of only one row at a time (defaults to false)
58190      */
58191     singleSelect : false,
58192
58193     // private
58194     initEvents : function(){
58195
58196         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
58197             this.grid.on("mousedown", this.handleMouseDown, this);
58198         }else{ // allow click to work like normal
58199             this.grid.on("rowclick", this.handleDragableRowClick, this);
58200         }
58201
58202         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
58203             "up" : function(e){
58204                 if(!e.shiftKey){
58205                     this.selectPrevious(e.shiftKey);
58206                 }else if(this.last !== false && this.lastActive !== false){
58207                     var last = this.last;
58208                     this.selectRange(this.last,  this.lastActive-1);
58209                     this.grid.getView().focusRow(this.lastActive);
58210                     if(last !== false){
58211                         this.last = last;
58212                     }
58213                 }else{
58214                     this.selectFirstRow();
58215                 }
58216                 this.fireEvent("afterselectionchange", this);
58217             },
58218             "down" : function(e){
58219                 if(!e.shiftKey){
58220                     this.selectNext(e.shiftKey);
58221                 }else if(this.last !== false && this.lastActive !== false){
58222                     var last = this.last;
58223                     this.selectRange(this.last,  this.lastActive+1);
58224                     this.grid.getView().focusRow(this.lastActive);
58225                     if(last !== false){
58226                         this.last = last;
58227                     }
58228                 }else{
58229                     this.selectFirstRow();
58230                 }
58231                 this.fireEvent("afterselectionchange", this);
58232             },
58233             scope: this
58234         });
58235
58236         var view = this.grid.view;
58237         view.on("refresh", this.onRefresh, this);
58238         view.on("rowupdated", this.onRowUpdated, this);
58239         view.on("rowremoved", this.onRemove, this);
58240     },
58241
58242     // private
58243     onRefresh : function(){
58244         var ds = this.grid.dataSource, i, v = this.grid.view;
58245         var s = this.selections;
58246         s.each(function(r){
58247             if((i = ds.indexOfId(r.id)) != -1){
58248                 v.onRowSelect(i);
58249                 s.add(ds.getAt(i)); // updating the selection relate data
58250             }else{
58251                 s.remove(r);
58252             }
58253         });
58254     },
58255
58256     // private
58257     onRemove : function(v, index, r){
58258         this.selections.remove(r);
58259     },
58260
58261     // private
58262     onRowUpdated : function(v, index, r){
58263         if(this.isSelected(r)){
58264             v.onRowSelect(index);
58265         }
58266     },
58267
58268     /**
58269      * Select records.
58270      * @param {Array} records The records to select
58271      * @param {Boolean} keepExisting (optional) True to keep existing selections
58272      */
58273     selectRecords : function(records, keepExisting){
58274         if(!keepExisting){
58275             this.clearSelections();
58276         }
58277         var ds = this.grid.dataSource;
58278         for(var i = 0, len = records.length; i < len; i++){
58279             this.selectRow(ds.indexOf(records[i]), true);
58280         }
58281     },
58282
58283     /**
58284      * Gets the number of selected rows.
58285      * @return {Number}
58286      */
58287     getCount : function(){
58288         return this.selections.length;
58289     },
58290
58291     /**
58292      * Selects the first row in the grid.
58293      */
58294     selectFirstRow : function(){
58295         this.selectRow(0);
58296     },
58297
58298     /**
58299      * Select the last row.
58300      * @param {Boolean} keepExisting (optional) True to keep existing selections
58301      */
58302     selectLastRow : function(keepExisting){
58303         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
58304     },
58305
58306     /**
58307      * Selects the row immediately following the last selected row.
58308      * @param {Boolean} keepExisting (optional) True to keep existing selections
58309      */
58310     selectNext : function(keepExisting){
58311         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
58312             this.selectRow(this.last+1, keepExisting);
58313             this.grid.getView().focusRow(this.last);
58314         }
58315     },
58316
58317     /**
58318      * Selects the row that precedes the last selected row.
58319      * @param {Boolean} keepExisting (optional) True to keep existing selections
58320      */
58321     selectPrevious : function(keepExisting){
58322         if(this.last){
58323             this.selectRow(this.last-1, keepExisting);
58324             this.grid.getView().focusRow(this.last);
58325         }
58326     },
58327
58328     /**
58329      * Returns the selected records
58330      * @return {Array} Array of selected records
58331      */
58332     getSelections : function(){
58333         return [].concat(this.selections.items);
58334     },
58335
58336     /**
58337      * Returns the first selected record.
58338      * @return {Record}
58339      */
58340     getSelected : function(){
58341         return this.selections.itemAt(0);
58342     },
58343
58344
58345     /**
58346      * Clears all selections.
58347      */
58348     clearSelections : function(fast){
58349         if(this.locked) {
58350             return;
58351         }
58352         if(fast !== true){
58353             var ds = this.grid.dataSource;
58354             var s = this.selections;
58355             s.each(function(r){
58356                 this.deselectRow(ds.indexOfId(r.id));
58357             }, this);
58358             s.clear();
58359         }else{
58360             this.selections.clear();
58361         }
58362         this.last = false;
58363     },
58364
58365
58366     /**
58367      * Selects all rows.
58368      */
58369     selectAll : function(){
58370         if(this.locked) {
58371             return;
58372         }
58373         this.selections.clear();
58374         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
58375             this.selectRow(i, true);
58376         }
58377     },
58378
58379     /**
58380      * Returns True if there is a selection.
58381      * @return {Boolean}
58382      */
58383     hasSelection : function(){
58384         return this.selections.length > 0;
58385     },
58386
58387     /**
58388      * Returns True if the specified row is selected.
58389      * @param {Number/Record} record The record or index of the record to check
58390      * @return {Boolean}
58391      */
58392     isSelected : function(index){
58393         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
58394         return (r && this.selections.key(r.id) ? true : false);
58395     },
58396
58397     /**
58398      * Returns True if the specified record id is selected.
58399      * @param {String} id The id of record to check
58400      * @return {Boolean}
58401      */
58402     isIdSelected : function(id){
58403         return (this.selections.key(id) ? true : false);
58404     },
58405
58406     // private
58407     handleMouseDown : function(e, t){
58408         var view = this.grid.getView(), rowIndex;
58409         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
58410             return;
58411         };
58412         if(e.shiftKey && this.last !== false){
58413             var last = this.last;
58414             this.selectRange(last, rowIndex, e.ctrlKey);
58415             this.last = last; // reset the last
58416             view.focusRow(rowIndex);
58417         }else{
58418             var isSelected = this.isSelected(rowIndex);
58419             if(e.button !== 0 && isSelected){
58420                 view.focusRow(rowIndex);
58421             }else if(e.ctrlKey && isSelected){
58422                 this.deselectRow(rowIndex);
58423             }else if(!isSelected){
58424                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
58425                 view.focusRow(rowIndex);
58426             }
58427         }
58428         this.fireEvent("afterselectionchange", this);
58429     },
58430     // private
58431     handleDragableRowClick :  function(grid, rowIndex, e) 
58432     {
58433         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
58434             this.selectRow(rowIndex, false);
58435             grid.view.focusRow(rowIndex);
58436              this.fireEvent("afterselectionchange", this);
58437         }
58438     },
58439     
58440     /**
58441      * Selects multiple rows.
58442      * @param {Array} rows Array of the indexes of the row to select
58443      * @param {Boolean} keepExisting (optional) True to keep existing selections
58444      */
58445     selectRows : function(rows, keepExisting){
58446         if(!keepExisting){
58447             this.clearSelections();
58448         }
58449         for(var i = 0, len = rows.length; i < len; i++){
58450             this.selectRow(rows[i], true);
58451         }
58452     },
58453
58454     /**
58455      * Selects a range of rows. All rows in between startRow and endRow are also selected.
58456      * @param {Number} startRow The index of the first row in the range
58457      * @param {Number} endRow The index of the last row in the range
58458      * @param {Boolean} keepExisting (optional) True to retain existing selections
58459      */
58460     selectRange : function(startRow, endRow, keepExisting){
58461         if(this.locked) {
58462             return;
58463         }
58464         if(!keepExisting){
58465             this.clearSelections();
58466         }
58467         if(startRow <= endRow){
58468             for(var i = startRow; i <= endRow; i++){
58469                 this.selectRow(i, true);
58470             }
58471         }else{
58472             for(var i = startRow; i >= endRow; i--){
58473                 this.selectRow(i, true);
58474             }
58475         }
58476     },
58477
58478     /**
58479      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
58480      * @param {Number} startRow The index of the first row in the range
58481      * @param {Number} endRow The index of the last row in the range
58482      */
58483     deselectRange : function(startRow, endRow, preventViewNotify){
58484         if(this.locked) {
58485             return;
58486         }
58487         for(var i = startRow; i <= endRow; i++){
58488             this.deselectRow(i, preventViewNotify);
58489         }
58490     },
58491
58492     /**
58493      * Selects a row.
58494      * @param {Number} row The index of the row to select
58495      * @param {Boolean} keepExisting (optional) True to keep existing selections
58496      */
58497     selectRow : function(index, keepExisting, preventViewNotify){
58498         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
58499             return;
58500         }
58501         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
58502             if(!keepExisting || this.singleSelect){
58503                 this.clearSelections();
58504             }
58505             var r = this.grid.dataSource.getAt(index);
58506             this.selections.add(r);
58507             this.last = this.lastActive = index;
58508             if(!preventViewNotify){
58509                 this.grid.getView().onRowSelect(index);
58510             }
58511             this.fireEvent("rowselect", this, index, r);
58512             this.fireEvent("selectionchange", this);
58513         }
58514     },
58515
58516     /**
58517      * Deselects a row.
58518      * @param {Number} row The index of the row to deselect
58519      */
58520     deselectRow : function(index, preventViewNotify){
58521         if(this.locked) {
58522             return;
58523         }
58524         if(this.last == index){
58525             this.last = false;
58526         }
58527         if(this.lastActive == index){
58528             this.lastActive = false;
58529         }
58530         var r = this.grid.dataSource.getAt(index);
58531         this.selections.remove(r);
58532         if(!preventViewNotify){
58533             this.grid.getView().onRowDeselect(index);
58534         }
58535         this.fireEvent("rowdeselect", this, index);
58536         this.fireEvent("selectionchange", this);
58537     },
58538
58539     // private
58540     restoreLast : function(){
58541         if(this._last){
58542             this.last = this._last;
58543         }
58544     },
58545
58546     // private
58547     acceptsNav : function(row, col, cm){
58548         return !cm.isHidden(col) && cm.isCellEditable(col, row);
58549     },
58550
58551     // private
58552     onEditorKey : function(field, e){
58553         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
58554         if(k == e.TAB){
58555             e.stopEvent();
58556             ed.completeEdit();
58557             if(e.shiftKey){
58558                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
58559             }else{
58560                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58561             }
58562         }else if(k == e.ENTER && !e.ctrlKey){
58563             e.stopEvent();
58564             ed.completeEdit();
58565             if(e.shiftKey){
58566                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
58567             }else{
58568                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
58569             }
58570         }else if(k == e.ESC){
58571             ed.cancelEdit();
58572         }
58573         if(newCell){
58574             g.startEditing(newCell[0], newCell[1]);
58575         }
58576     }
58577 });/*
58578  * Based on:
58579  * Ext JS Library 1.1.1
58580  * Copyright(c) 2006-2007, Ext JS, LLC.
58581  *
58582  * Originally Released Under LGPL - original licence link has changed is not relivant.
58583  *
58584  * Fork - LGPL
58585  * <script type="text/javascript">
58586  */
58587 /**
58588  * @class Roo.grid.CellSelectionModel
58589  * @extends Roo.grid.AbstractSelectionModel
58590  * This class provides the basic implementation for cell selection in a grid.
58591  * @constructor
58592  * @param {Object} config The object containing the configuration of this model.
58593  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
58594  */
58595 Roo.grid.CellSelectionModel = function(config){
58596     Roo.apply(this, config);
58597
58598     this.selection = null;
58599
58600     this.addEvents({
58601         /**
58602              * @event beforerowselect
58603              * Fires before a cell is selected.
58604              * @param {SelectionModel} this
58605              * @param {Number} rowIndex The selected row index
58606              * @param {Number} colIndex The selected cell index
58607              */
58608             "beforecellselect" : true,
58609         /**
58610              * @event cellselect
58611              * Fires when a cell is selected.
58612              * @param {SelectionModel} this
58613              * @param {Number} rowIndex The selected row index
58614              * @param {Number} colIndex The selected cell index
58615              */
58616             "cellselect" : true,
58617         /**
58618              * @event selectionchange
58619              * Fires when the active selection changes.
58620              * @param {SelectionModel} this
58621              * @param {Object} selection null for no selection or an object (o) with two properties
58622                 <ul>
58623                 <li>o.record: the record object for the row the selection is in</li>
58624                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
58625                 </ul>
58626              */
58627             "selectionchange" : true,
58628         /**
58629              * @event tabend
58630              * Fires when the tab (or enter) was pressed on the last editable cell
58631              * You can use this to trigger add new row.
58632              * @param {SelectionModel} this
58633              */
58634             "tabend" : true,
58635          /**
58636              * @event beforeeditnext
58637              * Fires before the next editable sell is made active
58638              * You can use this to skip to another cell or fire the tabend
58639              *    if you set cell to false
58640              * @param {Object} eventdata object : { cell : [ row, col ] } 
58641              */
58642             "beforeeditnext" : true
58643     });
58644     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
58645 };
58646
58647 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
58648     
58649     enter_is_tab: false,
58650
58651     /** @ignore */
58652     initEvents : function(){
58653         this.grid.on("mousedown", this.handleMouseDown, this);
58654         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
58655         var view = this.grid.view;
58656         view.on("refresh", this.onViewChange, this);
58657         view.on("rowupdated", this.onRowUpdated, this);
58658         view.on("beforerowremoved", this.clearSelections, this);
58659         view.on("beforerowsinserted", this.clearSelections, this);
58660         if(this.grid.isEditor){
58661             this.grid.on("beforeedit", this.beforeEdit,  this);
58662         }
58663     },
58664
58665         //private
58666     beforeEdit : function(e){
58667         this.select(e.row, e.column, false, true, e.record);
58668     },
58669
58670         //private
58671     onRowUpdated : function(v, index, r){
58672         if(this.selection && this.selection.record == r){
58673             v.onCellSelect(index, this.selection.cell[1]);
58674         }
58675     },
58676
58677         //private
58678     onViewChange : function(){
58679         this.clearSelections(true);
58680     },
58681
58682         /**
58683          * Returns the currently selected cell,.
58684          * @return {Array} The selected cell (row, column) or null if none selected.
58685          */
58686     getSelectedCell : function(){
58687         return this.selection ? this.selection.cell : null;
58688     },
58689
58690     /**
58691      * Clears all selections.
58692      * @param {Boolean} true to prevent the gridview from being notified about the change.
58693      */
58694     clearSelections : function(preventNotify){
58695         var s = this.selection;
58696         if(s){
58697             if(preventNotify !== true){
58698                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
58699             }
58700             this.selection = null;
58701             this.fireEvent("selectionchange", this, null);
58702         }
58703     },
58704
58705     /**
58706      * Returns true if there is a selection.
58707      * @return {Boolean}
58708      */
58709     hasSelection : function(){
58710         return this.selection ? true : false;
58711     },
58712
58713     /** @ignore */
58714     handleMouseDown : function(e, t){
58715         var v = this.grid.getView();
58716         if(this.isLocked()){
58717             return;
58718         };
58719         var row = v.findRowIndex(t);
58720         var cell = v.findCellIndex(t);
58721         if(row !== false && cell !== false){
58722             this.select(row, cell);
58723         }
58724     },
58725
58726     /**
58727      * Selects a cell.
58728      * @param {Number} rowIndex
58729      * @param {Number} collIndex
58730      */
58731     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
58732         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
58733             this.clearSelections();
58734             r = r || this.grid.dataSource.getAt(rowIndex);
58735             this.selection = {
58736                 record : r,
58737                 cell : [rowIndex, colIndex]
58738             };
58739             if(!preventViewNotify){
58740                 var v = this.grid.getView();
58741                 v.onCellSelect(rowIndex, colIndex);
58742                 if(preventFocus !== true){
58743                     v.focusCell(rowIndex, colIndex);
58744                 }
58745             }
58746             this.fireEvent("cellselect", this, rowIndex, colIndex);
58747             this.fireEvent("selectionchange", this, this.selection);
58748         }
58749     },
58750
58751         //private
58752     isSelectable : function(rowIndex, colIndex, cm){
58753         return !cm.isHidden(colIndex);
58754     },
58755
58756     /** @ignore */
58757     handleKeyDown : function(e){
58758         //Roo.log('Cell Sel Model handleKeyDown');
58759         if(!e.isNavKeyPress()){
58760             return;
58761         }
58762         var g = this.grid, s = this.selection;
58763         if(!s){
58764             e.stopEvent();
58765             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
58766             if(cell){
58767                 this.select(cell[0], cell[1]);
58768             }
58769             return;
58770         }
58771         var sm = this;
58772         var walk = function(row, col, step){
58773             return g.walkCells(row, col, step, sm.isSelectable,  sm);
58774         };
58775         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
58776         var newCell;
58777
58778       
58779
58780         switch(k){
58781             case e.TAB:
58782                 // handled by onEditorKey
58783                 if (g.isEditor && g.editing) {
58784                     return;
58785                 }
58786                 if(e.shiftKey) {
58787                     newCell = walk(r, c-1, -1);
58788                 } else {
58789                     newCell = walk(r, c+1, 1);
58790                 }
58791                 break;
58792             
58793             case e.DOWN:
58794                newCell = walk(r+1, c, 1);
58795                 break;
58796             
58797             case e.UP:
58798                 newCell = walk(r-1, c, -1);
58799                 break;
58800             
58801             case e.RIGHT:
58802                 newCell = walk(r, c+1, 1);
58803                 break;
58804             
58805             case e.LEFT:
58806                 newCell = walk(r, c-1, -1);
58807                 break;
58808             
58809             case e.ENTER:
58810                 
58811                 if(g.isEditor && !g.editing){
58812                    g.startEditing(r, c);
58813                    e.stopEvent();
58814                    return;
58815                 }
58816                 
58817                 
58818              break;
58819         };
58820         if(newCell){
58821             this.select(newCell[0], newCell[1]);
58822             e.stopEvent();
58823             
58824         }
58825     },
58826
58827     acceptsNav : function(row, col, cm){
58828         return !cm.isHidden(col) && cm.isCellEditable(col, row);
58829     },
58830     /**
58831      * Selects a cell.
58832      * @param {Number} field (not used) - as it's normally used as a listener
58833      * @param {Number} e - event - fake it by using
58834      *
58835      * var e = Roo.EventObjectImpl.prototype;
58836      * e.keyCode = e.TAB
58837      *
58838      * 
58839      */
58840     onEditorKey : function(field, e){
58841         
58842         var k = e.getKey(),
58843             newCell,
58844             g = this.grid,
58845             ed = g.activeEditor,
58846             forward = false;
58847         ///Roo.log('onEditorKey' + k);
58848         
58849         
58850         if (this.enter_is_tab && k == e.ENTER) {
58851             k = e.TAB;
58852         }
58853         
58854         if(k == e.TAB){
58855             if(e.shiftKey){
58856                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
58857             }else{
58858                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58859                 forward = true;
58860             }
58861             
58862             e.stopEvent();
58863             
58864         } else if(k == e.ENTER &&  !e.ctrlKey){
58865             ed.completeEdit();
58866             e.stopEvent();
58867             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58868         
58869                 } else if(k == e.ESC){
58870             ed.cancelEdit();
58871         }
58872                 
58873         if (newCell) {
58874             var ecall = { cell : newCell, forward : forward };
58875             this.fireEvent('beforeeditnext', ecall );
58876             newCell = ecall.cell;
58877                         forward = ecall.forward;
58878         }
58879                 
58880         if(newCell){
58881             //Roo.log('next cell after edit');
58882             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
58883         } else if (forward) {
58884             // tabbed past last
58885             this.fireEvent.defer(100, this, ['tabend',this]);
58886         }
58887     }
58888 });/*
58889  * Based on:
58890  * Ext JS Library 1.1.1
58891  * Copyright(c) 2006-2007, Ext JS, LLC.
58892  *
58893  * Originally Released Under LGPL - original licence link has changed is not relivant.
58894  *
58895  * Fork - LGPL
58896  * <script type="text/javascript">
58897  */
58898  
58899 /**
58900  * @class Roo.grid.EditorGrid
58901  * @extends Roo.grid.Grid
58902  * Class for creating and editable grid.
58903  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
58904  * The container MUST have some type of size defined for the grid to fill. The container will be 
58905  * automatically set to position relative if it isn't already.
58906  * @param {Object} dataSource The data model to bind to
58907  * @param {Object} colModel The column model with info about this grid's columns
58908  */
58909 Roo.grid.EditorGrid = function(container, config){
58910     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
58911     this.getGridEl().addClass("xedit-grid");
58912
58913     if(!this.selModel){
58914         this.selModel = new Roo.grid.CellSelectionModel();
58915     }
58916
58917     this.activeEditor = null;
58918
58919         this.addEvents({
58920             /**
58921              * @event beforeedit
58922              * Fires before cell editing is triggered. The edit event object has the following properties <br />
58923              * <ul style="padding:5px;padding-left:16px;">
58924              * <li>grid - This grid</li>
58925              * <li>record - The record being edited</li>
58926              * <li>field - The field name being edited</li>
58927              * <li>value - The value for the field being edited.</li>
58928              * <li>row - The grid row index</li>
58929              * <li>column - The grid column index</li>
58930              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
58931              * </ul>
58932              * @param {Object} e An edit event (see above for description)
58933              */
58934             "beforeedit" : true,
58935             /**
58936              * @event afteredit
58937              * Fires after a cell is edited. <br />
58938              * <ul style="padding:5px;padding-left:16px;">
58939              * <li>grid - This grid</li>
58940              * <li>record - The record being edited</li>
58941              * <li>field - The field name being edited</li>
58942              * <li>value - The value being set</li>
58943              * <li>originalValue - The original value for the field, before the edit.</li>
58944              * <li>row - The grid row index</li>
58945              * <li>column - The grid column index</li>
58946              * </ul>
58947              * @param {Object} e An edit event (see above for description)
58948              */
58949             "afteredit" : true,
58950             /**
58951              * @event validateedit
58952              * Fires after a cell is edited, but before the value is set in the record. 
58953          * You can use this to modify the value being set in the field, Return false
58954              * to cancel the change. The edit event object has the following properties <br />
58955              * <ul style="padding:5px;padding-left:16px;">
58956          * <li>editor - This editor</li>
58957              * <li>grid - This grid</li>
58958              * <li>record - The record being edited</li>
58959              * <li>field - The field name being edited</li>
58960              * <li>value - The value being set</li>
58961              * <li>originalValue - The original value for the field, before the edit.</li>
58962              * <li>row - The grid row index</li>
58963              * <li>column - The grid column index</li>
58964              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
58965              * </ul>
58966              * @param {Object} e An edit event (see above for description)
58967              */
58968             "validateedit" : true
58969         });
58970     this.on("bodyscroll", this.stopEditing,  this);
58971     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
58972 };
58973
58974 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
58975     /**
58976      * @cfg {Number} clicksToEdit
58977      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
58978      */
58979     clicksToEdit: 2,
58980
58981     // private
58982     isEditor : true,
58983     // private
58984     trackMouseOver: false, // causes very odd FF errors
58985
58986     onCellDblClick : function(g, row, col){
58987         this.startEditing(row, col);
58988     },
58989
58990     onEditComplete : function(ed, value, startValue){
58991         this.editing = false;
58992         this.activeEditor = null;
58993         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
58994         var r = ed.record;
58995         var field = this.colModel.getDataIndex(ed.col);
58996         var e = {
58997             grid: this,
58998             record: r,
58999             field: field,
59000             originalValue: startValue,
59001             value: value,
59002             row: ed.row,
59003             column: ed.col,
59004             cancel:false,
59005             editor: ed
59006         };
59007         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
59008         cell.show();
59009           
59010         if(String(value) !== String(startValue)){
59011             
59012             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
59013                 r.set(field, e.value);
59014                 // if we are dealing with a combo box..
59015                 // then we also set the 'name' colum to be the displayField
59016                 if (ed.field.displayField && ed.field.name) {
59017                     r.set(ed.field.name, ed.field.el.dom.value);
59018                 }
59019                 
59020                 delete e.cancel; //?? why!!!
59021                 this.fireEvent("afteredit", e);
59022             }
59023         } else {
59024             this.fireEvent("afteredit", e); // always fire it!
59025         }
59026         this.view.focusCell(ed.row, ed.col);
59027     },
59028
59029     /**
59030      * Starts editing the specified for the specified row/column
59031      * @param {Number} rowIndex
59032      * @param {Number} colIndex
59033      */
59034     startEditing : function(row, col){
59035         this.stopEditing();
59036         if(this.colModel.isCellEditable(col, row)){
59037             this.view.ensureVisible(row, col, true);
59038           
59039             var r = this.dataSource.getAt(row);
59040             var field = this.colModel.getDataIndex(col);
59041             var cell = Roo.get(this.view.getCell(row,col));
59042             var e = {
59043                 grid: this,
59044                 record: r,
59045                 field: field,
59046                 value: r.data[field],
59047                 row: row,
59048                 column: col,
59049                 cancel:false 
59050             };
59051             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
59052                 this.editing = true;
59053                 var ed = this.colModel.getCellEditor(col, row);
59054                 
59055                 if (!ed) {
59056                     return;
59057                 }
59058                 if(!ed.rendered){
59059                     ed.render(ed.parentEl || document.body);
59060                 }
59061                 ed.field.reset();
59062                
59063                 cell.hide();
59064                 
59065                 (function(){ // complex but required for focus issues in safari, ie and opera
59066                     ed.row = row;
59067                     ed.col = col;
59068                     ed.record = r;
59069                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
59070                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
59071                     this.activeEditor = ed;
59072                     var v = r.data[field];
59073                     ed.startEdit(this.view.getCell(row, col), v);
59074                     // combo's with 'displayField and name set
59075                     if (ed.field.displayField && ed.field.name) {
59076                         ed.field.el.dom.value = r.data[ed.field.name];
59077                     }
59078                     
59079                     
59080                 }).defer(50, this);
59081             }
59082         }
59083     },
59084         
59085     /**
59086      * Stops any active editing
59087      */
59088     stopEditing : function(){
59089         if(this.activeEditor){
59090             this.activeEditor.completeEdit();
59091         }
59092         this.activeEditor = null;
59093     },
59094         
59095          /**
59096      * Called to get grid's drag proxy text, by default returns this.ddText.
59097      * @return {String}
59098      */
59099     getDragDropText : function(){
59100         var count = this.selModel.getSelectedCell() ? 1 : 0;
59101         return String.format(this.ddText, count, count == 1 ? '' : 's');
59102     }
59103         
59104 });/*
59105  * Based on:
59106  * Ext JS Library 1.1.1
59107  * Copyright(c) 2006-2007, Ext JS, LLC.
59108  *
59109  * Originally Released Under LGPL - original licence link has changed is not relivant.
59110  *
59111  * Fork - LGPL
59112  * <script type="text/javascript">
59113  */
59114
59115 // private - not really -- you end up using it !
59116 // This is a support class used internally by the Grid components
59117
59118 /**
59119  * @class Roo.grid.GridEditor
59120  * @extends Roo.Editor
59121  * Class for creating and editable grid elements.
59122  * @param {Object} config any settings (must include field)
59123  */
59124 Roo.grid.GridEditor = function(field, config){
59125     if (!config && field.field) {
59126         config = field;
59127         field = Roo.factory(config.field, Roo.form);
59128     }
59129     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
59130     field.monitorTab = false;
59131 };
59132
59133 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
59134     
59135     /**
59136      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
59137      */
59138     
59139     alignment: "tl-tl",
59140     autoSize: "width",
59141     hideEl : false,
59142     cls: "x-small-editor x-grid-editor",
59143     shim:false,
59144     shadow:"frame"
59145 });/*
59146  * Based on:
59147  * Ext JS Library 1.1.1
59148  * Copyright(c) 2006-2007, Ext JS, LLC.
59149  *
59150  * Originally Released Under LGPL - original licence link has changed is not relivant.
59151  *
59152  * Fork - LGPL
59153  * <script type="text/javascript">
59154  */
59155   
59156
59157   
59158 Roo.grid.PropertyRecord = Roo.data.Record.create([
59159     {name:'name',type:'string'},  'value'
59160 ]);
59161
59162
59163 Roo.grid.PropertyStore = function(grid, source){
59164     this.grid = grid;
59165     this.store = new Roo.data.Store({
59166         recordType : Roo.grid.PropertyRecord
59167     });
59168     this.store.on('update', this.onUpdate,  this);
59169     if(source){
59170         this.setSource(source);
59171     }
59172     Roo.grid.PropertyStore.superclass.constructor.call(this);
59173 };
59174
59175
59176
59177 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
59178     setSource : function(o){
59179         this.source = o;
59180         this.store.removeAll();
59181         var data = [];
59182         for(var k in o){
59183             if(this.isEditableValue(o[k])){
59184                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
59185             }
59186         }
59187         this.store.loadRecords({records: data}, {}, true);
59188     },
59189
59190     onUpdate : function(ds, record, type){
59191         if(type == Roo.data.Record.EDIT){
59192             var v = record.data['value'];
59193             var oldValue = record.modified['value'];
59194             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
59195                 this.source[record.id] = v;
59196                 record.commit();
59197                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
59198             }else{
59199                 record.reject();
59200             }
59201         }
59202     },
59203
59204     getProperty : function(row){
59205        return this.store.getAt(row);
59206     },
59207
59208     isEditableValue: function(val){
59209         if(val && val instanceof Date){
59210             return true;
59211         }else if(typeof val == 'object' || typeof val == 'function'){
59212             return false;
59213         }
59214         return true;
59215     },
59216
59217     setValue : function(prop, value){
59218         this.source[prop] = value;
59219         this.store.getById(prop).set('value', value);
59220     },
59221
59222     getSource : function(){
59223         return this.source;
59224     }
59225 });
59226
59227 Roo.grid.PropertyColumnModel = function(grid, store){
59228     this.grid = grid;
59229     var g = Roo.grid;
59230     g.PropertyColumnModel.superclass.constructor.call(this, [
59231         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
59232         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
59233     ]);
59234     this.store = store;
59235     this.bselect = Roo.DomHelper.append(document.body, {
59236         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
59237             {tag: 'option', value: 'true', html: 'true'},
59238             {tag: 'option', value: 'false', html: 'false'}
59239         ]
59240     });
59241     Roo.id(this.bselect);
59242     var f = Roo.form;
59243     this.editors = {
59244         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
59245         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
59246         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
59247         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
59248         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
59249     };
59250     this.renderCellDelegate = this.renderCell.createDelegate(this);
59251     this.renderPropDelegate = this.renderProp.createDelegate(this);
59252 };
59253
59254 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
59255     
59256     
59257     nameText : 'Name',
59258     valueText : 'Value',
59259     
59260     dateFormat : 'm/j/Y',
59261     
59262     
59263     renderDate : function(dateVal){
59264         return dateVal.dateFormat(this.dateFormat);
59265     },
59266
59267     renderBool : function(bVal){
59268         return bVal ? 'true' : 'false';
59269     },
59270
59271     isCellEditable : function(colIndex, rowIndex){
59272         return colIndex == 1;
59273     },
59274
59275     getRenderer : function(col){
59276         return col == 1 ?
59277             this.renderCellDelegate : this.renderPropDelegate;
59278     },
59279
59280     renderProp : function(v){
59281         return this.getPropertyName(v);
59282     },
59283
59284     renderCell : function(val){
59285         var rv = val;
59286         if(val instanceof Date){
59287             rv = this.renderDate(val);
59288         }else if(typeof val == 'boolean'){
59289             rv = this.renderBool(val);
59290         }
59291         return Roo.util.Format.htmlEncode(rv);
59292     },
59293
59294     getPropertyName : function(name){
59295         var pn = this.grid.propertyNames;
59296         return pn && pn[name] ? pn[name] : name;
59297     },
59298
59299     getCellEditor : function(colIndex, rowIndex){
59300         var p = this.store.getProperty(rowIndex);
59301         var n = p.data['name'], val = p.data['value'];
59302         
59303         if(typeof(this.grid.customEditors[n]) == 'string'){
59304             return this.editors[this.grid.customEditors[n]];
59305         }
59306         if(typeof(this.grid.customEditors[n]) != 'undefined'){
59307             return this.grid.customEditors[n];
59308         }
59309         if(val instanceof Date){
59310             return this.editors['date'];
59311         }else if(typeof val == 'number'){
59312             return this.editors['number'];
59313         }else if(typeof val == 'boolean'){
59314             return this.editors['boolean'];
59315         }else{
59316             return this.editors['string'];
59317         }
59318     }
59319 });
59320
59321 /**
59322  * @class Roo.grid.PropertyGrid
59323  * @extends Roo.grid.EditorGrid
59324  * This class represents the  interface of a component based property grid control.
59325  * <br><br>Usage:<pre><code>
59326  var grid = new Roo.grid.PropertyGrid("my-container-id", {
59327       
59328  });
59329  // set any options
59330  grid.render();
59331  * </code></pre>
59332   
59333  * @constructor
59334  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
59335  * The container MUST have some type of size defined for the grid to fill. The container will be
59336  * automatically set to position relative if it isn't already.
59337  * @param {Object} config A config object that sets properties on this grid.
59338  */
59339 Roo.grid.PropertyGrid = function(container, config){
59340     config = config || {};
59341     var store = new Roo.grid.PropertyStore(this);
59342     this.store = store;
59343     var cm = new Roo.grid.PropertyColumnModel(this, store);
59344     store.store.sort('name', 'ASC');
59345     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
59346         ds: store.store,
59347         cm: cm,
59348         enableColLock:false,
59349         enableColumnMove:false,
59350         stripeRows:false,
59351         trackMouseOver: false,
59352         clicksToEdit:1
59353     }, config));
59354     this.getGridEl().addClass('x-props-grid');
59355     this.lastEditRow = null;
59356     this.on('columnresize', this.onColumnResize, this);
59357     this.addEvents({
59358          /**
59359              * @event beforepropertychange
59360              * Fires before a property changes (return false to stop?)
59361              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
59362              * @param {String} id Record Id
59363              * @param {String} newval New Value
59364          * @param {String} oldval Old Value
59365              */
59366         "beforepropertychange": true,
59367         /**
59368              * @event propertychange
59369              * Fires after a property changes
59370              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
59371              * @param {String} id Record Id
59372              * @param {String} newval New Value
59373          * @param {String} oldval Old Value
59374              */
59375         "propertychange": true
59376     });
59377     this.customEditors = this.customEditors || {};
59378 };
59379 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
59380     
59381      /**
59382      * @cfg {Object} customEditors map of colnames=> custom editors.
59383      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
59384      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
59385      * false disables editing of the field.
59386          */
59387     
59388       /**
59389      * @cfg {Object} propertyNames map of property Names to their displayed value
59390          */
59391     
59392     render : function(){
59393         Roo.grid.PropertyGrid.superclass.render.call(this);
59394         this.autoSize.defer(100, this);
59395     },
59396
59397     autoSize : function(){
59398         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
59399         if(this.view){
59400             this.view.fitColumns();
59401         }
59402     },
59403
59404     onColumnResize : function(){
59405         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
59406         this.autoSize();
59407     },
59408     /**
59409      * Sets the data for the Grid
59410      * accepts a Key => Value object of all the elements avaiable.
59411      * @param {Object} data  to appear in grid.
59412      */
59413     setSource : function(source){
59414         this.store.setSource(source);
59415         //this.autoSize();
59416     },
59417     /**
59418      * Gets all the data from the grid.
59419      * @return {Object} data  data stored in grid
59420      */
59421     getSource : function(){
59422         return this.store.getSource();
59423     }
59424 });/*
59425   
59426  * Licence LGPL
59427  
59428  */
59429  
59430 /**
59431  * @class Roo.grid.Calendar
59432  * @extends Roo.util.Grid
59433  * This class extends the Grid to provide a calendar widget
59434  * <br><br>Usage:<pre><code>
59435  var grid = new Roo.grid.Calendar("my-container-id", {
59436      ds: myDataStore,
59437      cm: myColModel,
59438      selModel: mySelectionModel,
59439      autoSizeColumns: true,
59440      monitorWindowResize: false,
59441      trackMouseOver: true
59442      eventstore : real data store..
59443  });
59444  // set any options
59445  grid.render();
59446   
59447   * @constructor
59448  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
59449  * The container MUST have some type of size defined for the grid to fill. The container will be
59450  * automatically set to position relative if it isn't already.
59451  * @param {Object} config A config object that sets properties on this grid.
59452  */
59453 Roo.grid.Calendar = function(container, config){
59454         // initialize the container
59455         this.container = Roo.get(container);
59456         this.container.update("");
59457         this.container.setStyle("overflow", "hidden");
59458     this.container.addClass('x-grid-container');
59459
59460     this.id = this.container.id;
59461
59462     Roo.apply(this, config);
59463     // check and correct shorthanded configs
59464     
59465     var rows = [];
59466     var d =1;
59467     for (var r = 0;r < 6;r++) {
59468         
59469         rows[r]=[];
59470         for (var c =0;c < 7;c++) {
59471             rows[r][c]= '';
59472         }
59473     }
59474     if (this.eventStore) {
59475         this.eventStore= Roo.factory(this.eventStore, Roo.data);
59476         this.eventStore.on('load',this.onLoad, this);
59477         this.eventStore.on('beforeload',this.clearEvents, this);
59478          
59479     }
59480     
59481     this.dataSource = new Roo.data.Store({
59482             proxy: new Roo.data.MemoryProxy(rows),
59483             reader: new Roo.data.ArrayReader({}, [
59484                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
59485     });
59486
59487     this.dataSource.load();
59488     this.ds = this.dataSource;
59489     this.ds.xmodule = this.xmodule || false;
59490     
59491     
59492     var cellRender = function(v,x,r)
59493     {
59494         return String.format(
59495             '<div class="fc-day  fc-widget-content"><div>' +
59496                 '<div class="fc-event-container"></div>' +
59497                 '<div class="fc-day-number">{0}</div>'+
59498                 
59499                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
59500             '</div></div>', v);
59501     
59502     }
59503     
59504     
59505     this.colModel = new Roo.grid.ColumnModel( [
59506         {
59507             xtype: 'ColumnModel',
59508             xns: Roo.grid,
59509             dataIndex : 'weekday0',
59510             header : 'Sunday',
59511             renderer : cellRender
59512         },
59513         {
59514             xtype: 'ColumnModel',
59515             xns: Roo.grid,
59516             dataIndex : 'weekday1',
59517             header : 'Monday',
59518             renderer : cellRender
59519         },
59520         {
59521             xtype: 'ColumnModel',
59522             xns: Roo.grid,
59523             dataIndex : 'weekday2',
59524             header : 'Tuesday',
59525             renderer : cellRender
59526         },
59527         {
59528             xtype: 'ColumnModel',
59529             xns: Roo.grid,
59530             dataIndex : 'weekday3',
59531             header : 'Wednesday',
59532             renderer : cellRender
59533         },
59534         {
59535             xtype: 'ColumnModel',
59536             xns: Roo.grid,
59537             dataIndex : 'weekday4',
59538             header : 'Thursday',
59539             renderer : cellRender
59540         },
59541         {
59542             xtype: 'ColumnModel',
59543             xns: Roo.grid,
59544             dataIndex : 'weekday5',
59545             header : 'Friday',
59546             renderer : cellRender
59547         },
59548         {
59549             xtype: 'ColumnModel',
59550             xns: Roo.grid,
59551             dataIndex : 'weekday6',
59552             header : 'Saturday',
59553             renderer : cellRender
59554         }
59555     ]);
59556     this.cm = this.colModel;
59557     this.cm.xmodule = this.xmodule || false;
59558  
59559         
59560           
59561     //this.selModel = new Roo.grid.CellSelectionModel();
59562     //this.sm = this.selModel;
59563     //this.selModel.init(this);
59564     
59565     
59566     if(this.width){
59567         this.container.setWidth(this.width);
59568     }
59569
59570     if(this.height){
59571         this.container.setHeight(this.height);
59572     }
59573     /** @private */
59574         this.addEvents({
59575         // raw events
59576         /**
59577          * @event click
59578          * The raw click event for the entire grid.
59579          * @param {Roo.EventObject} e
59580          */
59581         "click" : true,
59582         /**
59583          * @event dblclick
59584          * The raw dblclick event for the entire grid.
59585          * @param {Roo.EventObject} e
59586          */
59587         "dblclick" : true,
59588         /**
59589          * @event contextmenu
59590          * The raw contextmenu event for the entire grid.
59591          * @param {Roo.EventObject} e
59592          */
59593         "contextmenu" : true,
59594         /**
59595          * @event mousedown
59596          * The raw mousedown event for the entire grid.
59597          * @param {Roo.EventObject} e
59598          */
59599         "mousedown" : true,
59600         /**
59601          * @event mouseup
59602          * The raw mouseup event for the entire grid.
59603          * @param {Roo.EventObject} e
59604          */
59605         "mouseup" : true,
59606         /**
59607          * @event mouseover
59608          * The raw mouseover event for the entire grid.
59609          * @param {Roo.EventObject} e
59610          */
59611         "mouseover" : true,
59612         /**
59613          * @event mouseout
59614          * The raw mouseout event for the entire grid.
59615          * @param {Roo.EventObject} e
59616          */
59617         "mouseout" : true,
59618         /**
59619          * @event keypress
59620          * The raw keypress event for the entire grid.
59621          * @param {Roo.EventObject} e
59622          */
59623         "keypress" : true,
59624         /**
59625          * @event keydown
59626          * The raw keydown event for the entire grid.
59627          * @param {Roo.EventObject} e
59628          */
59629         "keydown" : true,
59630
59631         // custom events
59632
59633         /**
59634          * @event cellclick
59635          * Fires when a cell is clicked
59636          * @param {Grid} this
59637          * @param {Number} rowIndex
59638          * @param {Number} columnIndex
59639          * @param {Roo.EventObject} e
59640          */
59641         "cellclick" : true,
59642         /**
59643          * @event celldblclick
59644          * Fires when a cell is double clicked
59645          * @param {Grid} this
59646          * @param {Number} rowIndex
59647          * @param {Number} columnIndex
59648          * @param {Roo.EventObject} e
59649          */
59650         "celldblclick" : true,
59651         /**
59652          * @event rowclick
59653          * Fires when a row is clicked
59654          * @param {Grid} this
59655          * @param {Number} rowIndex
59656          * @param {Roo.EventObject} e
59657          */
59658         "rowclick" : true,
59659         /**
59660          * @event rowdblclick
59661          * Fires when a row is double clicked
59662          * @param {Grid} this
59663          * @param {Number} rowIndex
59664          * @param {Roo.EventObject} e
59665          */
59666         "rowdblclick" : true,
59667         /**
59668          * @event headerclick
59669          * Fires when a header is clicked
59670          * @param {Grid} this
59671          * @param {Number} columnIndex
59672          * @param {Roo.EventObject} e
59673          */
59674         "headerclick" : true,
59675         /**
59676          * @event headerdblclick
59677          * Fires when a header cell is double clicked
59678          * @param {Grid} this
59679          * @param {Number} columnIndex
59680          * @param {Roo.EventObject} e
59681          */
59682         "headerdblclick" : true,
59683         /**
59684          * @event rowcontextmenu
59685          * Fires when a row is right clicked
59686          * @param {Grid} this
59687          * @param {Number} rowIndex
59688          * @param {Roo.EventObject} e
59689          */
59690         "rowcontextmenu" : true,
59691         /**
59692          * @event cellcontextmenu
59693          * Fires when a cell is right clicked
59694          * @param {Grid} this
59695          * @param {Number} rowIndex
59696          * @param {Number} cellIndex
59697          * @param {Roo.EventObject} e
59698          */
59699          "cellcontextmenu" : true,
59700         /**
59701          * @event headercontextmenu
59702          * Fires when a header is right clicked
59703          * @param {Grid} this
59704          * @param {Number} columnIndex
59705          * @param {Roo.EventObject} e
59706          */
59707         "headercontextmenu" : true,
59708         /**
59709          * @event bodyscroll
59710          * Fires when the body element is scrolled
59711          * @param {Number} scrollLeft
59712          * @param {Number} scrollTop
59713          */
59714         "bodyscroll" : true,
59715         /**
59716          * @event columnresize
59717          * Fires when the user resizes a column
59718          * @param {Number} columnIndex
59719          * @param {Number} newSize
59720          */
59721         "columnresize" : true,
59722         /**
59723          * @event columnmove
59724          * Fires when the user moves a column
59725          * @param {Number} oldIndex
59726          * @param {Number} newIndex
59727          */
59728         "columnmove" : true,
59729         /**
59730          * @event startdrag
59731          * Fires when row(s) start being dragged
59732          * @param {Grid} this
59733          * @param {Roo.GridDD} dd The drag drop object
59734          * @param {event} e The raw browser event
59735          */
59736         "startdrag" : true,
59737         /**
59738          * @event enddrag
59739          * Fires when a drag operation is complete
59740          * @param {Grid} this
59741          * @param {Roo.GridDD} dd The drag drop object
59742          * @param {event} e The raw browser event
59743          */
59744         "enddrag" : true,
59745         /**
59746          * @event dragdrop
59747          * Fires when dragged row(s) are dropped on a valid DD target
59748          * @param {Grid} this
59749          * @param {Roo.GridDD} dd The drag drop object
59750          * @param {String} targetId The target drag drop object
59751          * @param {event} e The raw browser event
59752          */
59753         "dragdrop" : true,
59754         /**
59755          * @event dragover
59756          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
59757          * @param {Grid} this
59758          * @param {Roo.GridDD} dd The drag drop object
59759          * @param {String} targetId The target drag drop object
59760          * @param {event} e The raw browser event
59761          */
59762         "dragover" : true,
59763         /**
59764          * @event dragenter
59765          *  Fires when the dragged row(s) first cross another DD target while being dragged
59766          * @param {Grid} this
59767          * @param {Roo.GridDD} dd The drag drop object
59768          * @param {String} targetId The target drag drop object
59769          * @param {event} e The raw browser event
59770          */
59771         "dragenter" : true,
59772         /**
59773          * @event dragout
59774          * Fires when the dragged row(s) leave another DD target while being dragged
59775          * @param {Grid} this
59776          * @param {Roo.GridDD} dd The drag drop object
59777          * @param {String} targetId The target drag drop object
59778          * @param {event} e The raw browser event
59779          */
59780         "dragout" : true,
59781         /**
59782          * @event rowclass
59783          * Fires when a row is rendered, so you can change add a style to it.
59784          * @param {GridView} gridview   The grid view
59785          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
59786          */
59787         'rowclass' : true,
59788
59789         /**
59790          * @event render
59791          * Fires when the grid is rendered
59792          * @param {Grid} grid
59793          */
59794         'render' : true,
59795             /**
59796              * @event select
59797              * Fires when a date is selected
59798              * @param {DatePicker} this
59799              * @param {Date} date The selected date
59800              */
59801         'select': true,
59802         /**
59803              * @event monthchange
59804              * Fires when the displayed month changes 
59805              * @param {DatePicker} this
59806              * @param {Date} date The selected month
59807              */
59808         'monthchange': true,
59809         /**
59810              * @event evententer
59811              * Fires when mouse over an event
59812              * @param {Calendar} this
59813              * @param {event} Event
59814              */
59815         'evententer': true,
59816         /**
59817              * @event eventleave
59818              * Fires when the mouse leaves an
59819              * @param {Calendar} this
59820              * @param {event}
59821              */
59822         'eventleave': true,
59823         /**
59824              * @event eventclick
59825              * Fires when the mouse click an
59826              * @param {Calendar} this
59827              * @param {event}
59828              */
59829         'eventclick': true,
59830         /**
59831              * @event eventrender
59832              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
59833              * @param {Calendar} this
59834              * @param {data} data to be modified
59835              */
59836         'eventrender': true
59837         
59838     });
59839
59840     Roo.grid.Grid.superclass.constructor.call(this);
59841     this.on('render', function() {
59842         this.view.el.addClass('x-grid-cal'); 
59843         
59844         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
59845
59846     },this);
59847     
59848     if (!Roo.grid.Calendar.style) {
59849         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
59850             
59851             
59852             '.x-grid-cal .x-grid-col' :  {
59853                 height: 'auto !important',
59854                 'vertical-align': 'top'
59855             },
59856             '.x-grid-cal  .fc-event-hori' : {
59857                 height: '14px'
59858             }
59859              
59860             
59861         }, Roo.id());
59862     }
59863
59864     
59865     
59866 };
59867 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
59868     /**
59869      * @cfg {Store} eventStore The store that loads events.
59870      */
59871     eventStore : 25,
59872
59873      
59874     activeDate : false,
59875     startDay : 0,
59876     autoWidth : true,
59877     monitorWindowResize : false,
59878
59879     
59880     resizeColumns : function() {
59881         var col = (this.view.el.getWidth() / 7) - 3;
59882         // loop through cols, and setWidth
59883         for(var i =0 ; i < 7 ; i++){
59884             this.cm.setColumnWidth(i, col);
59885         }
59886     },
59887      setDate :function(date) {
59888         
59889         Roo.log('setDate?');
59890         
59891         this.resizeColumns();
59892         var vd = this.activeDate;
59893         this.activeDate = date;
59894 //        if(vd && this.el){
59895 //            var t = date.getTime();
59896 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
59897 //                Roo.log('using add remove');
59898 //                
59899 //                this.fireEvent('monthchange', this, date);
59900 //                
59901 //                this.cells.removeClass("fc-state-highlight");
59902 //                this.cells.each(function(c){
59903 //                   if(c.dateValue == t){
59904 //                       c.addClass("fc-state-highlight");
59905 //                       setTimeout(function(){
59906 //                            try{c.dom.firstChild.focus();}catch(e){}
59907 //                       }, 50);
59908 //                       return false;
59909 //                   }
59910 //                   return true;
59911 //                });
59912 //                return;
59913 //            }
59914 //        }
59915         
59916         var days = date.getDaysInMonth();
59917         
59918         var firstOfMonth = date.getFirstDateOfMonth();
59919         var startingPos = firstOfMonth.getDay()-this.startDay;
59920         
59921         if(startingPos < this.startDay){
59922             startingPos += 7;
59923         }
59924         
59925         var pm = date.add(Date.MONTH, -1);
59926         var prevStart = pm.getDaysInMonth()-startingPos;
59927 //        
59928         
59929         
59930         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
59931         
59932         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
59933         //this.cells.addClassOnOver('fc-state-hover');
59934         
59935         var cells = this.cells.elements;
59936         var textEls = this.textNodes;
59937         
59938         //Roo.each(cells, function(cell){
59939         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
59940         //});
59941         
59942         days += startingPos;
59943
59944         // convert everything to numbers so it's fast
59945         var day = 86400000;
59946         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
59947         //Roo.log(d);
59948         //Roo.log(pm);
59949         //Roo.log(prevStart);
59950         
59951         var today = new Date().clearTime().getTime();
59952         var sel = date.clearTime().getTime();
59953         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
59954         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
59955         var ddMatch = this.disabledDatesRE;
59956         var ddText = this.disabledDatesText;
59957         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
59958         var ddaysText = this.disabledDaysText;
59959         var format = this.format;
59960         
59961         var setCellClass = function(cal, cell){
59962             
59963             //Roo.log('set Cell Class');
59964             cell.title = "";
59965             var t = d.getTime();
59966             
59967             //Roo.log(d);
59968             
59969             
59970             cell.dateValue = t;
59971             if(t == today){
59972                 cell.className += " fc-today";
59973                 cell.className += " fc-state-highlight";
59974                 cell.title = cal.todayText;
59975             }
59976             if(t == sel){
59977                 // disable highlight in other month..
59978                 cell.className += " fc-state-highlight";
59979                 
59980             }
59981             // disabling
59982             if(t < min) {
59983                 //cell.className = " fc-state-disabled";
59984                 cell.title = cal.minText;
59985                 return;
59986             }
59987             if(t > max) {
59988                 //cell.className = " fc-state-disabled";
59989                 cell.title = cal.maxText;
59990                 return;
59991             }
59992             if(ddays){
59993                 if(ddays.indexOf(d.getDay()) != -1){
59994                     // cell.title = ddaysText;
59995                    // cell.className = " fc-state-disabled";
59996                 }
59997             }
59998             if(ddMatch && format){
59999                 var fvalue = d.dateFormat(format);
60000                 if(ddMatch.test(fvalue)){
60001                     cell.title = ddText.replace("%0", fvalue);
60002                    cell.className = " fc-state-disabled";
60003                 }
60004             }
60005             
60006             if (!cell.initialClassName) {
60007                 cell.initialClassName = cell.dom.className;
60008             }
60009             
60010             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
60011         };
60012
60013         var i = 0;
60014         
60015         for(; i < startingPos; i++) {
60016             cells[i].dayName =  (++prevStart);
60017             Roo.log(textEls[i]);
60018             d.setDate(d.getDate()+1);
60019             
60020             //cells[i].className = "fc-past fc-other-month";
60021             setCellClass(this, cells[i]);
60022         }
60023         
60024         var intDay = 0;
60025         
60026         for(; i < days; i++){
60027             intDay = i - startingPos + 1;
60028             cells[i].dayName =  (intDay);
60029             d.setDate(d.getDate()+1);
60030             
60031             cells[i].className = ''; // "x-date-active";
60032             setCellClass(this, cells[i]);
60033         }
60034         var extraDays = 0;
60035         
60036         for(; i < 42; i++) {
60037             //textEls[i].innerHTML = (++extraDays);
60038             
60039             d.setDate(d.getDate()+1);
60040             cells[i].dayName = (++extraDays);
60041             cells[i].className = "fc-future fc-other-month";
60042             setCellClass(this, cells[i]);
60043         }
60044         
60045         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
60046         
60047         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
60048         
60049         // this will cause all the cells to mis
60050         var rows= [];
60051         var i =0;
60052         for (var r = 0;r < 6;r++) {
60053             for (var c =0;c < 7;c++) {
60054                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
60055             }    
60056         }
60057         
60058         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
60059         for(i=0;i<cells.length;i++) {
60060             
60061             this.cells.elements[i].dayName = cells[i].dayName ;
60062             this.cells.elements[i].className = cells[i].className;
60063             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
60064             this.cells.elements[i].title = cells[i].title ;
60065             this.cells.elements[i].dateValue = cells[i].dateValue ;
60066         }
60067         
60068         
60069         
60070         
60071         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
60072         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
60073         
60074         ////if(totalRows != 6){
60075             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
60076            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
60077        // }
60078         
60079         this.fireEvent('monthchange', this, date);
60080         
60081         
60082     },
60083  /**
60084      * Returns the grid's SelectionModel.
60085      * @return {SelectionModel}
60086      */
60087     getSelectionModel : function(){
60088         if(!this.selModel){
60089             this.selModel = new Roo.grid.CellSelectionModel();
60090         }
60091         return this.selModel;
60092     },
60093
60094     load: function() {
60095         this.eventStore.load()
60096         
60097         
60098         
60099     },
60100     
60101     findCell : function(dt) {
60102         dt = dt.clearTime().getTime();
60103         var ret = false;
60104         this.cells.each(function(c){
60105             //Roo.log("check " +c.dateValue + '?=' + dt);
60106             if(c.dateValue == dt){
60107                 ret = c;
60108                 return false;
60109             }
60110             return true;
60111         });
60112         
60113         return ret;
60114     },
60115     
60116     findCells : function(rec) {
60117         var s = rec.data.start_dt.clone().clearTime().getTime();
60118        // Roo.log(s);
60119         var e= rec.data.end_dt.clone().clearTime().getTime();
60120        // Roo.log(e);
60121         var ret = [];
60122         this.cells.each(function(c){
60123              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
60124             
60125             if(c.dateValue > e){
60126                 return ;
60127             }
60128             if(c.dateValue < s){
60129                 return ;
60130             }
60131             ret.push(c);
60132         });
60133         
60134         return ret;    
60135     },
60136     
60137     findBestRow: function(cells)
60138     {
60139         var ret = 0;
60140         
60141         for (var i =0 ; i < cells.length;i++) {
60142             ret  = Math.max(cells[i].rows || 0,ret);
60143         }
60144         return ret;
60145         
60146     },
60147     
60148     
60149     addItem : function(rec)
60150     {
60151         // look for vertical location slot in
60152         var cells = this.findCells(rec);
60153         
60154         rec.row = this.findBestRow(cells);
60155         
60156         // work out the location.
60157         
60158         var crow = false;
60159         var rows = [];
60160         for(var i =0; i < cells.length; i++) {
60161             if (!crow) {
60162                 crow = {
60163                     start : cells[i],
60164                     end :  cells[i]
60165                 };
60166                 continue;
60167             }
60168             if (crow.start.getY() == cells[i].getY()) {
60169                 // on same row.
60170                 crow.end = cells[i];
60171                 continue;
60172             }
60173             // different row.
60174             rows.push(crow);
60175             crow = {
60176                 start: cells[i],
60177                 end : cells[i]
60178             };
60179             
60180         }
60181         
60182         rows.push(crow);
60183         rec.els = [];
60184         rec.rows = rows;
60185         rec.cells = cells;
60186         for (var i = 0; i < cells.length;i++) {
60187             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
60188             
60189         }
60190         
60191         
60192     },
60193     
60194     clearEvents: function() {
60195         
60196         if (!this.eventStore.getCount()) {
60197             return;
60198         }
60199         // reset number of rows in cells.
60200         Roo.each(this.cells.elements, function(c){
60201             c.rows = 0;
60202         });
60203         
60204         this.eventStore.each(function(e) {
60205             this.clearEvent(e);
60206         },this);
60207         
60208     },
60209     
60210     clearEvent : function(ev)
60211     {
60212         if (ev.els) {
60213             Roo.each(ev.els, function(el) {
60214                 el.un('mouseenter' ,this.onEventEnter, this);
60215                 el.un('mouseleave' ,this.onEventLeave, this);
60216                 el.remove();
60217             },this);
60218             ev.els = [];
60219         }
60220     },
60221     
60222     
60223     renderEvent : function(ev,ctr) {
60224         if (!ctr) {
60225              ctr = this.view.el.select('.fc-event-container',true).first();
60226         }
60227         
60228          
60229         this.clearEvent(ev);
60230             //code
60231        
60232         
60233         
60234         ev.els = [];
60235         var cells = ev.cells;
60236         var rows = ev.rows;
60237         this.fireEvent('eventrender', this, ev);
60238         
60239         for(var i =0; i < rows.length; i++) {
60240             
60241             cls = '';
60242             if (i == 0) {
60243                 cls += ' fc-event-start';
60244             }
60245             if ((i+1) == rows.length) {
60246                 cls += ' fc-event-end';
60247             }
60248             
60249             //Roo.log(ev.data);
60250             // how many rows should it span..
60251             var cg = this.eventTmpl.append(ctr,Roo.apply({
60252                 fccls : cls
60253                 
60254             }, ev.data) , true);
60255             
60256             
60257             cg.on('mouseenter' ,this.onEventEnter, this, ev);
60258             cg.on('mouseleave' ,this.onEventLeave, this, ev);
60259             cg.on('click', this.onEventClick, this, ev);
60260             
60261             ev.els.push(cg);
60262             
60263             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
60264             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
60265             //Roo.log(cg);
60266              
60267             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
60268             cg.setWidth(ebox.right - sbox.x -2);
60269         }
60270     },
60271     
60272     renderEvents: function()
60273     {   
60274         // first make sure there is enough space..
60275         
60276         if (!this.eventTmpl) {
60277             this.eventTmpl = new Roo.Template(
60278                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
60279                     '<div class="fc-event-inner">' +
60280                         '<span class="fc-event-time">{time}</span>' +
60281                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
60282                     '</div>' +
60283                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
60284                 '</div>'
60285             );
60286                 
60287         }
60288                
60289         
60290         
60291         this.cells.each(function(c) {
60292             //Roo.log(c.select('.fc-day-content div',true).first());
60293             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
60294         });
60295         
60296         var ctr = this.view.el.select('.fc-event-container',true).first();
60297         
60298         var cls;
60299         this.eventStore.each(function(ev){
60300             
60301             this.renderEvent(ev);
60302              
60303              
60304         }, this);
60305         this.view.layout();
60306         
60307     },
60308     
60309     onEventEnter: function (e, el,event,d) {
60310         this.fireEvent('evententer', this, el, event);
60311     },
60312     
60313     onEventLeave: function (e, el,event,d) {
60314         this.fireEvent('eventleave', this, el, event);
60315     },
60316     
60317     onEventClick: function (e, el,event,d) {
60318         this.fireEvent('eventclick', this, el, event);
60319     },
60320     
60321     onMonthChange: function () {
60322         this.store.load();
60323     },
60324     
60325     onLoad: function () {
60326         
60327         //Roo.log('calendar onload');
60328 //         
60329         if(this.eventStore.getCount() > 0){
60330             
60331            
60332             
60333             this.eventStore.each(function(d){
60334                 
60335                 
60336                 // FIXME..
60337                 var add =   d.data;
60338                 if (typeof(add.end_dt) == 'undefined')  {
60339                     Roo.log("Missing End time in calendar data: ");
60340                     Roo.log(d);
60341                     return;
60342                 }
60343                 if (typeof(add.start_dt) == 'undefined')  {
60344                     Roo.log("Missing Start time in calendar data: ");
60345                     Roo.log(d);
60346                     return;
60347                 }
60348                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
60349                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
60350                 add.id = add.id || d.id;
60351                 add.title = add.title || '??';
60352                 
60353                 this.addItem(d);
60354                 
60355              
60356             },this);
60357         }
60358         
60359         this.renderEvents();
60360     }
60361     
60362
60363 });
60364 /*
60365  grid : {
60366                 xtype: 'Grid',
60367                 xns: Roo.grid,
60368                 listeners : {
60369                     render : function ()
60370                     {
60371                         _this.grid = this;
60372                         
60373                         if (!this.view.el.hasClass('course-timesheet')) {
60374                             this.view.el.addClass('course-timesheet');
60375                         }
60376                         if (this.tsStyle) {
60377                             this.ds.load({});
60378                             return; 
60379                         }
60380                         Roo.log('width');
60381                         Roo.log(_this.grid.view.el.getWidth());
60382                         
60383                         
60384                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
60385                             '.course-timesheet .x-grid-row' : {
60386                                 height: '80px'
60387                             },
60388                             '.x-grid-row td' : {
60389                                 'vertical-align' : 0
60390                             },
60391                             '.course-edit-link' : {
60392                                 'color' : 'blue',
60393                                 'text-overflow' : 'ellipsis',
60394                                 'overflow' : 'hidden',
60395                                 'white-space' : 'nowrap',
60396                                 'cursor' : 'pointer'
60397                             },
60398                             '.sub-link' : {
60399                                 'color' : 'green'
60400                             },
60401                             '.de-act-sup-link' : {
60402                                 'color' : 'purple',
60403                                 'text-decoration' : 'line-through'
60404                             },
60405                             '.de-act-link' : {
60406                                 'color' : 'red',
60407                                 'text-decoration' : 'line-through'
60408                             },
60409                             '.course-timesheet .course-highlight' : {
60410                                 'border-top-style': 'dashed !important',
60411                                 'border-bottom-bottom': 'dashed !important'
60412                             },
60413                             '.course-timesheet .course-item' : {
60414                                 'font-family'   : 'tahoma, arial, helvetica',
60415                                 'font-size'     : '11px',
60416                                 'overflow'      : 'hidden',
60417                                 'padding-left'  : '10px',
60418                                 'padding-right' : '10px',
60419                                 'padding-top' : '10px' 
60420                             }
60421                             
60422                         }, Roo.id());
60423                                 this.ds.load({});
60424                     }
60425                 },
60426                 autoWidth : true,
60427                 monitorWindowResize : false,
60428                 cellrenderer : function(v,x,r)
60429                 {
60430                     return v;
60431                 },
60432                 sm : {
60433                     xtype: 'CellSelectionModel',
60434                     xns: Roo.grid
60435                 },
60436                 dataSource : {
60437                     xtype: 'Store',
60438                     xns: Roo.data,
60439                     listeners : {
60440                         beforeload : function (_self, options)
60441                         {
60442                             options.params = options.params || {};
60443                             options.params._month = _this.monthField.getValue();
60444                             options.params.limit = 9999;
60445                             options.params['sort'] = 'when_dt';    
60446                             options.params['dir'] = 'ASC';    
60447                             this.proxy.loadResponse = this.loadResponse;
60448                             Roo.log("load?");
60449                             //this.addColumns();
60450                         },
60451                         load : function (_self, records, options)
60452                         {
60453                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
60454                                 // if you click on the translation.. you can edit it...
60455                                 var el = Roo.get(this);
60456                                 var id = el.dom.getAttribute('data-id');
60457                                 var d = el.dom.getAttribute('data-date');
60458                                 var t = el.dom.getAttribute('data-time');
60459                                 //var id = this.child('span').dom.textContent;
60460                                 
60461                                 //Roo.log(this);
60462                                 Pman.Dialog.CourseCalendar.show({
60463                                     id : id,
60464                                     when_d : d,
60465                                     when_t : t,
60466                                     productitem_active : id ? 1 : 0
60467                                 }, function() {
60468                                     _this.grid.ds.load({});
60469                                 });
60470                            
60471                            });
60472                            
60473                            _this.panel.fireEvent('resize', [ '', '' ]);
60474                         }
60475                     },
60476                     loadResponse : function(o, success, response){
60477                             // this is overridden on before load..
60478                             
60479                             Roo.log("our code?");       
60480                             //Roo.log(success);
60481                             //Roo.log(response)
60482                             delete this.activeRequest;
60483                             if(!success){
60484                                 this.fireEvent("loadexception", this, o, response);
60485                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
60486                                 return;
60487                             }
60488                             var result;
60489                             try {
60490                                 result = o.reader.read(response);
60491                             }catch(e){
60492                                 Roo.log("load exception?");
60493                                 this.fireEvent("loadexception", this, o, response, e);
60494                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
60495                                 return;
60496                             }
60497                             Roo.log("ready...");        
60498                             // loop through result.records;
60499                             // and set this.tdate[date] = [] << array of records..
60500                             _this.tdata  = {};
60501                             Roo.each(result.records, function(r){
60502                                 //Roo.log(r.data);
60503                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
60504                                     _this.tdata[r.data.when_dt.format('j')] = [];
60505                                 }
60506                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
60507                             });
60508                             
60509                             //Roo.log(_this.tdata);
60510                             
60511                             result.records = [];
60512                             result.totalRecords = 6;
60513                     
60514                             // let's generate some duumy records for the rows.
60515                             //var st = _this.dateField.getValue();
60516                             
60517                             // work out monday..
60518                             //st = st.add(Date.DAY, -1 * st.format('w'));
60519                             
60520                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60521                             
60522                             var firstOfMonth = date.getFirstDayOfMonth();
60523                             var days = date.getDaysInMonth();
60524                             var d = 1;
60525                             var firstAdded = false;
60526                             for (var i = 0; i < result.totalRecords ; i++) {
60527                                 //var d= st.add(Date.DAY, i);
60528                                 var row = {};
60529                                 var added = 0;
60530                                 for(var w = 0 ; w < 7 ; w++){
60531                                     if(!firstAdded && firstOfMonth != w){
60532                                         continue;
60533                                     }
60534                                     if(d > days){
60535                                         continue;
60536                                     }
60537                                     firstAdded = true;
60538                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
60539                                     row['weekday'+w] = String.format(
60540                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
60541                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
60542                                                     d,
60543                                                     date.format('Y-m-')+dd
60544                                                 );
60545                                     added++;
60546                                     if(typeof(_this.tdata[d]) != 'undefined'){
60547                                         Roo.each(_this.tdata[d], function(r){
60548                                             var is_sub = '';
60549                                             var deactive = '';
60550                                             var id = r.id;
60551                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
60552                                             if(r.parent_id*1>0){
60553                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
60554                                                 id = r.parent_id;
60555                                             }
60556                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
60557                                                 deactive = 'de-act-link';
60558                                             }
60559                                             
60560                                             row['weekday'+w] += String.format(
60561                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
60562                                                     id, //0
60563                                                     r.product_id_name, //1
60564                                                     r.when_dt.format('h:ia'), //2
60565                                                     is_sub, //3
60566                                                     deactive, //4
60567                                                     desc // 5
60568                                             );
60569                                         });
60570                                     }
60571                                     d++;
60572                                 }
60573                                 
60574                                 // only do this if something added..
60575                                 if(added > 0){ 
60576                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
60577                                 }
60578                                 
60579                                 
60580                                 // push it twice. (second one with an hour..
60581                                 
60582                             }
60583                             //Roo.log(result);
60584                             this.fireEvent("load", this, o, o.request.arg);
60585                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
60586                         },
60587                     sortInfo : {field: 'when_dt', direction : 'ASC' },
60588                     proxy : {
60589                         xtype: 'HttpProxy',
60590                         xns: Roo.data,
60591                         method : 'GET',
60592                         url : baseURL + '/Roo/Shop_course.php'
60593                     },
60594                     reader : {
60595                         xtype: 'JsonReader',
60596                         xns: Roo.data,
60597                         id : 'id',
60598                         fields : [
60599                             {
60600                                 'name': 'id',
60601                                 'type': 'int'
60602                             },
60603                             {
60604                                 'name': 'when_dt',
60605                                 'type': 'string'
60606                             },
60607                             {
60608                                 'name': 'end_dt',
60609                                 'type': 'string'
60610                             },
60611                             {
60612                                 'name': 'parent_id',
60613                                 'type': 'int'
60614                             },
60615                             {
60616                                 'name': 'product_id',
60617                                 'type': 'int'
60618                             },
60619                             {
60620                                 'name': 'productitem_id',
60621                                 'type': 'int'
60622                             },
60623                             {
60624                                 'name': 'guid',
60625                                 'type': 'int'
60626                             }
60627                         ]
60628                     }
60629                 },
60630                 toolbar : {
60631                     xtype: 'Toolbar',
60632                     xns: Roo,
60633                     items : [
60634                         {
60635                             xtype: 'Button',
60636                             xns: Roo.Toolbar,
60637                             listeners : {
60638                                 click : function (_self, e)
60639                                 {
60640                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60641                                     sd.setMonth(sd.getMonth()-1);
60642                                     _this.monthField.setValue(sd.format('Y-m-d'));
60643                                     _this.grid.ds.load({});
60644                                 }
60645                             },
60646                             text : "Back"
60647                         },
60648                         {
60649                             xtype: 'Separator',
60650                             xns: Roo.Toolbar
60651                         },
60652                         {
60653                             xtype: 'MonthField',
60654                             xns: Roo.form,
60655                             listeners : {
60656                                 render : function (_self)
60657                                 {
60658                                     _this.monthField = _self;
60659                                    // _this.monthField.set  today
60660                                 },
60661                                 select : function (combo, date)
60662                                 {
60663                                     _this.grid.ds.load({});
60664                                 }
60665                             },
60666                             value : (function() { return new Date(); })()
60667                         },
60668                         {
60669                             xtype: 'Separator',
60670                             xns: Roo.Toolbar
60671                         },
60672                         {
60673                             xtype: 'TextItem',
60674                             xns: Roo.Toolbar,
60675                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
60676                         },
60677                         {
60678                             xtype: 'Fill',
60679                             xns: Roo.Toolbar
60680                         },
60681                         {
60682                             xtype: 'Button',
60683                             xns: Roo.Toolbar,
60684                             listeners : {
60685                                 click : function (_self, e)
60686                                 {
60687                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60688                                     sd.setMonth(sd.getMonth()+1);
60689                                     _this.monthField.setValue(sd.format('Y-m-d'));
60690                                     _this.grid.ds.load({});
60691                                 }
60692                             },
60693                             text : "Next"
60694                         }
60695                     ]
60696                 },
60697                  
60698             }
60699         };
60700         
60701         *//*
60702  * Based on:
60703  * Ext JS Library 1.1.1
60704  * Copyright(c) 2006-2007, Ext JS, LLC.
60705  *
60706  * Originally Released Under LGPL - original licence link has changed is not relivant.
60707  *
60708  * Fork - LGPL
60709  * <script type="text/javascript">
60710  */
60711  
60712 /**
60713  * @class Roo.LoadMask
60714  * A simple utility class for generically masking elements while loading data.  If the element being masked has
60715  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
60716  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
60717  * element's UpdateManager load indicator and will be destroyed after the initial load.
60718  * @constructor
60719  * Create a new LoadMask
60720  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
60721  * @param {Object} config The config object
60722  */
60723 Roo.LoadMask = function(el, config){
60724     this.el = Roo.get(el);
60725     Roo.apply(this, config);
60726     if(this.store){
60727         this.store.on('beforeload', this.onBeforeLoad, this);
60728         this.store.on('load', this.onLoad, this);
60729         this.store.on('loadexception', this.onLoadException, this);
60730         this.removeMask = false;
60731     }else{
60732         var um = this.el.getUpdateManager();
60733         um.showLoadIndicator = false; // disable the default indicator
60734         um.on('beforeupdate', this.onBeforeLoad, this);
60735         um.on('update', this.onLoad, this);
60736         um.on('failure', this.onLoad, this);
60737         this.removeMask = true;
60738     }
60739 };
60740
60741 Roo.LoadMask.prototype = {
60742     /**
60743      * @cfg {Boolean} removeMask
60744      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
60745      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
60746      */
60747     /**
60748      * @cfg {String} msg
60749      * The text to display in a centered loading message box (defaults to 'Loading...')
60750      */
60751     msg : 'Loading...',
60752     /**
60753      * @cfg {String} msgCls
60754      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
60755      */
60756     msgCls : 'x-mask-loading',
60757
60758     /**
60759      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
60760      * @type Boolean
60761      */
60762     disabled: false,
60763
60764     /**
60765      * Disables the mask to prevent it from being displayed
60766      */
60767     disable : function(){
60768        this.disabled = true;
60769     },
60770
60771     /**
60772      * Enables the mask so that it can be displayed
60773      */
60774     enable : function(){
60775         this.disabled = false;
60776     },
60777     
60778     onLoadException : function()
60779     {
60780         Roo.log(arguments);
60781         
60782         if (typeof(arguments[3]) != 'undefined') {
60783             Roo.MessageBox.alert("Error loading",arguments[3]);
60784         } 
60785         /*
60786         try {
60787             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
60788                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
60789             }   
60790         } catch(e) {
60791             
60792         }
60793         */
60794     
60795         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
60796     },
60797     // private
60798     onLoad : function()
60799     {
60800         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
60801     },
60802
60803     // private
60804     onBeforeLoad : function(){
60805         if(!this.disabled){
60806             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
60807         }
60808     },
60809
60810     // private
60811     destroy : function(){
60812         if(this.store){
60813             this.store.un('beforeload', this.onBeforeLoad, this);
60814             this.store.un('load', this.onLoad, this);
60815             this.store.un('loadexception', this.onLoadException, this);
60816         }else{
60817             var um = this.el.getUpdateManager();
60818             um.un('beforeupdate', this.onBeforeLoad, this);
60819             um.un('update', this.onLoad, this);
60820             um.un('failure', this.onLoad, this);
60821         }
60822     }
60823 };/*
60824  * Based on:
60825  * Ext JS Library 1.1.1
60826  * Copyright(c) 2006-2007, Ext JS, LLC.
60827  *
60828  * Originally Released Under LGPL - original licence link has changed is not relivant.
60829  *
60830  * Fork - LGPL
60831  * <script type="text/javascript">
60832  */
60833
60834
60835 /**
60836  * @class Roo.XTemplate
60837  * @extends Roo.Template
60838  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
60839 <pre><code>
60840 var t = new Roo.XTemplate(
60841         '&lt;select name="{name}"&gt;',
60842                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
60843         '&lt;/select&gt;'
60844 );
60845  
60846 // then append, applying the master template values
60847  </code></pre>
60848  *
60849  * Supported features:
60850  *
60851  *  Tags:
60852
60853 <pre><code>
60854       {a_variable} - output encoded.
60855       {a_variable.format:("Y-m-d")} - call a method on the variable
60856       {a_variable:raw} - unencoded output
60857       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
60858       {a_variable:this.method_on_template(...)} - call a method on the template object.
60859  
60860 </code></pre>
60861  *  The tpl tag:
60862 <pre><code>
60863         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
60864         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
60865         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
60866         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
60867   
60868         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
60869         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
60870 </code></pre>
60871  *      
60872  */
60873 Roo.XTemplate = function()
60874 {
60875     Roo.XTemplate.superclass.constructor.apply(this, arguments);
60876     if (this.html) {
60877         this.compile();
60878     }
60879 };
60880
60881
60882 Roo.extend(Roo.XTemplate, Roo.Template, {
60883
60884     /**
60885      * The various sub templates
60886      */
60887     tpls : false,
60888     /**
60889      *
60890      * basic tag replacing syntax
60891      * WORD:WORD()
60892      *
60893      * // you can fake an object call by doing this
60894      *  x.t:(test,tesT) 
60895      * 
60896      */
60897     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
60898
60899     /**
60900      * compile the template
60901      *
60902      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
60903      *
60904      */
60905     compile: function()
60906     {
60907         var s = this.html;
60908      
60909         s = ['<tpl>', s, '</tpl>'].join('');
60910     
60911         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
60912             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
60913             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
60914             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
60915             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
60916             m,
60917             id     = 0,
60918             tpls   = [];
60919     
60920         while(true == !!(m = s.match(re))){
60921             var forMatch   = m[0].match(nameRe),
60922                 ifMatch   = m[0].match(ifRe),
60923                 execMatch   = m[0].match(execRe),
60924                 namedMatch   = m[0].match(namedRe),
60925                 
60926                 exp  = null, 
60927                 fn   = null,
60928                 exec = null,
60929                 name = forMatch && forMatch[1] ? forMatch[1] : '';
60930                 
60931             if (ifMatch) {
60932                 // if - puts fn into test..
60933                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
60934                 if(exp){
60935                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
60936                 }
60937             }
60938             
60939             if (execMatch) {
60940                 // exec - calls a function... returns empty if true is  returned.
60941                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
60942                 if(exp){
60943                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
60944                 }
60945             }
60946             
60947             
60948             if (name) {
60949                 // for = 
60950                 switch(name){
60951                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
60952                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
60953                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
60954                 }
60955             }
60956             var uid = namedMatch ? namedMatch[1] : id;
60957             
60958             
60959             tpls.push({
60960                 id:     namedMatch ? namedMatch[1] : id,
60961                 target: name,
60962                 exec:   exec,
60963                 test:   fn,
60964                 body:   m[1] || ''
60965             });
60966             if (namedMatch) {
60967                 s = s.replace(m[0], '');
60968             } else { 
60969                 s = s.replace(m[0], '{xtpl'+ id + '}');
60970             }
60971             ++id;
60972         }
60973         this.tpls = [];
60974         for(var i = tpls.length-1; i >= 0; --i){
60975             this.compileTpl(tpls[i]);
60976             this.tpls[tpls[i].id] = tpls[i];
60977         }
60978         this.master = tpls[tpls.length-1];
60979         return this;
60980     },
60981     /**
60982      * same as applyTemplate, except it's done to one of the subTemplates
60983      * when using named templates, you can do:
60984      *
60985      * var str = pl.applySubTemplate('your-name', values);
60986      *
60987      * 
60988      * @param {Number} id of the template
60989      * @param {Object} values to apply to template
60990      * @param {Object} parent (normaly the instance of this object)
60991      */
60992     applySubTemplate : function(id, values, parent)
60993     {
60994         
60995         
60996         var t = this.tpls[id];
60997         
60998         
60999         try { 
61000             if(t.test && !t.test.call(this, values, parent)){
61001                 return '';
61002             }
61003         } catch(e) {
61004             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
61005             Roo.log(e.toString());
61006             Roo.log(t.test);
61007             return ''
61008         }
61009         try { 
61010             
61011             if(t.exec && t.exec.call(this, values, parent)){
61012                 return '';
61013             }
61014         } catch(e) {
61015             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
61016             Roo.log(e.toString());
61017             Roo.log(t.exec);
61018             return ''
61019         }
61020         try {
61021             var vs = t.target ? t.target.call(this, values, parent) : values;
61022             parent = t.target ? values : parent;
61023             if(t.target && vs instanceof Array){
61024                 var buf = [];
61025                 for(var i = 0, len = vs.length; i < len; i++){
61026                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
61027                 }
61028                 return buf.join('');
61029             }
61030             return t.compiled.call(this, vs, parent);
61031         } catch (e) {
61032             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
61033             Roo.log(e.toString());
61034             Roo.log(t.compiled);
61035             return '';
61036         }
61037     },
61038
61039     compileTpl : function(tpl)
61040     {
61041         var fm = Roo.util.Format;
61042         var useF = this.disableFormats !== true;
61043         var sep = Roo.isGecko ? "+" : ",";
61044         var undef = function(str) {
61045             Roo.log("Property not found :"  + str);
61046             return '';
61047         };
61048         
61049         var fn = function(m, name, format, args)
61050         {
61051             //Roo.log(arguments);
61052             args = args ? args.replace(/\\'/g,"'") : args;
61053             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
61054             if (typeof(format) == 'undefined') {
61055                 format= 'htmlEncode';
61056             }
61057             if (format == 'raw' ) {
61058                 format = false;
61059             }
61060             
61061             if(name.substr(0, 4) == 'xtpl'){
61062                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
61063             }
61064             
61065             // build an array of options to determine if value is undefined..
61066             
61067             // basically get 'xxxx.yyyy' then do
61068             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
61069             //    (function () { Roo.log("Property not found"); return ''; })() :
61070             //    ......
61071             
61072             var udef_ar = [];
61073             var lookfor = '';
61074             Roo.each(name.split('.'), function(st) {
61075                 lookfor += (lookfor.length ? '.': '') + st;
61076                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
61077             });
61078             
61079             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
61080             
61081             
61082             if(format && useF){
61083                 
61084                 args = args ? ',' + args : "";
61085                  
61086                 if(format.substr(0, 5) != "this."){
61087                     format = "fm." + format + '(';
61088                 }else{
61089                     format = 'this.call("'+ format.substr(5) + '", ';
61090                     args = ", values";
61091                 }
61092                 
61093                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
61094             }
61095              
61096             if (args.length) {
61097                 // called with xxyx.yuu:(test,test)
61098                 // change to ()
61099                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
61100             }
61101             // raw.. - :raw modifier..
61102             return "'"+ sep + udef_st  + name + ")"+sep+"'";
61103             
61104         };
61105         var body;
61106         // branched to use + in gecko and [].join() in others
61107         if(Roo.isGecko){
61108             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
61109                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
61110                     "';};};";
61111         }else{
61112             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
61113             body.push(tpl.body.replace(/(\r\n|\n)/g,
61114                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
61115             body.push("'].join('');};};");
61116             body = body.join('');
61117         }
61118         
61119         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
61120        
61121         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
61122         eval(body);
61123         
61124         return this;
61125     },
61126
61127     applyTemplate : function(values){
61128         return this.master.compiled.call(this, values, {});
61129         //var s = this.subs;
61130     },
61131
61132     apply : function(){
61133         return this.applyTemplate.apply(this, arguments);
61134     }
61135
61136  });
61137
61138 Roo.XTemplate.from = function(el){
61139     el = Roo.getDom(el);
61140     return new Roo.XTemplate(el.value || el.innerHTML);
61141 };