58d100f2168d31cf4bd566c3a52ea3187054e7a6
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * 
20  * @constructor
21  * Do not use directly - it does not do anything..
22  * @param {Object} config The config object
23  */
24
25
26
27 Roo.bootstrap.Component = function(config){
28     Roo.bootstrap.Component.superclass.constructor.call(this, config);
29 };
30
31 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
32     
33     
34     allowDomMove : false, // to stop relocations in parent onRender...
35     
36     cls : false,
37     
38     style : false,
39     
40     autoCreate : false,
41     
42     initEvents : function() {  },
43     
44     xattr : false,
45     
46     parentId : false,
47     
48     can_build_overlaid : true,
49     
50     dataId : false,
51     
52     name : false,
53     
54     parent: function() {
55         // returns the parent component..
56         return Roo.ComponentMgr.get(this.parentId)
57         
58         
59     },
60     
61     // private
62     onRender : function(ct, position)
63     {
64        // Roo.log("Call onRender: " + this.xtype);
65         
66         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
67         
68         if(this.el){
69             if (this.el.attr('xtype')) {
70                 this.el.attr('xtypex', this.el.attr('xtype'));
71                 this.el.dom.removeAttribute('xtype');
72                 
73                 this.initEvents();
74             }
75             
76             return;
77         }
78         
79          
80         
81         var cfg = Roo.apply({},  this.getAutoCreate());
82         cfg.id = Roo.id();
83         
84         // fill in the extra attributes 
85         if (this.xattr && typeof(this.xattr) =='object') {
86             for (var i in this.xattr) {
87                 cfg[i] = this.xattr[i];
88             }
89         }
90         
91         if(this.dataId){
92             cfg.dataId = this.dataId;
93         }
94         
95         if (this.cls) {
96             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
97         }
98         
99         if (this.style) { // fixme needs to support more complex style data.
100             cfg.style = this.style;
101         }
102         
103         if(this.name){
104             cfg.name = this.name;
105         }
106         
107         this.el = ct.createChild(cfg, position);
108         
109         if(this.tabIndex !== undefined){
110             this.el.dom.setAttribute('tabIndex', this.tabIndex);
111         }
112         this.initEvents();
113         
114         
115     },
116     
117     getChildContainer : function()
118     {
119         return this.el;
120     },
121     
122     
123     addxtype  : function(tree,cntr)
124     {
125         var cn = this;
126         
127         cn = Roo.factory(tree);
128            
129         cn.parentType = this.xtype; //??
130         cn.parentId = this.id;
131         
132         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
133         
134         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
135         
136         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
137         
138         var build_from_html =  Roo.XComponent.build_from_html;
139           
140         var is_body  = (tree.xtype == 'Body') ;
141           
142         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
143           
144         var self_cntr_el = Roo.get(this[cntr](false));
145         
146         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
147             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
148                 return this.addxtypeChild(tree,cntr);
149             }
150             
151             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
152                 
153             if(echild){
154                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
155             }
156             
157             Roo.log('skipping render');
158             return cn;
159             
160         }
161         
162         var ret = false;
163         
164         while (true) {
165             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
166             
167             if (!echild) {
168                 break;
169             }
170             
171             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
172                 break;
173             }
174             
175             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
176         }
177         return ret;
178     },
179     
180     addxtypeChild : function (tree, cntr)
181     {
182         Roo.log('addxtypeChild:' + cntr);
183         var cn = this;
184         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
185         
186         
187         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
188                     (typeof(tree['flexy:foreach']) != 'undefined');
189           
190         
191         
192          skip_children = false;
193         // render the element if it's not BODY.
194         if (tree.xtype != 'Body') {
195            
196             cn = Roo.factory(tree);
197            
198             cn.parentType = this.xtype; //??
199             cn.parentId = this.id;
200             
201             var build_from_html =  Roo.XComponent.build_from_html;
202             
203             
204             // does the container contain child eleemnts with 'xtype' attributes.
205             // that match this xtype..
206             // note - when we render we create these as well..
207             // so we should check to see if body has xtype set.
208             if (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
209                
210                 var self_cntr_el = Roo.get(this[cntr](false));
211                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
212                 
213                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
214                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
215                   
216                   
217                   
218                     cn.el = echild;
219                   //  Roo.log("GOT");
220                     //echild.dom.removeAttribute('xtype');
221                 } else {
222                     Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
223                     Roo.log(self_cntr_el);
224                    Roo.log(echild);
225                     Roo.log(cn);
226                 }
227             }
228            
229             
230            
231             // if object has flexy:if - then it may or may not be rendered.
232             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
233                 // skip a flexy if element.
234                 Roo.log('skipping render');
235                 Roo.log(tree);
236                 if (!cn.el) {
237                     Roo.log('skipping all children');
238                     skip_children = true;
239                 }
240                 
241              } else {
242                  
243                 // actually if flexy:foreach is found, we really want to create 
244                 // multiple copies here...
245                 //Roo.log('render');
246                 //Roo.log(this[cntr]());
247                 cn.render(this[cntr](true));
248              }
249             // then add the element..
250         }
251         
252         
253         // handle the kids..
254         
255         var nitems = [];
256         /*
257         if (typeof (tree.menu) != 'undefined') {
258             tree.menu.parentType = cn.xtype;
259             tree.menu.triggerEl = cn.el;
260             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
261             
262         }
263         */
264         if (!tree.items || !tree.items.length) {
265             cn.items = nitems;
266             return cn;
267         }
268         var items = tree.items;
269         delete tree.items;
270         
271         //Roo.log(items.length);
272             // add the items..
273         if (!skip_children) {    
274             for(var i =0;i < items.length;i++) {
275                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
276             }
277         }
278         
279         cn.items = nitems;
280         
281         return cn;
282     }
283     
284     
285     
286     
287 });
288
289  /*
290  * - LGPL
291  *
292  * Body
293  * 
294  */
295
296 /**
297  * @class Roo.bootstrap.Body
298  * @extends Roo.bootstrap.Component
299  * Bootstrap Body class
300  * 
301  * @constructor
302  * Create a new body
303  * @param {Object} config The config object
304  */
305
306 Roo.bootstrap.Body = function(config){
307     Roo.bootstrap.Body.superclass.constructor.call(this, config);
308     this.el = Roo.get(document.body);
309     if (this.cls && this.cls.length) {
310         Roo.get(document.body).addClass(this.cls);
311     }
312 };
313
314 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
315       
316         autoCreate : {
317         cls: 'container'
318     },
319     onRender : function(ct, position)
320     {
321        /* Roo.log("Roo.bootstrap.Body - onRender");
322         if (this.cls && this.cls.length) {
323             Roo.get(document.body).addClass(this.cls);
324         }
325         // style??? xttr???
326         */
327     }
328     
329     
330  
331    
332 });
333
334  /*
335  * - LGPL
336  *
337  * button group
338  * 
339  */
340
341
342 /**
343  * @class Roo.bootstrap.ButtonGroup
344  * @extends Roo.bootstrap.Component
345  * Bootstrap ButtonGroup class
346  * @cfg {String} size lg | sm | xs (default empty normal)
347  * @cfg {String} align vertical | justified  (default none)
348  * @cfg {String} direction up | down (default down)
349  * @cfg {Boolean} toolbar false | true
350  * @cfg {Boolean} btn true | false
351  * 
352  * 
353  * @constructor
354  * Create a new Input
355  * @param {Object} config The config object
356  */
357
358 Roo.bootstrap.ButtonGroup = function(config){
359     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
360 };
361
362 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
363     
364     size: '',
365     align: '',
366     direction: '',
367     toolbar: false,
368     btn: true,
369
370     getAutoCreate : function(){
371         var cfg = {
372             cls: 'btn-group',
373             html : null
374         }
375         
376         cfg.html = this.html || cfg.html;
377         
378         if (this.toolbar) {
379             cfg = {
380                 cls: 'btn-toolbar',
381                 html: null
382             }
383             
384             return cfg;
385         }
386         
387         if (['vertical','justified'].indexOf(this.align)!==-1) {
388             cfg.cls = 'btn-group-' + this.align;
389             
390             if (this.align == 'justified') {
391                 console.log(this.items);
392             }
393         }
394         
395         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
396             cfg.cls += ' btn-group-' + this.size;
397         }
398         
399         if (this.direction == 'up') {
400             cfg.cls += ' dropup' ;
401         }
402         
403         return cfg;
404     }
405    
406 });
407
408  /*
409  * - LGPL
410  *
411  * button
412  * 
413  */
414
415 /**
416  * @class Roo.bootstrap.Button
417  * @extends Roo.bootstrap.Component
418  * Bootstrap Button class
419  * @cfg {String} html The button content
420  * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
421  * @cfg {String} size empty | lg | sm | xs
422  * @cfg {String} tag empty | a | input | submit
423  * @cfg {String} href empty or href
424  * @cfg {Boolean} disabled false | true
425  * @cfg {Boolean} isClose false | true
426  * @cfg {String} glyphicon empty | adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out
427  * @cfg {String} badge text for badge
428  * @cfg {String} theme default (or empty) | glow
429  * @cfg {Boolean} inverse false | true
430  * @cfg {Boolean} toggle false | true
431  * @cfg {String} ontext text for on toggle state
432  * @cfg {String} offtext text for off toggle state
433  * @cfg {Boolean} defaulton true | false
434  * @cfg {Boolean} preventDefault (true | false) default true
435  * @cfg {Boolean} removeClass true | false remove the standard class..
436  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
437  * 
438  * @constructor
439  * Create a new button
440  * @param {Object} config The config object
441  */
442
443
444 Roo.bootstrap.Button = function(config){
445     Roo.bootstrap.Button.superclass.constructor.call(this, config);
446     this.addEvents({
447         // raw events
448         /**
449          * @event click
450          * When a butotn is pressed
451          * @param {Roo.EventObject} e
452          */
453         "click" : true,
454          /**
455          * @event toggle
456          * After the button has been toggles
457          * @param {Roo.EventObject} e
458          * @param {boolean} pressed (also available as button.pressed)
459          */
460         "toggle" : true
461     });
462 };
463
464 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
465     html: false,
466     active: false,
467     weight: '',
468     size: '',
469     tag: 'button',
470     href: '',
471     disabled: false,
472     isClose: false,
473     glyphicon: '',
474     badge: '',
475     theme: 'default',
476     inverse: false,
477     
478     toggle: false,
479     ontext: 'ON',
480     offtext: 'OFF',
481     defaulton: true,
482     preventDefault: true,
483     removeClass: false,
484     name: false,
485     target: false,
486     
487     
488     pressed : null,
489      
490     
491     getAutoCreate : function(){
492         
493         var cfg = {
494             tag : 'button',
495             cls : 'roo-button',
496             html: ''
497         };
498         
499         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
500             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
501             this.tag = 'button';
502         } else {
503             cfg.tag = this.tag;
504         }
505         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
506         
507         if (this.toggle == true) {
508             cfg={
509                 tag: 'div',
510                 cls: 'slider-frame roo-button',
511                 cn: [
512                     {
513                         tag: 'span',
514                         'data-on-text':'ON',
515                         'data-off-text':'OFF',
516                         cls: 'slider-button',
517                         html: this.offtext
518                     }
519                 ]
520             };
521             
522             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
523                 cfg.cls += ' '+this.weight;
524             }
525             
526             return cfg;
527         }
528         
529         if (this.isClose) {
530             cfg.cls += ' close';
531             
532             cfg["aria-hidden"] = true;
533             
534             cfg.html = "&times;";
535             
536             return cfg;
537         }
538         
539          
540         if (this.theme==='default') {
541             cfg.cls = 'btn roo-button';
542             
543             //if (this.parentType != 'Navbar') {
544             this.weight = this.weight.length ?  this.weight : 'default';
545             //}
546             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
547                 
548                 cfg.cls += ' btn-' + this.weight;
549             }
550         } else if (this.theme==='glow') {
551             
552             cfg.tag = 'a';
553             cfg.cls = 'btn-glow roo-button';
554             
555             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
556                 
557                 cfg.cls += ' ' + this.weight;
558             }
559         }
560    
561         
562         if (this.inverse) {
563             this.cls += ' inverse';
564         }
565         
566         
567         if (this.active) {
568             cfg.cls += ' active';
569         }
570         
571         if (this.disabled) {
572             cfg.disabled = 'disabled';
573         }
574         
575         if (this.items) {
576             Roo.log('changing to ul' );
577             cfg.tag = 'ul';
578             this.glyphicon = 'caret';
579         }
580         
581         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
582          
583         //gsRoo.log(this.parentType);
584         if (this.parentType === 'Navbar' && !this.parent().bar) {
585             Roo.log('changing to li?');
586             
587             cfg.tag = 'li';
588             
589             cfg.cls = '';
590             cfg.cn =  [{
591                 tag : 'a',
592                 cls : 'roo-button',
593                 html : this.html,
594                 href : this.href || '#'
595             }];
596             if (this.menu) {
597                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
598                 cfg.cls += ' dropdown';
599             }   
600             
601             delete cfg.html;
602             
603         }
604         
605        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
606         
607         if (this.glyphicon) {
608             cfg.html = ' ' + cfg.html;
609             
610             cfg.cn = [
611                 {
612                     tag: 'span',
613                     cls: 'glyphicon glyphicon-' + this.glyphicon
614                 }
615             ];
616         }
617         
618         if (this.badge) {
619             cfg.html += ' ';
620             
621             cfg.tag = 'a';
622             
623 //            cfg.cls='btn roo-button';
624             
625             cfg.href=this.href;
626             
627             var value = cfg.html;
628             
629             if(this.glyphicon){
630                 value = {
631                             tag: 'span',
632                             cls: 'glyphicon glyphicon-' + this.glyphicon,
633                             html: this.html
634                         };
635                 
636             }
637             
638             cfg.cn = [
639                 value,
640                 {
641                     tag: 'span',
642                     cls: 'badge',
643                     html: this.badge
644                 }
645             ];
646             
647             cfg.html='';
648         }
649         
650         if (this.menu) {
651             cfg.cls += ' dropdown';
652             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
653         }
654         
655         if (cfg.tag !== 'a' && this.href !== '') {
656             throw "Tag must be a to set href.";
657         } else if (this.href.length > 0) {
658             cfg.href = this.href;
659         }
660         
661         if(this.removeClass){
662             cfg.cls = '';
663         }
664         
665         if(this.target){
666             cfg.target = this.target;
667         }
668         
669         return cfg;
670     },
671     initEvents: function() {
672        // Roo.log('init events?');
673 //        Roo.log(this.el.dom);
674         // add the menu...
675         
676         if (typeof (this.menu) != 'undefined') {
677             this.menu.parentType = this.xtype;
678             this.menu.triggerEl = this.el;
679             this.addxtype(Roo.apply({}, this.menu));
680         }
681
682
683        if (this.el.hasClass('roo-button')) {
684             this.el.on('click', this.onClick, this);
685        } else {
686             this.el.select('.roo-button').on('click', this.onClick, this);
687        }
688        
689        if(this.removeClass){
690            this.el.on('click', this.onClick, this);
691        }
692        
693        this.el.enableDisplayMode();
694         
695     },
696     onClick : function(e)
697     {
698         if (this.disabled) {
699             return;
700         }
701         
702         Roo.log('button on click ');
703         if(this.preventDefault){
704             e.preventDefault();
705         }
706         if (this.pressed === true || this.pressed === false) {
707             this.pressed = !this.pressed;
708             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
709             this.fireEvent('toggle', this, e, this.pressed);
710         }
711         
712         
713         this.fireEvent('click', this, e);
714     },
715     
716     /**
717      * Enables this button
718      */
719     enable : function()
720     {
721         this.disabled = false;
722         this.el.removeClass('disabled');
723     },
724     
725     /**
726      * Disable this button
727      */
728     disable : function()
729     {
730         this.disabled = true;
731         this.el.addClass('disabled');
732     },
733      /**
734      * sets the active state on/off, 
735      * @param {Boolean} state (optional) Force a particular state
736      */
737     setActive : function(v) {
738         
739         this.el[v ? 'addClass' : 'removeClass']('active');
740     },
741      /**
742      * toggles the current active state 
743      */
744     toggleActive : function()
745     {
746        var active = this.el.hasClass('active');
747        this.setActive(!active);
748        
749         
750     },
751     setText : function(str)
752     {
753         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
754     },
755     getText : function()
756     {
757         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
758     },
759     hide: function() {
760        
761      
762         this.el.hide();   
763     },
764     show: function() {
765        
766         this.el.show();   
767     }
768     
769     
770 });
771
772  /*
773  * - LGPL
774  *
775  * column
776  * 
777  */
778
779 /**
780  * @class Roo.bootstrap.Column
781  * @extends Roo.bootstrap.Component
782  * Bootstrap Column class
783  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
784  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
785  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
786  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
787  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
788  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
789  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
790  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
791  *
792  * 
793  * @cfg {Boolean} hidden (true|false) hide the element
794  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
795  * @cfg {String} fa (ban|check|...) font awesome icon
796  * @cfg {Number} fasize (1|2|....) font awsome size
797
798  * @cfg {String} icon (info-sign|check|...) glyphicon name
799
800  * @cfg {String} html content of column.
801  * 
802  * @constructor
803  * Create a new Column
804  * @param {Object} config The config object
805  */
806
807 Roo.bootstrap.Column = function(config){
808     Roo.bootstrap.Column.superclass.constructor.call(this, config);
809 };
810
811 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
812     
813     xs: false,
814     sm: false,
815     md: false,
816     lg: false,
817     xsoff: false,
818     smoff: false,
819     mdoff: false,
820     lgoff: false,
821     html: '',
822     offset: 0,
823     alert: false,
824     fa: false,
825     icon : false,
826     hidden : false,
827     fasize : 1,
828     
829     getAutoCreate : function(){
830         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
831         
832         cfg = {
833             tag: 'div',
834             cls: 'column'
835         };
836         
837         var settings=this;
838         ['xs','sm','md','lg'].map(function(size){
839             //Roo.log( size + ':' + settings[size]);
840             
841             if (settings[size+'off'] !== false) {
842                 cfg.cls += ' col-' + settings[size+'off'] + '-offset';
843             }
844             
845             if (settings[size] === false) {
846                 return;
847             }
848             Roo.log(settings[size]);
849             if (!settings[size]) { // 0 = hidden
850                 cfg.cls += ' hidden-' + size;
851                 return;
852             }
853             cfg.cls += ' col-' + size + '-' + settings[size];
854             
855         });
856         
857         if (this.hidden) {
858             cfg.cls += ' hidden';
859         }
860         
861         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
862             cfg.cls +=' alert alert-' + this.alert;
863         }
864         
865         
866         if (this.html.length) {
867             cfg.html = this.html;
868         }
869         if (this.fa) {
870             var fasize = '';
871             if (this.fasize > 1) {
872                 fasize = ' fa-' + this.fasize + 'x';
873             }
874             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
875             
876             
877         }
878         if (this.icon) {
879             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + + (cfg.html || '')
880         }
881         
882         return cfg;
883     }
884    
885 });
886
887  
888
889  /*
890  * - LGPL
891  *
892  * page container.
893  * 
894  */
895
896
897 /**
898  * @class Roo.bootstrap.Container
899  * @extends Roo.bootstrap.Component
900  * Bootstrap Container class
901  * @cfg {Boolean} jumbotron is it a jumbotron element
902  * @cfg {String} html content of element
903  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
904  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
905  * @cfg {String} header content of header (for panel)
906  * @cfg {String} footer content of footer (for panel)
907  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
908  * @cfg {String} tag (header|aside|section) type of HTML tag.
909  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
910  * @cfg {String} fa (ban|check|...) font awesome icon
911  * @cfg {String} icon (info-sign|check|...) glyphicon name
912  * @cfg {Boolean} hidden (true|false) hide the element
913
914  *     
915  * @constructor
916  * Create a new Container
917  * @param {Object} config The config object
918  */
919
920 Roo.bootstrap.Container = function(config){
921     Roo.bootstrap.Container.superclass.constructor.call(this, config);
922 };
923
924 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
925     
926     jumbotron : false,
927     well: '',
928     panel : '',
929     header: '',
930     footer : '',
931     sticky: '',
932     tag : false,
933     alert : false,
934     fa: false,
935     icon : false,
936   
937      
938     getChildContainer : function() {
939         
940         if(!this.el){
941             return false;
942         }
943         
944         if (this.panel.length) {
945             return this.el.select('.panel-body',true).first();
946         }
947         
948         return this.el;
949     },
950     
951     
952     getAutoCreate : function(){
953         
954         var cfg = {
955             tag : this.tag || 'div',
956             html : '',
957             cls : ''
958         };
959         if (this.jumbotron) {
960             cfg.cls = 'jumbotron';
961         }
962         
963         
964         
965         // - this is applied by the parent..
966         //if (this.cls) {
967         //    cfg.cls = this.cls + '';
968         //}
969         
970         if (this.sticky.length) {
971             
972             var bd = Roo.get(document.body);
973             if (!bd.hasClass('bootstrap-sticky')) {
974                 bd.addClass('bootstrap-sticky');
975                 Roo.select('html',true).setStyle('height', '100%');
976             }
977              
978             cfg.cls += 'bootstrap-sticky-' + this.sticky;
979         }
980         
981         
982         if (this.well.length) {
983             switch (this.well) {
984                 case 'lg':
985                 case 'sm':
986                     cfg.cls +=' well well-' +this.well;
987                     break;
988                 default:
989                     cfg.cls +=' well';
990                     break;
991             }
992         }
993         
994         if (this.hidden) {
995             cfg.cls += ' hidden';
996         }
997         
998         
999         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1000             cfg.cls +=' alert alert-' + this.alert;
1001         }
1002         
1003         var body = cfg;
1004         
1005         if (this.panel.length) {
1006             cfg.cls += ' panel panel-' + this.panel;
1007             cfg.cn = [];
1008             if (this.header.length) {
1009                 cfg.cn.push({
1010                     
1011                     cls : 'panel-heading',
1012                     cn : [{
1013                         tag: 'h3',
1014                         cls : 'panel-title',
1015                         html : this.header
1016                     }]
1017                     
1018                 });
1019             }
1020             body = false;
1021             cfg.cn.push({
1022                 cls : 'panel-body',
1023                 html : this.html
1024             });
1025             
1026             
1027             if (this.footer.length) {
1028                 cfg.cn.push({
1029                     cls : 'panel-footer',
1030                     html : this.footer
1031                     
1032                 });
1033             }
1034             
1035         }
1036         
1037         if (body) {
1038             body.html = this.html || cfg.html;
1039             // prefix with the icons..
1040             if (this.fa) {
1041                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1042             }
1043             if (this.icon) {
1044                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1045             }
1046             
1047             
1048         }
1049         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1050             cfg.cls =  'container';
1051         }
1052         
1053         return cfg;
1054     },
1055     
1056     titleEl : function()
1057     {
1058         if(!this.el || !this.panel.length || !this.header.length){
1059             return;
1060         }
1061         
1062         return this.el.select('.panel-title',true).first();
1063     },
1064     
1065     setTitle : function(v)
1066     {
1067         var titleEl = this.titleEl();
1068         
1069         if(!titleEl){
1070             return;
1071         }
1072         
1073         titleEl.dom.innerHTML = v;
1074     },
1075     
1076     getTitle : function()
1077     {
1078         
1079         var titleEl = this.titleEl();
1080         
1081         if(!titleEl){
1082             return '';
1083         }
1084         
1085         return titleEl.dom.innerHTML;
1086     }
1087    
1088 });
1089
1090  /*
1091  * - LGPL
1092  *
1093  * image
1094  * 
1095  */
1096
1097
1098 /**
1099  * @class Roo.bootstrap.Img
1100  * @extends Roo.bootstrap.Component
1101  * Bootstrap Img class
1102  * @cfg {Boolean} imgResponsive false | true
1103  * @cfg {String} border rounded | circle | thumbnail
1104  * @cfg {String} src image source
1105  * @cfg {String} alt image alternative text
1106  * @cfg {String} href a tag href
1107  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1108  * 
1109  * @constructor
1110  * Create a new Input
1111  * @param {Object} config The config object
1112  */
1113
1114 Roo.bootstrap.Img = function(config){
1115     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1116     
1117     this.addEvents({
1118         // img events
1119         /**
1120          * @event click
1121          * The img click event for the img.
1122          * @param {Roo.EventObject} e
1123          */
1124         "click" : true
1125     });
1126 };
1127
1128 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1129     
1130     imgResponsive: true,
1131     border: '',
1132     src: '',
1133     href: false,
1134     target: false,
1135
1136     getAutoCreate : function(){
1137         
1138         var cfg = {
1139             tag: 'img',
1140             cls: (this.imgResponsive) ? 'img-responsive' : '',
1141             html : null
1142         }
1143         
1144         cfg.html = this.html || cfg.html;
1145         
1146         cfg.src = this.src || cfg.src;
1147         
1148         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1149             cfg.cls += ' img-' + this.border;
1150         }
1151         
1152         if(this.alt){
1153             cfg.alt = this.alt;
1154         }
1155         
1156         if(this.href){
1157             var a = {
1158                 tag: 'a',
1159                 href: this.href,
1160                 cn: [
1161                     cfg
1162                 ]
1163             }
1164             
1165             if(this.target){
1166                 a.target = this.target;
1167             }
1168             
1169         }
1170         
1171         
1172         return (this.href) ? a : cfg;
1173     },
1174     
1175     initEvents: function() {
1176         
1177         if(!this.href){
1178             this.el.on('click', this.onClick, this);
1179         }
1180     },
1181     
1182     onClick : function(e)
1183     {
1184         Roo.log('img onclick');
1185         this.fireEvent('click', this, e);
1186     }
1187    
1188 });
1189
1190  /*
1191  * - LGPL
1192  *
1193  * image
1194  * 
1195  */
1196
1197
1198 /**
1199  * @class Roo.bootstrap.Link
1200  * @extends Roo.bootstrap.Component
1201  * Bootstrap Link Class
1202  * @cfg {String} alt image alternative text
1203  * @cfg {String} href a tag href
1204  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1205  * @cfg {String} html the content of the link.
1206  * @cfg {String} anchor name for the anchor link
1207
1208  * @cfg {Boolean} preventDefault (true | false) default false
1209
1210  * 
1211  * @constructor
1212  * Create a new Input
1213  * @param {Object} config The config object
1214  */
1215
1216 Roo.bootstrap.Link = function(config){
1217     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1218     
1219     this.addEvents({
1220         // img events
1221         /**
1222          * @event click
1223          * The img click event for the img.
1224          * @param {Roo.EventObject} e
1225          */
1226         "click" : true
1227     });
1228 };
1229
1230 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1231     
1232     href: false,
1233     target: false,
1234     preventDefault: false,
1235     anchor : false,
1236     alt : false,
1237
1238     getAutoCreate : function()
1239     {
1240         
1241         var cfg = {
1242             tag: 'a'
1243         };
1244         // anchor's do not require html/href...
1245         if (this.anchor === false) {
1246             cfg.html = this.html || 'html-missing';
1247             cfg.href = this.href || '#';
1248         } else {
1249             cfg.name = this.anchor;
1250             if (this.html !== false) {
1251                 cfg.html = this.html;
1252             }
1253             if (this.href !== false) {
1254                 cfg.href = this.href;
1255             }
1256         }
1257         
1258         if(this.alt !== false){
1259             cfg.alt = this.alt;
1260         }
1261         
1262         
1263         if(this.target !== false) {
1264             cfg.target = this.target;
1265         }
1266         
1267         return cfg;
1268     },
1269     
1270     initEvents: function() {
1271         
1272         if(!this.href || this.preventDefault){
1273             this.el.on('click', this.onClick, this);
1274         }
1275     },
1276     
1277     onClick : function(e)
1278     {
1279         if(this.preventDefault){
1280             e.preventDefault();
1281         }
1282         //Roo.log('img onclick');
1283         this.fireEvent('click', this, e);
1284     }
1285    
1286 });
1287
1288  /*
1289  * - LGPL
1290  *
1291  * header
1292  * 
1293  */
1294
1295 /**
1296  * @class Roo.bootstrap.Header
1297  * @extends Roo.bootstrap.Component
1298  * Bootstrap Header class
1299  * @cfg {String} html content of header
1300  * @cfg {Number} level (1|2|3|4|5|6) default 1
1301  * 
1302  * @constructor
1303  * Create a new Header
1304  * @param {Object} config The config object
1305  */
1306
1307
1308 Roo.bootstrap.Header  = function(config){
1309     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1310 };
1311
1312 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1313     
1314     //href : false,
1315     html : false,
1316     level : 1,
1317     
1318     
1319     
1320     getAutoCreate : function(){
1321         
1322         var cfg = {
1323             tag: 'h' + (1 *this.level),
1324             html: this.html || 'fill in html'
1325         } ;
1326         
1327         return cfg;
1328     }
1329    
1330 });
1331
1332  
1333
1334  /*
1335  * Based on:
1336  * Ext JS Library 1.1.1
1337  * Copyright(c) 2006-2007, Ext JS, LLC.
1338  *
1339  * Originally Released Under LGPL - original licence link has changed is not relivant.
1340  *
1341  * Fork - LGPL
1342  * <script type="text/javascript">
1343  */
1344  
1345 /**
1346  * @class Roo.bootstrap.MenuMgr
1347  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1348  * @singleton
1349  */
1350 Roo.bootstrap.MenuMgr = function(){
1351    var menus, active, groups = {}, attached = false, lastShow = new Date();
1352
1353    // private - called when first menu is created
1354    function init(){
1355        menus = {};
1356        active = new Roo.util.MixedCollection();
1357        Roo.get(document).addKeyListener(27, function(){
1358            if(active.length > 0){
1359                hideAll();
1360            }
1361        });
1362    }
1363
1364    // private
1365    function hideAll(){
1366        if(active && active.length > 0){
1367            var c = active.clone();
1368            c.each(function(m){
1369                m.hide();
1370            });
1371        }
1372    }
1373
1374    // private
1375    function onHide(m){
1376        active.remove(m);
1377        if(active.length < 1){
1378            Roo.get(document).un("mouseup", onMouseDown);
1379             
1380            attached = false;
1381        }
1382    }
1383
1384    // private
1385    function onShow(m){
1386        var last = active.last();
1387        lastShow = new Date();
1388        active.add(m);
1389        if(!attached){
1390           Roo.get(document).on("mouseup", onMouseDown);
1391            
1392            attached = true;
1393        }
1394        if(m.parentMenu){
1395           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1396           m.parentMenu.activeChild = m;
1397        }else if(last && last.isVisible()){
1398           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1399        }
1400    }
1401
1402    // private
1403    function onBeforeHide(m){
1404        if(m.activeChild){
1405            m.activeChild.hide();
1406        }
1407        if(m.autoHideTimer){
1408            clearTimeout(m.autoHideTimer);
1409            delete m.autoHideTimer;
1410        }
1411    }
1412
1413    // private
1414    function onBeforeShow(m){
1415        var pm = m.parentMenu;
1416        if(!pm && !m.allowOtherMenus){
1417            hideAll();
1418        }else if(pm && pm.activeChild && active != m){
1419            pm.activeChild.hide();
1420        }
1421    }
1422
1423    // private
1424    function onMouseDown(e){
1425         Roo.log("on MouseDown");
1426         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1427            hideAll();
1428         }
1429         
1430         
1431    }
1432
1433    // private
1434    function onBeforeCheck(mi, state){
1435        if(state){
1436            var g = groups[mi.group];
1437            for(var i = 0, l = g.length; i < l; i++){
1438                if(g[i] != mi){
1439                    g[i].setChecked(false);
1440                }
1441            }
1442        }
1443    }
1444
1445    return {
1446
1447        /**
1448         * Hides all menus that are currently visible
1449         */
1450        hideAll : function(){
1451             hideAll();  
1452        },
1453
1454        // private
1455        register : function(menu){
1456            if(!menus){
1457                init();
1458            }
1459            menus[menu.id] = menu;
1460            menu.on("beforehide", onBeforeHide);
1461            menu.on("hide", onHide);
1462            menu.on("beforeshow", onBeforeShow);
1463            menu.on("show", onShow);
1464            var g = menu.group;
1465            if(g && menu.events["checkchange"]){
1466                if(!groups[g]){
1467                    groups[g] = [];
1468                }
1469                groups[g].push(menu);
1470                menu.on("checkchange", onCheck);
1471            }
1472        },
1473
1474         /**
1475          * Returns a {@link Roo.menu.Menu} object
1476          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1477          * be used to generate and return a new Menu instance.
1478          */
1479        get : function(menu){
1480            if(typeof menu == "string"){ // menu id
1481                return menus[menu];
1482            }else if(menu.events){  // menu instance
1483                return menu;
1484            }
1485            /*else if(typeof menu.length == 'number'){ // array of menu items?
1486                return new Roo.bootstrap.Menu({items:menu});
1487            }else{ // otherwise, must be a config
1488                return new Roo.bootstrap.Menu(menu);
1489            }
1490            */
1491            return false;
1492        },
1493
1494        // private
1495        unregister : function(menu){
1496            delete menus[menu.id];
1497            menu.un("beforehide", onBeforeHide);
1498            menu.un("hide", onHide);
1499            menu.un("beforeshow", onBeforeShow);
1500            menu.un("show", onShow);
1501            var g = menu.group;
1502            if(g && menu.events["checkchange"]){
1503                groups[g].remove(menu);
1504                menu.un("checkchange", onCheck);
1505            }
1506        },
1507
1508        // private
1509        registerCheckable : function(menuItem){
1510            var g = menuItem.group;
1511            if(g){
1512                if(!groups[g]){
1513                    groups[g] = [];
1514                }
1515                groups[g].push(menuItem);
1516                menuItem.on("beforecheckchange", onBeforeCheck);
1517            }
1518        },
1519
1520        // private
1521        unregisterCheckable : function(menuItem){
1522            var g = menuItem.group;
1523            if(g){
1524                groups[g].remove(menuItem);
1525                menuItem.un("beforecheckchange", onBeforeCheck);
1526            }
1527        }
1528    };
1529 }();/*
1530  * - LGPL
1531  *
1532  * menu
1533  * 
1534  */
1535
1536 /**
1537  * @class Roo.bootstrap.Menu
1538  * @extends Roo.bootstrap.Component
1539  * Bootstrap Menu class - container for MenuItems
1540  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1541  * 
1542  * @constructor
1543  * Create a new Menu
1544  * @param {Object} config The config object
1545  */
1546
1547
1548 Roo.bootstrap.Menu = function(config){
1549     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1550     if (this.registerMenu) {
1551         Roo.bootstrap.MenuMgr.register(this);
1552     }
1553     this.addEvents({
1554         /**
1555          * @event beforeshow
1556          * Fires before this menu is displayed
1557          * @param {Roo.menu.Menu} this
1558          */
1559         beforeshow : true,
1560         /**
1561          * @event beforehide
1562          * Fires before this menu is hidden
1563          * @param {Roo.menu.Menu} this
1564          */
1565         beforehide : true,
1566         /**
1567          * @event show
1568          * Fires after this menu is displayed
1569          * @param {Roo.menu.Menu} this
1570          */
1571         show : true,
1572         /**
1573          * @event hide
1574          * Fires after this menu is hidden
1575          * @param {Roo.menu.Menu} this
1576          */
1577         hide : true,
1578         /**
1579          * @event click
1580          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1581          * @param {Roo.menu.Menu} this
1582          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1583          * @param {Roo.EventObject} e
1584          */
1585         click : true,
1586         /**
1587          * @event mouseover
1588          * Fires when the mouse is hovering over this menu
1589          * @param {Roo.menu.Menu} this
1590          * @param {Roo.EventObject} e
1591          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1592          */
1593         mouseover : true,
1594         /**
1595          * @event mouseout
1596          * Fires when the mouse exits this menu
1597          * @param {Roo.menu.Menu} this
1598          * @param {Roo.EventObject} e
1599          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1600          */
1601         mouseout : true,
1602         /**
1603          * @event itemclick
1604          * Fires when a menu item contained in this menu is clicked
1605          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1606          * @param {Roo.EventObject} e
1607          */
1608         itemclick: true
1609     });
1610     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1611 };
1612
1613 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1614     
1615    /// html : false,
1616     //align : '',
1617     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1618     type: false,
1619     /**
1620      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1621      */
1622     registerMenu : true,
1623     
1624     menuItems :false, // stores the menu items..
1625     
1626     hidden:true,
1627     
1628     parentMenu : false,
1629     
1630     getChildContainer : function() {
1631         return this.el;  
1632     },
1633     
1634     getAutoCreate : function(){
1635          
1636         //if (['right'].indexOf(this.align)!==-1) {
1637         //    cfg.cn[1].cls += ' pull-right'
1638         //}
1639         
1640         
1641         var cfg = {
1642             tag : 'ul',
1643             cls : 'dropdown-menu' ,
1644             style : 'z-index:1000'
1645             
1646         }
1647         
1648         if (this.type === 'submenu') {
1649             cfg.cls = 'submenu active';
1650         }
1651         if (this.type === 'treeview') {
1652             cfg.cls = 'treeview-menu';
1653         }
1654         
1655         return cfg;
1656     },
1657     initEvents : function() {
1658         
1659        // Roo.log("ADD event");
1660        // Roo.log(this.triggerEl.dom);
1661         this.triggerEl.on('click', this.onTriggerPress, this);
1662         this.triggerEl.addClass('dropdown-toggle');
1663         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1664
1665         this.el.on("mouseover", this.onMouseOver, this);
1666         this.el.on("mouseout", this.onMouseOut, this);
1667         
1668         
1669     },
1670     findTargetItem : function(e){
1671         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1672         if(!t){
1673             return false;
1674         }
1675         //Roo.log(t);         Roo.log(t.id);
1676         if(t && t.id){
1677             //Roo.log(this.menuitems);
1678             return this.menuitems.get(t.id);
1679             
1680             //return this.items.get(t.menuItemId);
1681         }
1682         
1683         return false;
1684     },
1685     onClick : function(e){
1686         Roo.log("menu.onClick");
1687         var t = this.findTargetItem(e);
1688         if(!t){
1689             return;
1690         }
1691         Roo.log(e);
1692         /*
1693         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1694             if(t == this.activeItem && t.shouldDeactivate(e)){
1695                 this.activeItem.deactivate();
1696                 delete this.activeItem;
1697                 return;
1698             }
1699             if(t.canActivate){
1700                 this.setActiveItem(t, true);
1701             }
1702             return;
1703             
1704             
1705         }
1706         */
1707         Roo.log('pass click event');
1708         
1709         t.onClick(e);
1710         
1711         this.fireEvent("click", this, t, e);
1712         
1713         this.hide();
1714     },
1715      onMouseOver : function(e){
1716         var t  = this.findTargetItem(e);
1717         //Roo.log(t);
1718         //if(t){
1719         //    if(t.canActivate && !t.disabled){
1720         //        this.setActiveItem(t, true);
1721         //    }
1722         //}
1723         
1724         this.fireEvent("mouseover", this, e, t);
1725     },
1726     isVisible : function(){
1727         return !this.hidden;
1728     },
1729      onMouseOut : function(e){
1730         var t  = this.findTargetItem(e);
1731         
1732         //if(t ){
1733         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1734         //        this.activeItem.deactivate();
1735         //        delete this.activeItem;
1736         //    }
1737         //}
1738         this.fireEvent("mouseout", this, e, t);
1739     },
1740     
1741     
1742     /**
1743      * Displays this menu relative to another element
1744      * @param {String/HTMLElement/Roo.Element} element The element to align to
1745      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1746      * the element (defaults to this.defaultAlign)
1747      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1748      */
1749     show : function(el, pos, parentMenu){
1750         this.parentMenu = parentMenu;
1751         if(!this.el){
1752             this.render();
1753         }
1754         this.fireEvent("beforeshow", this);
1755         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1756     },
1757      /**
1758      * Displays this menu at a specific xy position
1759      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1760      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1761      */
1762     showAt : function(xy, parentMenu, /* private: */_e){
1763         this.parentMenu = parentMenu;
1764         if(!this.el){
1765             this.render();
1766         }
1767         if(_e !== false){
1768             this.fireEvent("beforeshow", this);
1769             
1770             //xy = this.el.adjustForConstraints(xy);
1771         }
1772         //this.el.setXY(xy);
1773         //this.el.show();
1774         this.hideMenuItems();
1775         this.hidden = false;
1776         this.triggerEl.addClass('open');
1777         this.focus();
1778         this.fireEvent("show", this);
1779     },
1780     
1781     focus : function(){
1782         return;
1783         if(!this.hidden){
1784             this.doFocus.defer(50, this);
1785         }
1786     },
1787
1788     doFocus : function(){
1789         if(!this.hidden){
1790             this.focusEl.focus();
1791         }
1792     },
1793
1794     /**
1795      * Hides this menu and optionally all parent menus
1796      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1797      */
1798     hide : function(deep){
1799         
1800         this.hideMenuItems();
1801         if(this.el && this.isVisible()){
1802             this.fireEvent("beforehide", this);
1803             if(this.activeItem){
1804                 this.activeItem.deactivate();
1805                 this.activeItem = null;
1806             }
1807             this.triggerEl.removeClass('open');;
1808             this.hidden = true;
1809             this.fireEvent("hide", this);
1810         }
1811         if(deep === true && this.parentMenu){
1812             this.parentMenu.hide(true);
1813         }
1814     },
1815     
1816     onTriggerPress  : function(e)
1817     {
1818         
1819         Roo.log('trigger press');
1820         //Roo.log(e.getTarget());
1821        // Roo.log(this.triggerEl.dom);
1822         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1823             return;
1824         }
1825         if (this.isVisible()) {
1826             Roo.log('hide');
1827             this.hide();
1828         } else {
1829             this.show(this.triggerEl, false, false);
1830         }
1831         
1832         
1833     },
1834     
1835          
1836        
1837     
1838     hideMenuItems : function()
1839     {
1840         //$(backdrop).remove()
1841         Roo.select('.open',true).each(function(aa) {
1842             
1843             aa.removeClass('open');
1844           //var parent = getParent($(this))
1845           //var relatedTarget = { relatedTarget: this }
1846           
1847            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1848           //if (e.isDefaultPrevented()) return
1849            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1850         })
1851     },
1852     addxtypeChild : function (tree, cntr) {
1853         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1854           
1855         this.menuitems.add(comp);
1856         return comp;
1857
1858     },
1859     getEl : function()
1860     {
1861         Roo.log(this.el);
1862         return this.el;
1863     }
1864 });
1865
1866  
1867  /*
1868  * - LGPL
1869  *
1870  * menu item
1871  * 
1872  */
1873
1874
1875 /**
1876  * @class Roo.bootstrap.MenuItem
1877  * @extends Roo.bootstrap.Component
1878  * Bootstrap MenuItem class
1879  * @cfg {String} html the menu label
1880  * @cfg {String} href the link
1881  * @cfg {Boolean} preventDefault (true | false) default true
1882  * 
1883  * 
1884  * @constructor
1885  * Create a new MenuItem
1886  * @param {Object} config The config object
1887  */
1888
1889
1890 Roo.bootstrap.MenuItem = function(config){
1891     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1892     this.addEvents({
1893         // raw events
1894         /**
1895          * @event click
1896          * The raw click event for the entire grid.
1897          * @param {Roo.EventObject} e
1898          */
1899         "click" : true
1900     });
1901 };
1902
1903 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1904     
1905     href : false,
1906     html : false,
1907     preventDefault: true,
1908     
1909     getAutoCreate : function(){
1910         var cfg= {
1911             tag: 'li',
1912             cls: 'dropdown-menu-item',
1913             cn: [
1914                     {
1915                         tag : 'a',
1916                         href : '#',
1917                         html : 'Link'
1918                     }
1919                 ]
1920         };
1921         if (this.parent().type == 'treeview') {
1922             cfg.cls = 'treeview-menu';
1923         }
1924         
1925         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1926         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1927         return cfg;
1928     },
1929     
1930     initEvents: function() {
1931         
1932         //this.el.select('a').on('click', this.onClick, this);
1933         
1934     },
1935     onClick : function(e)
1936     {
1937         Roo.log('item on click ');
1938         //if(this.preventDefault){
1939         //    e.preventDefault();
1940         //}
1941         //this.parent().hideMenuItems();
1942         
1943         this.fireEvent('click', this, e);
1944     },
1945     getEl : function()
1946     {
1947         return this.el;
1948     }
1949 });
1950
1951  
1952
1953  /*
1954  * - LGPL
1955  *
1956  * menu separator
1957  * 
1958  */
1959
1960
1961 /**
1962  * @class Roo.bootstrap.MenuSeparator
1963  * @extends Roo.bootstrap.Component
1964  * Bootstrap MenuSeparator class
1965  * 
1966  * @constructor
1967  * Create a new MenuItem
1968  * @param {Object} config The config object
1969  */
1970
1971
1972 Roo.bootstrap.MenuSeparator = function(config){
1973     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1974 };
1975
1976 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
1977     
1978     getAutoCreate : function(){
1979         var cfg = {
1980             cls: 'divider',
1981             tag : 'li'
1982         };
1983         
1984         return cfg;
1985     }
1986    
1987 });
1988
1989  
1990
1991  
1992 /*
1993 <div class="modal fade">
1994   <div class="modal-dialog">
1995     <div class="modal-content">
1996       <div class="modal-header">
1997         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
1998         <h4 class="modal-title">Modal title</h4>
1999       </div>
2000       <div class="modal-body">
2001         <p>One fine body&hellip;</p>
2002       </div>
2003       <div class="modal-footer">
2004         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
2005         <button type="button" class="btn btn-primary">Save changes</button>
2006       </div>
2007     </div><!-- /.modal-content -->
2008   </div><!-- /.modal-dialog -->
2009 </div><!-- /.modal -->
2010 */
2011 /*
2012  * - LGPL
2013  *
2014  * page contgainer.
2015  * 
2016  */
2017
2018 /**
2019  * @class Roo.bootstrap.Modal
2020  * @extends Roo.bootstrap.Component
2021  * Bootstrap Modal class
2022  * @cfg {String} title Title of dialog
2023  * @cfg {Boolean} specificTitle (true|false) default false
2024  * @cfg {Array} buttons Array of buttons or standard button set..
2025  * @cfg {String} buttonPosition (left|right|center) default right
2026  * 
2027  * @constructor
2028  * Create a new Modal Dialog
2029  * @param {Object} config The config object
2030  */
2031
2032 Roo.bootstrap.Modal = function(config){
2033     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2034     this.addEvents({
2035         // raw events
2036         /**
2037          * @event btnclick
2038          * The raw btnclick event for the button
2039          * @param {Roo.EventObject} e
2040          */
2041         "btnclick" : true
2042     });
2043     this.buttons = this.buttons || [];
2044 };
2045
2046 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2047     
2048     title : 'test dialog',
2049    
2050     buttons : false,
2051     
2052     // set on load...
2053     body:  false,
2054     
2055     specificTitle: false,
2056     
2057     buttonPosition: 'right',
2058     
2059     onRender : function(ct, position)
2060     {
2061         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2062      
2063         if(!this.el){
2064             var cfg = Roo.apply({},  this.getAutoCreate());
2065             cfg.id = Roo.id();
2066             //if(!cfg.name){
2067             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2068             //}
2069             //if (!cfg.name.length) {
2070             //    delete cfg.name;
2071            // }
2072             if (this.cls) {
2073                 cfg.cls += ' ' + this.cls;
2074             }
2075             if (this.style) {
2076                 cfg.style = this.style;
2077             }
2078             this.el = Roo.get(document.body).createChild(cfg, position);
2079         }
2080         //var type = this.el.dom.type;
2081         
2082         if(this.tabIndex !== undefined){
2083             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2084         }
2085         
2086         
2087         
2088         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2089         this.maskEl.enableDisplayMode("block");
2090         this.maskEl.hide();
2091         //this.el.addClass("x-dlg-modal");
2092     
2093         if (this.buttons.length) {
2094             Roo.each(this.buttons, function(bb) {
2095                 b = Roo.apply({}, bb);
2096                 b.xns = b.xns || Roo.bootstrap;
2097                 b.xtype = b.xtype || 'Button';
2098                 if (typeof(b.listeners) == 'undefined') {
2099                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2100                 }
2101                 
2102                 var btn = Roo.factory(b);
2103                 
2104                 btn.onRender(this.el.select('.modal-footer div').first());
2105                 
2106             },this);
2107         }
2108         // render the children.
2109         var nitems = [];
2110         
2111         if(typeof(this.items) != 'undefined'){
2112             var items = this.items;
2113             delete this.items;
2114
2115             for(var i =0;i < items.length;i++) {
2116                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2117             }
2118         }
2119         
2120         this.items = nitems;
2121         
2122         this.body = this.el.select('.modal-body',true).first();
2123         this.close = this.el.select('.modal-header .close', true).first();
2124         this.footer = this.el.select('.modal-footer',true).first();
2125         this.initEvents();
2126         //this.el.addClass([this.fieldClass, this.cls]);
2127         
2128     },
2129     getAutoCreate : function(){
2130         
2131         
2132         var bdy = {
2133                 cls : 'modal-body',
2134                 html : this.html || ''
2135         };
2136         
2137         var title = {
2138             tag: 'h4',
2139             cls : 'modal-title',
2140             html : this.title
2141         };
2142         
2143         if(this.specificTitle){
2144             title = this.title;
2145         };
2146         
2147         return modal = {
2148             cls: "modal fade",
2149             style : 'display: none',
2150             cn : [
2151                 {
2152                     cls: "modal-dialog",
2153                     cn : [
2154                         {
2155                             cls : "modal-content",
2156                             cn : [
2157                                 {
2158                                     cls : 'modal-header',
2159                                     cn : [
2160                                         {
2161                                             tag: 'button',
2162                                             cls : 'close',
2163                                             html : '&times'
2164                                         },
2165                                         title
2166                                     ]
2167                                 },
2168                                 bdy,
2169                                 {
2170                                     cls : 'modal-footer',
2171                                     cn : [
2172                                         {
2173                                             tag: 'div',
2174                                             cls: 'btn-' + this.buttonPosition
2175                                         }
2176                                     ]
2177                                     
2178                                 }
2179                                 
2180                                 
2181                             ]
2182                             
2183                         }
2184                     ]
2185                         
2186                 }
2187             ]
2188             
2189             
2190         };
2191           
2192     },
2193     getChildContainer : function() {
2194          
2195          return this.el.select('.modal-body',true).first();
2196         
2197     },
2198     getButtonContainer : function() {
2199          return this.el.select('.modal-footer div',true).first();
2200         
2201     },
2202     initEvents : function()
2203     {
2204         this.el.select('.modal-header .close').on('click', this.hide, this);
2205 //        
2206 //        this.addxtype(this);
2207     },
2208     show : function() {
2209         
2210         if (!this.rendered) {
2211             this.render();
2212         }
2213        
2214         this.el.addClass('on');
2215         this.el.removeClass('fade');
2216         this.el.setStyle('display', 'block');
2217         Roo.get(document.body).addClass("x-body-masked");
2218         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2219         this.maskEl.show();
2220         this.el.setStyle('zIndex', '10001');
2221         this.fireEvent('show', this);
2222         
2223         
2224     },
2225     hide : function()
2226     {
2227         Roo.log('Modal hide?!');
2228         this.maskEl.hide();
2229         Roo.get(document.body).removeClass("x-body-masked");
2230         this.el.removeClass('on');
2231         this.el.addClass('fade');
2232         this.el.setStyle('display', 'none');
2233         this.fireEvent('hide', this);
2234     },
2235     
2236     addButton : function(str, cb)
2237     {
2238          
2239         
2240         var b = Roo.apply({}, { html : str } );
2241         b.xns = b.xns || Roo.bootstrap;
2242         b.xtype = b.xtype || 'Button';
2243         if (typeof(b.listeners) == 'undefined') {
2244             b.listeners = { click : cb.createDelegate(this)  };
2245         }
2246         
2247         var btn = Roo.factory(b);
2248            
2249         btn.onRender(this.el.select('.modal-footer div').first());
2250         
2251         return btn;   
2252        
2253     },
2254     
2255     setDefaultButton : function(btn)
2256     {
2257         //this.el.select('.modal-footer').()
2258     },
2259     resizeTo: function(w,h)
2260     {
2261         // skip..
2262     },
2263     setContentSize  : function(w, h)
2264     {
2265         
2266     },
2267     onButtonClick: function(btn,e)
2268     {
2269         //Roo.log([a,b,c]);
2270         this.fireEvent('btnclick', btn.name, e);
2271     },
2272     setTitle: function(str) {
2273         this.el.select('.modal-title',true).first().dom.innerHTML = str;
2274         
2275     }
2276 });
2277
2278
2279 Roo.apply(Roo.bootstrap.Modal,  {
2280     /**
2281          * Button config that displays a single OK button
2282          * @type Object
2283          */
2284         OK :  [{
2285             name : 'ok',
2286             weight : 'primary',
2287             html : 'OK'
2288         }], 
2289         /**
2290          * Button config that displays Yes and No buttons
2291          * @type Object
2292          */
2293         YESNO : [
2294             {
2295                 name  : 'no',
2296                 html : 'No'
2297             },
2298             {
2299                 name  :'yes',
2300                 weight : 'primary',
2301                 html : 'Yes'
2302             }
2303         ],
2304         
2305         /**
2306          * Button config that displays OK and Cancel buttons
2307          * @type Object
2308          */
2309         OKCANCEL : [
2310             {
2311                name : 'cancel',
2312                 html : 'Cancel'
2313             },
2314             {
2315                 name : 'ok',
2316                 weight : 'primary',
2317                 html : 'OK'
2318             }
2319         ],
2320         /**
2321          * Button config that displays Yes, No and Cancel buttons
2322          * @type Object
2323          */
2324         YESNOCANCEL : [
2325             {
2326                 name : 'yes',
2327                 weight : 'primary',
2328                 html : 'Yes'
2329             },
2330             {
2331                 name : 'no',
2332                 html : 'No'
2333             },
2334             {
2335                 name : 'cancel',
2336                 html : 'Cancel'
2337             }
2338         ]
2339 });
2340  /*
2341  * - LGPL
2342  *
2343  * messagebox - can be used as a replace
2344  * 
2345  */
2346 /**
2347  * @class Roo.MessageBox
2348  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2349  * Example usage:
2350  *<pre><code>
2351 // Basic alert:
2352 Roo.Msg.alert('Status', 'Changes saved successfully.');
2353
2354 // Prompt for user data:
2355 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2356     if (btn == 'ok'){
2357         // process text value...
2358     }
2359 });
2360
2361 // Show a dialog using config options:
2362 Roo.Msg.show({
2363    title:'Save Changes?',
2364    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2365    buttons: Roo.Msg.YESNOCANCEL,
2366    fn: processResult,
2367    animEl: 'elId'
2368 });
2369 </code></pre>
2370  * @singleton
2371  */
2372 Roo.bootstrap.MessageBox = function(){
2373     var dlg, opt, mask, waitTimer;
2374     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2375     var buttons, activeTextEl, bwidth;
2376
2377     
2378     // private
2379     var handleButton = function(button){
2380         dlg.hide();
2381         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2382     };
2383
2384     // private
2385     var handleHide = function(){
2386         if(opt && opt.cls){
2387             dlg.el.removeClass(opt.cls);
2388         }
2389         //if(waitTimer){
2390         //    Roo.TaskMgr.stop(waitTimer);
2391         //    waitTimer = null;
2392         //}
2393     };
2394
2395     // private
2396     var updateButtons = function(b){
2397         var width = 0;
2398         if(!b){
2399             buttons["ok"].hide();
2400             buttons["cancel"].hide();
2401             buttons["yes"].hide();
2402             buttons["no"].hide();
2403             //dlg.footer.dom.style.display = 'none';
2404             return width;
2405         }
2406         dlg.footer.dom.style.display = '';
2407         for(var k in buttons){
2408             if(typeof buttons[k] != "function"){
2409                 if(b[k]){
2410                     buttons[k].show();
2411                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2412                     width += buttons[k].el.getWidth()+15;
2413                 }else{
2414                     buttons[k].hide();
2415                 }
2416             }
2417         }
2418         return width;
2419     };
2420
2421     // private
2422     var handleEsc = function(d, k, e){
2423         if(opt && opt.closable !== false){
2424             dlg.hide();
2425         }
2426         if(e){
2427             e.stopEvent();
2428         }
2429     };
2430
2431     return {
2432         /**
2433          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2434          * @return {Roo.BasicDialog} The BasicDialog element
2435          */
2436         getDialog : function(){
2437            if(!dlg){
2438                 dlg = new Roo.bootstrap.Modal( {
2439                     //draggable: true,
2440                     //resizable:false,
2441                     //constraintoviewport:false,
2442                     //fixedcenter:true,
2443                     //collapsible : false,
2444                     //shim:true,
2445                     //modal: true,
2446                   //  width:400,
2447                   //  height:100,
2448                     //buttonAlign:"center",
2449                     closeClick : function(){
2450                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2451                             handleButton("no");
2452                         }else{
2453                             handleButton("cancel");
2454                         }
2455                     }
2456                 });
2457                 dlg.render();
2458                 dlg.on("hide", handleHide);
2459                 mask = dlg.mask;
2460                 //dlg.addKeyListener(27, handleEsc);
2461                 buttons = {};
2462                 this.buttons = buttons;
2463                 var bt = this.buttonText;
2464                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2465                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2466                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2467                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2468                 Roo.log(buttons)
2469                 bodyEl = dlg.body.createChild({
2470
2471                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2472                         '<textarea class="roo-mb-textarea"></textarea>' +
2473                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2474                 });
2475                 msgEl = bodyEl.dom.firstChild;
2476                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2477                 textboxEl.enableDisplayMode();
2478                 textboxEl.addKeyListener([10,13], function(){
2479                     if(dlg.isVisible() && opt && opt.buttons){
2480                         if(opt.buttons.ok){
2481                             handleButton("ok");
2482                         }else if(opt.buttons.yes){
2483                             handleButton("yes");
2484                         }
2485                     }
2486                 });
2487                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2488                 textareaEl.enableDisplayMode();
2489                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2490                 progressEl.enableDisplayMode();
2491                 var pf = progressEl.dom.firstChild;
2492                 if (pf) {
2493                     pp = Roo.get(pf.firstChild);
2494                     pp.setHeight(pf.offsetHeight);
2495                 }
2496                 
2497             }
2498             return dlg;
2499         },
2500
2501         /**
2502          * Updates the message box body text
2503          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2504          * the XHTML-compliant non-breaking space character '&amp;#160;')
2505          * @return {Roo.MessageBox} This message box
2506          */
2507         updateText : function(text){
2508             if(!dlg.isVisible() && !opt.width){
2509                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2510             }
2511             msgEl.innerHTML = text || '&#160;';
2512       
2513             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2514             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2515             var w = Math.max(
2516                     Math.min(opt.width || cw , this.maxWidth), 
2517                     Math.max(opt.minWidth || this.minWidth, bwidth)
2518             );
2519             if(opt.prompt){
2520                 activeTextEl.setWidth(w);
2521             }
2522             if(dlg.isVisible()){
2523                 dlg.fixedcenter = false;
2524             }
2525             // to big, make it scroll. = But as usual stupid IE does not support
2526             // !important..
2527             
2528             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2529                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2530                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2531             } else {
2532                 bodyEl.dom.style.height = '';
2533                 bodyEl.dom.style.overflowY = '';
2534             }
2535             if (cw > w) {
2536                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2537             } else {
2538                 bodyEl.dom.style.overflowX = '';
2539             }
2540             
2541             dlg.setContentSize(w, bodyEl.getHeight());
2542             if(dlg.isVisible()){
2543                 dlg.fixedcenter = true;
2544             }
2545             return this;
2546         },
2547
2548         /**
2549          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2550          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2551          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2552          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2553          * @return {Roo.MessageBox} This message box
2554          */
2555         updateProgress : function(value, text){
2556             if(text){
2557                 this.updateText(text);
2558             }
2559             if (pp) { // weird bug on my firefox - for some reason this is not defined
2560                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2561             }
2562             return this;
2563         },        
2564
2565         /**
2566          * Returns true if the message box is currently displayed
2567          * @return {Boolean} True if the message box is visible, else false
2568          */
2569         isVisible : function(){
2570             return dlg && dlg.isVisible();  
2571         },
2572
2573         /**
2574          * Hides the message box if it is displayed
2575          */
2576         hide : function(){
2577             if(this.isVisible()){
2578                 dlg.hide();
2579             }  
2580         },
2581
2582         /**
2583          * Displays a new message box, or reinitializes an existing message box, based on the config options
2584          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2585          * The following config object properties are supported:
2586          * <pre>
2587 Property    Type             Description
2588 ----------  ---------------  ------------------------------------------------------------------------------------
2589 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2590                                    closes (defaults to undefined)
2591 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2592                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2593 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2594                                    progress and wait dialogs will ignore this property and always hide the
2595                                    close button as they can only be closed programmatically.
2596 cls               String           A custom CSS class to apply to the message box element
2597 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2598                                    displayed (defaults to 75)
2599 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2600                                    function will be btn (the name of the button that was clicked, if applicable,
2601                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2602                                    Progress and wait dialogs will ignore this option since they do not respond to
2603                                    user actions and can only be closed programmatically, so any required function
2604                                    should be called by the same code after it closes the dialog.
2605 icon              String           A CSS class that provides a background image to be used as an icon for
2606                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2607 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2608 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2609 modal             Boolean          False to allow user interaction with the page while the message box is
2610                                    displayed (defaults to true)
2611 msg               String           A string that will replace the existing message box body text (defaults
2612                                    to the XHTML-compliant non-breaking space character '&#160;')
2613 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2614 progress          Boolean          True to display a progress bar (defaults to false)
2615 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2616 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2617 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2618 title             String           The title text
2619 value             String           The string value to set into the active textbox element if displayed
2620 wait              Boolean          True to display a progress bar (defaults to false)
2621 width             Number           The width of the dialog in pixels
2622 </pre>
2623          *
2624          * Example usage:
2625          * <pre><code>
2626 Roo.Msg.show({
2627    title: 'Address',
2628    msg: 'Please enter your address:',
2629    width: 300,
2630    buttons: Roo.MessageBox.OKCANCEL,
2631    multiline: true,
2632    fn: saveAddress,
2633    animEl: 'addAddressBtn'
2634 });
2635 </code></pre>
2636          * @param {Object} config Configuration options
2637          * @return {Roo.MessageBox} This message box
2638          */
2639         show : function(options)
2640         {
2641             
2642             // this causes nightmares if you show one dialog after another
2643             // especially on callbacks..
2644              
2645             if(this.isVisible()){
2646                 
2647                 this.hide();
2648                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2649                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2650                 Roo.log("New Dialog Message:" +  options.msg )
2651                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2652                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2653                 
2654             }
2655             var d = this.getDialog();
2656             opt = options;
2657             d.setTitle(opt.title || "&#160;");
2658             d.close.setDisplayed(opt.closable !== false);
2659             activeTextEl = textboxEl;
2660             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2661             if(opt.prompt){
2662                 if(opt.multiline){
2663                     textboxEl.hide();
2664                     textareaEl.show();
2665                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2666                         opt.multiline : this.defaultTextHeight);
2667                     activeTextEl = textareaEl;
2668                 }else{
2669                     textboxEl.show();
2670                     textareaEl.hide();
2671                 }
2672             }else{
2673                 textboxEl.hide();
2674                 textareaEl.hide();
2675             }
2676             progressEl.setDisplayed(opt.progress === true);
2677             this.updateProgress(0);
2678             activeTextEl.dom.value = opt.value || "";
2679             if(opt.prompt){
2680                 dlg.setDefaultButton(activeTextEl);
2681             }else{
2682                 var bs = opt.buttons;
2683                 var db = null;
2684                 if(bs && bs.ok){
2685                     db = buttons["ok"];
2686                 }else if(bs && bs.yes){
2687                     db = buttons["yes"];
2688                 }
2689                 dlg.setDefaultButton(db);
2690             }
2691             bwidth = updateButtons(opt.buttons);
2692             this.updateText(opt.msg);
2693             if(opt.cls){
2694                 d.el.addClass(opt.cls);
2695             }
2696             d.proxyDrag = opt.proxyDrag === true;
2697             d.modal = opt.modal !== false;
2698             d.mask = opt.modal !== false ? mask : false;
2699             if(!d.isVisible()){
2700                 // force it to the end of the z-index stack so it gets a cursor in FF
2701                 document.body.appendChild(dlg.el.dom);
2702                 d.animateTarget = null;
2703                 d.show(options.animEl);
2704             }
2705             return this;
2706         },
2707
2708         /**
2709          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2710          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2711          * and closing the message box when the process is complete.
2712          * @param {String} title The title bar text
2713          * @param {String} msg The message box body text
2714          * @return {Roo.MessageBox} This message box
2715          */
2716         progress : function(title, msg){
2717             this.show({
2718                 title : title,
2719                 msg : msg,
2720                 buttons: false,
2721                 progress:true,
2722                 closable:false,
2723                 minWidth: this.minProgressWidth,
2724                 modal : true
2725             });
2726             return this;
2727         },
2728
2729         /**
2730          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2731          * If a callback function is passed it will be called after the user clicks the button, and the
2732          * id of the button that was clicked will be passed as the only parameter to the callback
2733          * (could also be the top-right close button).
2734          * @param {String} title The title bar text
2735          * @param {String} msg The message box body text
2736          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2737          * @param {Object} scope (optional) The scope of the callback function
2738          * @return {Roo.MessageBox} This message box
2739          */
2740         alert : function(title, msg, fn, scope){
2741             this.show({
2742                 title : title,
2743                 msg : msg,
2744                 buttons: this.OK,
2745                 fn: fn,
2746                 scope : scope,
2747                 modal : true
2748             });
2749             return this;
2750         },
2751
2752         /**
2753          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2754          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2755          * You are responsible for closing the message box when the process is complete.
2756          * @param {String} msg The message box body text
2757          * @param {String} title (optional) The title bar text
2758          * @return {Roo.MessageBox} This message box
2759          */
2760         wait : function(msg, title){
2761             this.show({
2762                 title : title,
2763                 msg : msg,
2764                 buttons: false,
2765                 closable:false,
2766                 progress:true,
2767                 modal:true,
2768                 width:300,
2769                 wait:true
2770             });
2771             waitTimer = Roo.TaskMgr.start({
2772                 run: function(i){
2773                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2774                 },
2775                 interval: 1000
2776             });
2777             return this;
2778         },
2779
2780         /**
2781          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2782          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2783          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2784          * @param {String} title The title bar text
2785          * @param {String} msg The message box body text
2786          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2787          * @param {Object} scope (optional) The scope of the callback function
2788          * @return {Roo.MessageBox} This message box
2789          */
2790         confirm : function(title, msg, fn, scope){
2791             this.show({
2792                 title : title,
2793                 msg : msg,
2794                 buttons: this.YESNO,
2795                 fn: fn,
2796                 scope : scope,
2797                 modal : true
2798             });
2799             return this;
2800         },
2801
2802         /**
2803          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2804          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2805          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2806          * (could also be the top-right close button) and the text that was entered will be passed as the two
2807          * parameters to the callback.
2808          * @param {String} title The title bar text
2809          * @param {String} msg The message box body text
2810          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2811          * @param {Object} scope (optional) The scope of the callback function
2812          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2813          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2814          * @return {Roo.MessageBox} This message box
2815          */
2816         prompt : function(title, msg, fn, scope, multiline){
2817             this.show({
2818                 title : title,
2819                 msg : msg,
2820                 buttons: this.OKCANCEL,
2821                 fn: fn,
2822                 minWidth:250,
2823                 scope : scope,
2824                 prompt:true,
2825                 multiline: multiline,
2826                 modal : true
2827             });
2828             return this;
2829         },
2830
2831         /**
2832          * Button config that displays a single OK button
2833          * @type Object
2834          */
2835         OK : {ok:true},
2836         /**
2837          * Button config that displays Yes and No buttons
2838          * @type Object
2839          */
2840         YESNO : {yes:true, no:true},
2841         /**
2842          * Button config that displays OK and Cancel buttons
2843          * @type Object
2844          */
2845         OKCANCEL : {ok:true, cancel:true},
2846         /**
2847          * Button config that displays Yes, No and Cancel buttons
2848          * @type Object
2849          */
2850         YESNOCANCEL : {yes:true, no:true, cancel:true},
2851
2852         /**
2853          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2854          * @type Number
2855          */
2856         defaultTextHeight : 75,
2857         /**
2858          * The maximum width in pixels of the message box (defaults to 600)
2859          * @type Number
2860          */
2861         maxWidth : 600,
2862         /**
2863          * The minimum width in pixels of the message box (defaults to 100)
2864          * @type Number
2865          */
2866         minWidth : 100,
2867         /**
2868          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
2869          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2870          * @type Number
2871          */
2872         minProgressWidth : 250,
2873         /**
2874          * An object containing the default button text strings that can be overriden for localized language support.
2875          * Supported properties are: ok, cancel, yes and no.
2876          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2877          * @type Object
2878          */
2879         buttonText : {
2880             ok : "OK",
2881             cancel : "Cancel",
2882             yes : "Yes",
2883             no : "No"
2884         }
2885     };
2886 }();
2887
2888 /**
2889  * Shorthand for {@link Roo.MessageBox}
2890  */
2891 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox 
2892 Roo.Msg = Roo.Msg || Roo.MessageBox;
2893 /*
2894  * - LGPL
2895  *
2896  * navbar
2897  * 
2898  */
2899
2900 /**
2901  * @class Roo.bootstrap.Navbar
2902  * @extends Roo.bootstrap.Component
2903  * Bootstrap Navbar class
2904
2905  * @constructor
2906  * Create a new Navbar
2907  * @param {Object} config The config object
2908  */
2909
2910
2911 Roo.bootstrap.Navbar = function(config){
2912     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2913     
2914 };
2915
2916 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2917     
2918     
2919    
2920     // private
2921     navItems : false,
2922     loadMask : false,
2923     
2924     
2925     getAutoCreate : function(){
2926         
2927         
2928         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
2929         
2930     },
2931     
2932     initEvents :function ()
2933     {
2934         //Roo.log(this.el.select('.navbar-toggle',true));
2935         this.el.select('.navbar-toggle',true).on('click', function() {
2936            // Roo.log('click');
2937             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
2938         }, this);
2939         
2940         var mark = {
2941             tag: "div",
2942             cls:"x-dlg-mask"
2943         }
2944         
2945         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2946         
2947         var size = this.el.getSize();
2948         this.maskEl.setSize(size.width, size.height);
2949         this.maskEl.enableDisplayMode("block");
2950         this.maskEl.hide();
2951         
2952         if(this.loadMask){
2953             this.maskEl.show();
2954         }
2955     },
2956     
2957     
2958     getChildContainer : function()
2959     {
2960         if (this.el.select('.collapse').getCount()) {
2961             return this.el.select('.collapse',true).first();
2962         }
2963         
2964         return this.el;
2965     },
2966     
2967     mask : function()
2968     {
2969         this.maskEl.show();
2970     },
2971     
2972     unmask : function()
2973     {
2974         this.maskEl.hide();
2975     } 
2976     
2977     
2978     
2979     
2980 });
2981
2982
2983
2984  
2985
2986  /*
2987  * - LGPL
2988  *
2989  * navbar
2990  * 
2991  */
2992
2993 /**
2994  * @class Roo.bootstrap.NavSimplebar
2995  * @extends Roo.bootstrap.Navbar
2996  * Bootstrap Sidebar class
2997  *
2998  * @cfg {Boolean} inverse is inverted color
2999  * 
3000  * @cfg {String} type (nav | pills | tabs)
3001  * @cfg {Boolean} arrangement stacked | justified
3002  * @cfg {String} align (left | right) alignment
3003  * 
3004  * @cfg {Boolean} main (true|false) main nav bar? default false
3005  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3006  * 
3007  * @cfg {String} tag (header|footer|nav|div) default is nav 
3008
3009  * 
3010  * 
3011  * 
3012  * @constructor
3013  * Create a new Sidebar
3014  * @param {Object} config The config object
3015  */
3016
3017
3018 Roo.bootstrap.NavSimplebar = function(config){
3019     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3020 };
3021
3022 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3023     
3024     inverse: false,
3025     
3026     type: false,
3027     arrangement: '',
3028     align : false,
3029     
3030     
3031     
3032     main : false,
3033     
3034     
3035     tag : false,
3036     
3037     
3038     getAutoCreate : function(){
3039         
3040         
3041         var cfg = {
3042             tag : this.tag || 'div',
3043             cls : 'navbar'
3044         };
3045           
3046         
3047         cfg.cn = [
3048             {
3049                 cls: 'nav',
3050                 tag : 'ul'
3051             }
3052         ];
3053         
3054          
3055         this.type = this.type || 'nav';
3056         if (['tabs','pills'].indexOf(this.type)!==-1) {
3057             cfg.cn[0].cls += ' nav-' + this.type
3058         
3059         
3060         } else {
3061             if (this.type!=='nav') {
3062                 Roo.log('nav type must be nav/tabs/pills')
3063             }
3064             cfg.cn[0].cls += ' navbar-nav'
3065         }
3066         
3067         
3068         
3069         
3070         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3071             cfg.cn[0].cls += ' nav-' + this.arrangement;
3072         }
3073         
3074         
3075         if (this.align === 'right') {
3076             cfg.cn[0].cls += ' navbar-right';
3077         }
3078         
3079         if (this.inverse) {
3080             cfg.cls += ' navbar-inverse';
3081             
3082         }
3083         
3084         
3085         return cfg;
3086     
3087         
3088     }
3089     
3090     
3091     
3092 });
3093
3094
3095
3096  
3097
3098  
3099        /*
3100  * - LGPL
3101  *
3102  * navbar
3103  * 
3104  */
3105
3106 /**
3107  * @class Roo.bootstrap.NavHeaderbar
3108  * @extends Roo.bootstrap.NavSimplebar
3109  * Bootstrap Sidebar class
3110  *
3111  * @cfg {String} brand what is brand
3112  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3113  * @cfg {String} brand_href href of the brand
3114  * @cfg {Boolean} srButton generate the sr-only button (true | false) default true
3115  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3116  * 
3117  * @constructor
3118  * Create a new Sidebar
3119  * @param {Object} config The config object
3120  */
3121
3122
3123 Roo.bootstrap.NavHeaderbar = function(config){
3124     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3125 };
3126
3127 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3128     
3129     position: '',
3130     brand: '',
3131     brand_href: false,
3132     srButton : true,
3133     autohide : false,
3134     
3135     getAutoCreate : function(){
3136         
3137         var   cfg = {
3138             tag: this.nav || 'nav',
3139             cls: 'navbar',
3140             role: 'navigation',
3141             cn: []
3142         };
3143         
3144         if(this.srButton){
3145             cfg.cn.push({
3146                 tag: 'div',
3147                 cls: 'navbar-header',
3148                 cn: [
3149                     {
3150                         tag: 'button',
3151                         type: 'button',
3152                         cls: 'navbar-toggle',
3153                         'data-toggle': 'collapse',
3154                         cn: [
3155                             {
3156                                 tag: 'span',
3157                                 cls: 'sr-only',
3158                                 html: 'Toggle navigation'
3159                             },
3160                             {
3161                                 tag: 'span',
3162                                 cls: 'icon-bar'
3163                             },
3164                             {
3165                                 tag: 'span',
3166                                 cls: 'icon-bar'
3167                             },
3168                             {
3169                                 tag: 'span',
3170                                 cls: 'icon-bar'
3171                             }
3172                         ]
3173                     }
3174                 ]
3175             });
3176         }
3177         
3178         cfg.cn.push({
3179             tag: 'div',
3180             cls: 'collapse navbar-collapse',
3181             cn : []
3182         });
3183         
3184         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3185         
3186         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3187             cfg.cls += ' navbar-' + this.position;
3188             
3189             // tag can override this..
3190             
3191             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3192         }
3193         
3194         if (this.brand !== '') {
3195             cfg.cn[0].cn.push({
3196                 tag: 'a',
3197                 href: this.brand_href ? this.brand_href : '#',
3198                 cls: 'navbar-brand',
3199                 cn: [
3200                 this.brand
3201                 ]
3202             });
3203         }
3204         
3205         if(this.main){
3206             cfg.cls += ' main-nav';
3207         }
3208         
3209         
3210         return cfg;
3211
3212         
3213     },
3214     initEvents : function()
3215     {
3216         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3217         
3218         if (this.autohide) {
3219             
3220             var prevScroll = 0;
3221             var ft = this.el;
3222             
3223             Roo.get(document).on('scroll',function(e) {
3224                 var ns = Roo.get(document).getScroll().top;
3225                 var os = prevScroll;
3226                 prevScroll = ns;
3227                 
3228                 if(ns > os){
3229                     ft.removeClass('slideDown');
3230                     ft.addClass('slideUp');
3231                     return;
3232                 }
3233                 ft.removeClass('slideUp');
3234                 ft.addClass('slideDown');
3235                  
3236               
3237           },this);
3238         }
3239     }    
3240           
3241       
3242     
3243     
3244 });
3245
3246
3247
3248  
3249
3250  /*
3251  * - LGPL
3252  *
3253  * navbar
3254  * 
3255  */
3256
3257 /**
3258  * @class Roo.bootstrap.NavSidebar
3259  * @extends Roo.bootstrap.Navbar
3260  * Bootstrap Sidebar class
3261  * 
3262  * @constructor
3263  * Create a new Sidebar
3264  * @param {Object} config The config object
3265  */
3266
3267
3268 Roo.bootstrap.NavSidebar = function(config){
3269     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3270 };
3271
3272 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3273     
3274     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3275     
3276     getAutoCreate : function(){
3277         
3278         
3279         return  {
3280             tag: 'div',
3281             cls: 'sidebar sidebar-nav'
3282         };
3283     
3284         
3285     }
3286     
3287     
3288     
3289 });
3290
3291
3292
3293  
3294
3295  /*
3296  * - LGPL
3297  *
3298  * nav group
3299  * 
3300  */
3301
3302 /**
3303  * @class Roo.bootstrap.NavGroup
3304  * @extends Roo.bootstrap.Component
3305  * Bootstrap NavGroup class
3306  * @cfg {String} align left | right
3307  * @cfg {Boolean} inverse false | true
3308  * @cfg {String} type (nav|pills|tab) default nav
3309  * @cfg {String} navId - reference Id for navbar.
3310
3311  * 
3312  * @constructor
3313  * Create a new nav group
3314  * @param {Object} config The config object
3315  */
3316
3317 Roo.bootstrap.NavGroup = function(config){
3318     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3319     this.navItems = [];
3320    
3321     Roo.bootstrap.NavGroup.register(this);
3322      this.addEvents({
3323         /**
3324              * @event changed
3325              * Fires when the active item changes
3326              * @param {Roo.bootstrap.NavGroup} this
3327              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3328              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3329          */
3330         'changed': true
3331      });
3332     
3333 };
3334
3335 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3336     
3337     align: '',
3338     inverse: false,
3339     form: false,
3340     type: 'nav',
3341     navId : '',
3342     // private
3343     
3344     navItems : false, 
3345     
3346     getAutoCreate : function()
3347     {
3348         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3349         
3350         cfg = {
3351             tag : 'ul',
3352             cls: 'nav' 
3353         }
3354         
3355         if (['tabs','pills'].indexOf(this.type)!==-1) {
3356             cfg.cls += ' nav-' + this.type
3357         } else {
3358             if (this.type!=='nav') {
3359                 Roo.log('nav type must be nav/tabs/pills')
3360             }
3361             cfg.cls += ' navbar-nav'
3362         }
3363         
3364         if (this.parent().sidebar) {
3365             cfg = {
3366                 tag: 'ul',
3367                 cls: 'dashboard-menu sidebar-menu'
3368             }
3369             
3370             return cfg;
3371         }
3372         
3373         if (this.form === true) {
3374             cfg = {
3375                 tag: 'form',
3376                 cls: 'navbar-form'
3377             }
3378             
3379             if (this.align === 'right') {
3380                 cfg.cls += ' navbar-right';
3381             } else {
3382                 cfg.cls += ' navbar-left';
3383             }
3384         }
3385         
3386         if (this.align === 'right') {
3387             cfg.cls += ' navbar-right';
3388         }
3389         
3390         if (this.inverse) {
3391             cfg.cls += ' navbar-inverse';
3392             
3393         }
3394         
3395         
3396         return cfg;
3397     },
3398     /**
3399     * sets the active Navigation item
3400     * @param {Roo.bootstrap.NavItem} the new current navitem
3401     */
3402     setActiveItem : function(item)
3403     {
3404         var prev = false;
3405         Roo.each(this.navItems, function(v){
3406             if (v == item) {
3407                 return ;
3408             }
3409             if (v.isActive()) {
3410                 v.setActive(false, true);
3411                 prev = v;
3412                 
3413             }
3414             
3415         });
3416
3417         item.setActive(true, true);
3418         this.fireEvent('changed', this, item, prev);
3419         
3420         
3421     },
3422     /**
3423     * gets the active Navigation item
3424     * @return {Roo.bootstrap.NavItem} the current navitem
3425     */
3426     getActive : function()
3427     {
3428         
3429         var prev = false;
3430         Roo.each(this.navItems, function(v){
3431             
3432             if (v.isActive()) {
3433                 prev = v;
3434                 
3435             }
3436             
3437         });
3438         return prev;
3439     },
3440     
3441     indexOfNav : function()
3442     {
3443         
3444         var prev = false;
3445         Roo.each(this.navItems, function(v,i){
3446             
3447             if (v.isActive()) {
3448                 prev = i;
3449                 
3450             }
3451             
3452         });
3453         return prev;
3454     },
3455     /**
3456     * adds a Navigation item
3457     * @param {Roo.bootstrap.NavItem} the navitem to add
3458     */
3459     addItem : function(cfg)
3460     {
3461         var cn = new Roo.bootstrap.NavItem(cfg);
3462         this.register(cn);
3463         cn.parentId = this.id;
3464         cn.onRender(this.el, null);
3465         return cn;
3466     },
3467     /**
3468     * register a Navigation item
3469     * @param {Roo.bootstrap.NavItem} the navitem to add
3470     */
3471     register : function(item)
3472     {
3473         this.navItems.push( item);
3474         item.navId = this.navId;
3475     
3476     },
3477   
3478     
3479     getNavItem: function(tabId)
3480     {
3481         var ret = false;
3482         Roo.each(this.navItems, function(e) {
3483             if (e.tabId == tabId) {
3484                ret =  e;
3485                return false;
3486             }
3487             return true;
3488             
3489         });
3490         return ret;
3491     },
3492     
3493     setActiveNext : function()
3494     {
3495         var i = this.indexOfNav(this.getActive());
3496         if (i > this.navItems.length) {
3497             return;
3498         }
3499         this.setActiveItem(this.navItems[i+1]);
3500     },
3501     setActivePrev : function()
3502     {
3503         var i = this.indexOfNav(this.getActive());
3504         if (i  < 1) {
3505             return;
3506         }
3507         this.setActiveItem(this.navItems[i-1]);
3508     },
3509     clearWasActive : function(except) {
3510         Roo.each(this.navItems, function(e) {
3511             if (e.tabId != except.tabId && e.was_active) {
3512                e.was_active = false;
3513                return false;
3514             }
3515             return true;
3516             
3517         });
3518     },
3519     getWasActive : function ()
3520     {
3521         var r = false;
3522         Roo.each(this.navItems, function(e) {
3523             if (e.was_active) {
3524                r = e;
3525                return false;
3526             }
3527             return true;
3528             
3529         });
3530         return r;
3531     }
3532     
3533     
3534 });
3535
3536  
3537 Roo.apply(Roo.bootstrap.NavGroup, {
3538     
3539     groups: {},
3540      /**
3541     * register a Navigation Group
3542     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3543     */
3544     register : function(navgrp)
3545     {
3546         this.groups[navgrp.navId] = navgrp;
3547         
3548     },
3549     /**
3550     * fetch a Navigation Group based on the navigation ID
3551     * @param {string} the navgroup to add
3552     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3553     */
3554     get: function(navId) {
3555         if (typeof(this.groups[navId]) == 'undefined') {
3556             return false;
3557             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3558         }
3559         return this.groups[navId] ;
3560     }
3561     
3562     
3563     
3564 });
3565
3566  /*
3567  * - LGPL
3568  *
3569  * row
3570  * 
3571  */
3572
3573 /**
3574  * @class Roo.bootstrap.NavItem
3575  * @extends Roo.bootstrap.Component
3576  * Bootstrap Navbar.NavItem class
3577  * @cfg {String} href  link to
3578  * @cfg {String} html content of button
3579  * @cfg {String} badge text inside badge
3580  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3581  * @cfg {String} glyphicon name of glyphicon
3582  * @cfg {String} icon name of font awesome icon
3583  * @cfg {Boolean} active Is item active
3584  * @cfg {Boolean} disabled Is item disabled
3585  
3586  * @cfg {Boolean} preventDefault (true | false) default false
3587  * @cfg {String} tabId the tab that this item activates.
3588  * @cfg {String} tagtype (a|span) render as a href or span?
3589   
3590  * @constructor
3591  * Create a new Navbar Item
3592  * @param {Object} config The config object
3593  */
3594 Roo.bootstrap.NavItem = function(config){
3595     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3596     this.addEvents({
3597         // raw events
3598         /**
3599          * @event click
3600          * The raw click event for the entire grid.
3601          * @param {Roo.EventObject} e
3602          */
3603         "click" : true,
3604          /**
3605             * @event changed
3606             * Fires when the active item active state changes
3607             * @param {Roo.bootstrap.NavItem} this
3608             * @param {boolean} state the new state
3609              
3610          */
3611         'changed': true
3612     });
3613    
3614 };
3615
3616 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3617     
3618     href: false,
3619     html: '',
3620     badge: '',
3621     icon: false,
3622     glyphicon: false,
3623     active: false,
3624     preventDefault : false,
3625     tabId : false,
3626     tagtype : 'a',
3627     disabled : false,
3628     
3629     was_active : false,
3630     
3631     getAutoCreate : function(){
3632          
3633         var cfg = {
3634             tag: 'li',
3635             cls: 'nav-item'
3636             
3637         }
3638         if (this.active) {
3639             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3640         }
3641         if (this.disabled) {
3642             cfg.cls += ' disabled';
3643         }
3644         
3645         if (this.href || this.html || this.glyphicon || this.icon) {
3646             cfg.cn = [
3647                 {
3648                     tag: this.tagtype,
3649                     href : this.href || "#",
3650                     html: this.html || ''
3651                 }
3652             ];
3653             
3654             if (this.icon) {
3655                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3656             }
3657
3658             if(this.glyphicon) {
3659                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
3660             }
3661             
3662             if (this.menu) {
3663                 
3664                 cfg.cn[0].html += " <span class='caret'></span>";
3665              
3666             }
3667             
3668             if (this.badge !== '') {
3669                  
3670                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3671             }
3672         }
3673         
3674         
3675         
3676         return cfg;
3677     },
3678     initEvents: function() {
3679        // Roo.log('init events?');
3680        // Roo.log(this.el.dom);
3681         if (typeof (this.menu) != 'undefined') {
3682             this.menu.parentType = this.xtype;
3683             this.menu.triggerEl = this.el;
3684             this.addxtype(Roo.apply({}, this.menu));
3685         }
3686
3687        
3688         this.el.select('a',true).on('click', this.onClick, this);
3689         // at this point parent should be available..
3690         this.parent().register(this);
3691     },
3692     
3693     onClick : function(e)
3694     {
3695          
3696         if(this.preventDefault){
3697             e.preventDefault();
3698         }
3699         if (this.disabled) {
3700             return;
3701         }
3702         
3703         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3704         if (tg && tg.transition) {
3705             Roo.log("waiting for the transitionend");
3706             return;
3707         }
3708         
3709         Roo.log("fire event clicked");
3710         if(this.fireEvent('click', this, e) === false){
3711             return;
3712         };
3713         var p = this.parent();
3714         if (['tabs','pills'].indexOf(p.type)!==-1) {
3715             if (typeof(p.setActiveItem) !== 'undefined') {
3716                 p.setActiveItem(this);
3717             }
3718         }
3719         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
3720         if (p.parentType == 'NavHeaderbar' && !this.menu) {
3721             // remove the collapsed menu expand...
3722             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
3723         }
3724         
3725     },
3726     
3727     isActive: function () {
3728         return this.active
3729     },
3730     setActive : function(state, fire, is_was_active)
3731     {
3732         if (this.active && !state & this.navId) {
3733             this.was_active = true;
3734             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3735             if (nv) {
3736                 nv.clearWasActive(this);
3737             }
3738             
3739         }
3740         this.active = state;
3741         
3742         if (!state ) {
3743             this.el.removeClass('active');
3744         } else if (!this.el.hasClass('active')) {
3745             this.el.addClass('active');
3746         }
3747         if (fire) {
3748             this.fireEvent('changed', this, state);
3749         }
3750         
3751         // show a panel if it's registered and related..
3752         
3753         if (!this.navId || !this.tabId || !state || is_was_active) {
3754             return;
3755         }
3756         
3757         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3758         if (!tg) {
3759             return;
3760         }
3761         var pan = tg.getPanelByName(this.tabId);
3762         if (!pan) {
3763             return;
3764         }
3765         // if we can not flip to new panel - go back to old nav highlight..
3766         if (false == tg.showPanel(pan)) {
3767             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3768             if (nv) {
3769                 var onav = nv.getWasActive();
3770                 if (onav) {
3771                     onav.setActive(true, false, true);
3772                 }
3773             }
3774             
3775         }
3776         
3777         
3778         
3779     },
3780      // this should not be here...
3781     setDisabled : function(state)
3782     {
3783         this.disabled = state;
3784         if (!state ) {
3785             this.el.removeClass('disabled');
3786         } else if (!this.el.hasClass('disabled')) {
3787             this.el.addClass('disabled');
3788         }
3789         
3790     }
3791 });
3792  
3793
3794  /*
3795  * - LGPL
3796  *
3797  * sidebar item
3798  *
3799  *  li
3800  *    <span> icon </span>
3801  *    <span> text </span>
3802  *    <span>badge </span>
3803  */
3804
3805 /**
3806  * @class Roo.bootstrap.NavSidebarItem
3807  * @extends Roo.bootstrap.NavItem
3808  * Bootstrap Navbar.NavSidebarItem class
3809  * @constructor
3810  * Create a new Navbar Button
3811  * @param {Object} config The config object
3812  */
3813 Roo.bootstrap.NavSidebarItem = function(config){
3814     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3815     this.addEvents({
3816         // raw events
3817         /**
3818          * @event click
3819          * The raw click event for the entire grid.
3820          * @param {Roo.EventObject} e
3821          */
3822         "click" : true,
3823          /**
3824             * @event changed
3825             * Fires when the active item active state changes
3826             * @param {Roo.bootstrap.NavSidebarItem} this
3827             * @param {boolean} state the new state
3828              
3829          */
3830         'changed': true
3831     });
3832    
3833 };
3834
3835 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
3836     
3837     
3838     getAutoCreate : function(){
3839         
3840         
3841         var a = {
3842                 tag: 'a',
3843                 href : this.href || '#',
3844                 cls: '',
3845                 html : '',
3846                 cn : []
3847         };
3848         var cfg = {
3849             tag: 'li',
3850             cls: '',
3851             cn: [ a ]
3852         }
3853         var span = {
3854             tag: 'span',
3855             html : this.html || ''
3856         }
3857         
3858         
3859         if (this.active) {
3860             cfg.cls += ' active';
3861         }
3862         
3863         // left icon..
3864         if (this.glyphicon || this.icon) {
3865             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
3866             a.cn.push({ tag : 'i', cls : c }) ;
3867         }
3868         // html..
3869         a.cn.push(span);
3870         // then badge..
3871         if (this.badge !== '') {
3872             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
3873         }
3874         // fi
3875         if (this.menu) {
3876             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
3877             a.cls += 'dropdown-toggle treeview' ;
3878             
3879         }
3880         
3881         
3882         
3883         return cfg;
3884          
3885            
3886     }
3887    
3888      
3889  
3890 });
3891  
3892
3893  /*
3894  * - LGPL
3895  *
3896  * row
3897  * 
3898  */
3899
3900 /**
3901  * @class Roo.bootstrap.Row
3902  * @extends Roo.bootstrap.Component
3903  * Bootstrap Row class (contains columns...)
3904  * 
3905  * @constructor
3906  * Create a new Row
3907  * @param {Object} config The config object
3908  */
3909
3910 Roo.bootstrap.Row = function(config){
3911     Roo.bootstrap.Row.superclass.constructor.call(this, config);
3912 };
3913
3914 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
3915     
3916     getAutoCreate : function(){
3917        return {
3918             cls: 'row clearfix'
3919        };
3920     }
3921     
3922     
3923 });
3924
3925  
3926
3927  /*
3928  * - LGPL
3929  *
3930  * element
3931  * 
3932  */
3933
3934 /**
3935  * @class Roo.bootstrap.Element
3936  * @extends Roo.bootstrap.Component
3937  * Bootstrap Element class
3938  * @cfg {String} html contents of the element
3939  * @cfg {String} tag tag of the element
3940  * @cfg {String} cls class of the element
3941  * 
3942  * @constructor
3943  * Create a new Element
3944  * @param {Object} config The config object
3945  */
3946
3947 Roo.bootstrap.Element = function(config){
3948     Roo.bootstrap.Element.superclass.constructor.call(this, config);
3949 };
3950
3951 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
3952     
3953     tag: 'div',
3954     cls: '',
3955     html: '',
3956      
3957     
3958     getAutoCreate : function(){
3959         
3960         var cfg = {
3961             tag: this.tag,
3962             cls: this.cls,
3963             html: this.html
3964         }
3965         
3966         
3967         
3968         return cfg;
3969     }
3970    
3971 });
3972
3973  
3974
3975  /*
3976  * - LGPL
3977  *
3978  * pagination
3979  * 
3980  */
3981
3982 /**
3983  * @class Roo.bootstrap.Pagination
3984  * @extends Roo.bootstrap.Component
3985  * Bootstrap Pagination class
3986  * @cfg {String} size xs | sm | md | lg
3987  * @cfg {Boolean} inverse false | true
3988  * 
3989  * @constructor
3990  * Create a new Pagination
3991  * @param {Object} config The config object
3992  */
3993
3994 Roo.bootstrap.Pagination = function(config){
3995     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
3996 };
3997
3998 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
3999     
4000     cls: false,
4001     size: false,
4002     inverse: false,
4003     
4004     getAutoCreate : function(){
4005         var cfg = {
4006             tag: 'ul',
4007                 cls: 'pagination'
4008         };
4009         if (this.inverse) {
4010             cfg.cls += ' inverse';
4011         }
4012         if (this.html) {
4013             cfg.html=this.html;
4014         }
4015         if (this.cls) {
4016             cfg.cls += " " + this.cls;
4017         }
4018         return cfg;
4019     }
4020    
4021 });
4022
4023  
4024
4025  /*
4026  * - LGPL
4027  *
4028  * Pagination item
4029  * 
4030  */
4031
4032
4033 /**
4034  * @class Roo.bootstrap.PaginationItem
4035  * @extends Roo.bootstrap.Component
4036  * Bootstrap PaginationItem class
4037  * @cfg {String} html text
4038  * @cfg {String} href the link
4039  * @cfg {Boolean} preventDefault (true | false) default true
4040  * @cfg {Boolean} active (true | false) default false
4041  * 
4042  * 
4043  * @constructor
4044  * Create a new PaginationItem
4045  * @param {Object} config The config object
4046  */
4047
4048
4049 Roo.bootstrap.PaginationItem = function(config){
4050     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4051     this.addEvents({
4052         // raw events
4053         /**
4054          * @event click
4055          * The raw click event for the entire grid.
4056          * @param {Roo.EventObject} e
4057          */
4058         "click" : true
4059     });
4060 };
4061
4062 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4063     
4064     href : false,
4065     html : false,
4066     preventDefault: true,
4067     active : false,
4068     cls : false,
4069     
4070     getAutoCreate : function(){
4071         var cfg= {
4072             tag: 'li',
4073             cn: [
4074                 {
4075                     tag : 'a',
4076                     href : this.href ? this.href : '#',
4077                     html : this.html ? this.html : ''
4078                 }
4079             ]
4080         };
4081         
4082         if(this.cls){
4083             cfg.cls = this.cls;
4084         }
4085         
4086         if(this.active){
4087             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4088         }
4089         
4090         return cfg;
4091     },
4092     
4093     initEvents: function() {
4094         
4095         this.el.on('click', this.onClick, this);
4096         
4097     },
4098     onClick : function(e)
4099     {
4100         Roo.log('PaginationItem on click ');
4101         if(this.preventDefault){
4102             e.preventDefault();
4103         }
4104         
4105         this.fireEvent('click', this, e);
4106     }
4107    
4108 });
4109
4110  
4111
4112  /*
4113  * - LGPL
4114  *
4115  * slider
4116  * 
4117  */
4118
4119
4120 /**
4121  * @class Roo.bootstrap.Slider
4122  * @extends Roo.bootstrap.Component
4123  * Bootstrap Slider class
4124  *    
4125  * @constructor
4126  * Create a new Slider
4127  * @param {Object} config The config object
4128  */
4129
4130 Roo.bootstrap.Slider = function(config){
4131     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4132 };
4133
4134 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4135     
4136     getAutoCreate : function(){
4137         
4138         var cfg = {
4139             tag: 'div',
4140             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4141             cn: [
4142                 {
4143                     tag: 'a',
4144                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4145                 }
4146             ]
4147         }
4148         
4149         return cfg;
4150     }
4151    
4152 });
4153
4154  /*
4155  * Based on:
4156  * Ext JS Library 1.1.1
4157  * Copyright(c) 2006-2007, Ext JS, LLC.
4158  *
4159  * Originally Released Under LGPL - original licence link has changed is not relivant.
4160  *
4161  * Fork - LGPL
4162  * <script type="text/javascript">
4163  */
4164  
4165
4166 /**
4167  * @class Roo.grid.ColumnModel
4168  * @extends Roo.util.Observable
4169  * This is the default implementation of a ColumnModel used by the Grid. It defines
4170  * the columns in the grid.
4171  * <br>Usage:<br>
4172  <pre><code>
4173  var colModel = new Roo.grid.ColumnModel([
4174         {header: "Ticker", width: 60, sortable: true, locked: true},
4175         {header: "Company Name", width: 150, sortable: true},
4176         {header: "Market Cap.", width: 100, sortable: true},
4177         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4178         {header: "Employees", width: 100, sortable: true, resizable: false}
4179  ]);
4180  </code></pre>
4181  * <p>
4182  
4183  * The config options listed for this class are options which may appear in each
4184  * individual column definition.
4185  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4186  * @constructor
4187  * @param {Object} config An Array of column config objects. See this class's
4188  * config objects for details.
4189 */
4190 Roo.grid.ColumnModel = function(config){
4191         /**
4192      * The config passed into the constructor
4193      */
4194     this.config = config;
4195     this.lookup = {};
4196
4197     // if no id, create one
4198     // if the column does not have a dataIndex mapping,
4199     // map it to the order it is in the config
4200     for(var i = 0, len = config.length; i < len; i++){
4201         var c = config[i];
4202         if(typeof c.dataIndex == "undefined"){
4203             c.dataIndex = i;
4204         }
4205         if(typeof c.renderer == "string"){
4206             c.renderer = Roo.util.Format[c.renderer];
4207         }
4208         if(typeof c.id == "undefined"){
4209             c.id = Roo.id();
4210         }
4211         if(c.editor && c.editor.xtype){
4212             c.editor  = Roo.factory(c.editor, Roo.grid);
4213         }
4214         if(c.editor && c.editor.isFormField){
4215             c.editor = new Roo.grid.GridEditor(c.editor);
4216         }
4217         this.lookup[c.id] = c;
4218     }
4219
4220     /**
4221      * The width of columns which have no width specified (defaults to 100)
4222      * @type Number
4223      */
4224     this.defaultWidth = 100;
4225
4226     /**
4227      * Default sortable of columns which have no sortable specified (defaults to false)
4228      * @type Boolean
4229      */
4230     this.defaultSortable = false;
4231
4232     this.addEvents({
4233         /**
4234              * @event widthchange
4235              * Fires when the width of a column changes.
4236              * @param {ColumnModel} this
4237              * @param {Number} columnIndex The column index
4238              * @param {Number} newWidth The new width
4239              */
4240             "widthchange": true,
4241         /**
4242              * @event headerchange
4243              * Fires when the text of a header changes.
4244              * @param {ColumnModel} this
4245              * @param {Number} columnIndex The column index
4246              * @param {Number} newText The new header text
4247              */
4248             "headerchange": true,
4249         /**
4250              * @event hiddenchange
4251              * Fires when a column is hidden or "unhidden".
4252              * @param {ColumnModel} this
4253              * @param {Number} columnIndex The column index
4254              * @param {Boolean} hidden true if hidden, false otherwise
4255              */
4256             "hiddenchange": true,
4257             /**
4258          * @event columnmoved
4259          * Fires when a column is moved.
4260          * @param {ColumnModel} this
4261          * @param {Number} oldIndex
4262          * @param {Number} newIndex
4263          */
4264         "columnmoved" : true,
4265         /**
4266          * @event columlockchange
4267          * Fires when a column's locked state is changed
4268          * @param {ColumnModel} this
4269          * @param {Number} colIndex
4270          * @param {Boolean} locked true if locked
4271          */
4272         "columnlockchange" : true
4273     });
4274     Roo.grid.ColumnModel.superclass.constructor.call(this);
4275 };
4276 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4277     /**
4278      * @cfg {String} header The header text to display in the Grid view.
4279      */
4280     /**
4281      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4282      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4283      * specified, the column's index is used as an index into the Record's data Array.
4284      */
4285     /**
4286      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4287      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4288      */
4289     /**
4290      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4291      * Defaults to the value of the {@link #defaultSortable} property.
4292      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4293      */
4294     /**
4295      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4296      */
4297     /**
4298      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4299      */
4300     /**
4301      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4302      */
4303     /**
4304      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4305      */
4306     /**
4307      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4308      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4309      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4310      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4311      */
4312        /**
4313      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4314      */
4315     /**
4316      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4317      */
4318
4319     /**
4320      * Returns the id of the column at the specified index.
4321      * @param {Number} index The column index
4322      * @return {String} the id
4323      */
4324     getColumnId : function(index){
4325         return this.config[index].id;
4326     },
4327
4328     /**
4329      * Returns the column for a specified id.
4330      * @param {String} id The column id
4331      * @return {Object} the column
4332      */
4333     getColumnById : function(id){
4334         return this.lookup[id];
4335     },
4336
4337     
4338     /**
4339      * Returns the column for a specified dataIndex.
4340      * @param {String} dataIndex The column dataIndex
4341      * @return {Object|Boolean} the column or false if not found
4342      */
4343     getColumnByDataIndex: function(dataIndex){
4344         var index = this.findColumnIndex(dataIndex);
4345         return index > -1 ? this.config[index] : false;
4346     },
4347     
4348     /**
4349      * Returns the index for a specified column id.
4350      * @param {String} id The column id
4351      * @return {Number} the index, or -1 if not found
4352      */
4353     getIndexById : function(id){
4354         for(var i = 0, len = this.config.length; i < len; i++){
4355             if(this.config[i].id == id){
4356                 return i;
4357             }
4358         }
4359         return -1;
4360     },
4361     
4362     /**
4363      * Returns the index for a specified column dataIndex.
4364      * @param {String} dataIndex The column dataIndex
4365      * @return {Number} the index, or -1 if not found
4366      */
4367     
4368     findColumnIndex : function(dataIndex){
4369         for(var i = 0, len = this.config.length; i < len; i++){
4370             if(this.config[i].dataIndex == dataIndex){
4371                 return i;
4372             }
4373         }
4374         return -1;
4375     },
4376     
4377     
4378     moveColumn : function(oldIndex, newIndex){
4379         var c = this.config[oldIndex];
4380         this.config.splice(oldIndex, 1);
4381         this.config.splice(newIndex, 0, c);
4382         this.dataMap = null;
4383         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4384     },
4385
4386     isLocked : function(colIndex){
4387         return this.config[colIndex].locked === true;
4388     },
4389
4390     setLocked : function(colIndex, value, suppressEvent){
4391         if(this.isLocked(colIndex) == value){
4392             return;
4393         }
4394         this.config[colIndex].locked = value;
4395         if(!suppressEvent){
4396             this.fireEvent("columnlockchange", this, colIndex, value);
4397         }
4398     },
4399
4400     getTotalLockedWidth : function(){
4401         var totalWidth = 0;
4402         for(var i = 0; i < this.config.length; i++){
4403             if(this.isLocked(i) && !this.isHidden(i)){
4404                 this.totalWidth += this.getColumnWidth(i);
4405             }
4406         }
4407         return totalWidth;
4408     },
4409
4410     getLockedCount : function(){
4411         for(var i = 0, len = this.config.length; i < len; i++){
4412             if(!this.isLocked(i)){
4413                 return i;
4414             }
4415         }
4416     },
4417
4418     /**
4419      * Returns the number of columns.
4420      * @return {Number}
4421      */
4422     getColumnCount : function(visibleOnly){
4423         if(visibleOnly === true){
4424             var c = 0;
4425             for(var i = 0, len = this.config.length; i < len; i++){
4426                 if(!this.isHidden(i)){
4427                     c++;
4428                 }
4429             }
4430             return c;
4431         }
4432         return this.config.length;
4433     },
4434
4435     /**
4436      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4437      * @param {Function} fn
4438      * @param {Object} scope (optional)
4439      * @return {Array} result
4440      */
4441     getColumnsBy : function(fn, scope){
4442         var r = [];
4443         for(var i = 0, len = this.config.length; i < len; i++){
4444             var c = this.config[i];
4445             if(fn.call(scope||this, c, i) === true){
4446                 r[r.length] = c;
4447             }
4448         }
4449         return r;
4450     },
4451
4452     /**
4453      * Returns true if the specified column is sortable.
4454      * @param {Number} col The column index
4455      * @return {Boolean}
4456      */
4457     isSortable : function(col){
4458         if(typeof this.config[col].sortable == "undefined"){
4459             return this.defaultSortable;
4460         }
4461         return this.config[col].sortable;
4462     },
4463
4464     /**
4465      * Returns the rendering (formatting) function defined for the column.
4466      * @param {Number} col The column index.
4467      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4468      */
4469     getRenderer : function(col){
4470         if(!this.config[col].renderer){
4471             return Roo.grid.ColumnModel.defaultRenderer;
4472         }
4473         return this.config[col].renderer;
4474     },
4475
4476     /**
4477      * Sets the rendering (formatting) function for a column.
4478      * @param {Number} col The column index
4479      * @param {Function} fn The function to use to process the cell's raw data
4480      * to return HTML markup for the grid view. The render function is called with
4481      * the following parameters:<ul>
4482      * <li>Data value.</li>
4483      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4484      * <li>css A CSS style string to apply to the table cell.</li>
4485      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4486      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4487      * <li>Row index</li>
4488      * <li>Column index</li>
4489      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4490      */
4491     setRenderer : function(col, fn){
4492         this.config[col].renderer = fn;
4493     },
4494
4495     /**
4496      * Returns the width for the specified column.
4497      * @param {Number} col The column index
4498      * @return {Number}
4499      */
4500     getColumnWidth : function(col){
4501         return this.config[col].width * 1 || this.defaultWidth;
4502     },
4503
4504     /**
4505      * Sets the width for a column.
4506      * @param {Number} col The column index
4507      * @param {Number} width The new width
4508      */
4509     setColumnWidth : function(col, width, suppressEvent){
4510         this.config[col].width = width;
4511         this.totalWidth = null;
4512         if(!suppressEvent){
4513              this.fireEvent("widthchange", this, col, width);
4514         }
4515     },
4516
4517     /**
4518      * Returns the total width of all columns.
4519      * @param {Boolean} includeHidden True to include hidden column widths
4520      * @return {Number}
4521      */
4522     getTotalWidth : function(includeHidden){
4523         if(!this.totalWidth){
4524             this.totalWidth = 0;
4525             for(var i = 0, len = this.config.length; i < len; i++){
4526                 if(includeHidden || !this.isHidden(i)){
4527                     this.totalWidth += this.getColumnWidth(i);
4528                 }
4529             }
4530         }
4531         return this.totalWidth;
4532     },
4533
4534     /**
4535      * Returns the header for the specified column.
4536      * @param {Number} col The column index
4537      * @return {String}
4538      */
4539     getColumnHeader : function(col){
4540         return this.config[col].header;
4541     },
4542
4543     /**
4544      * Sets the header for a column.
4545      * @param {Number} col The column index
4546      * @param {String} header The new header
4547      */
4548     setColumnHeader : function(col, header){
4549         this.config[col].header = header;
4550         this.fireEvent("headerchange", this, col, header);
4551     },
4552
4553     /**
4554      * Returns the tooltip for the specified column.
4555      * @param {Number} col The column index
4556      * @return {String}
4557      */
4558     getColumnTooltip : function(col){
4559             return this.config[col].tooltip;
4560     },
4561     /**
4562      * Sets the tooltip for a column.
4563      * @param {Number} col The column index
4564      * @param {String} tooltip The new tooltip
4565      */
4566     setColumnTooltip : function(col, tooltip){
4567             this.config[col].tooltip = tooltip;
4568     },
4569
4570     /**
4571      * Returns the dataIndex for the specified column.
4572      * @param {Number} col The column index
4573      * @return {Number}
4574      */
4575     getDataIndex : function(col){
4576         return this.config[col].dataIndex;
4577     },
4578
4579     /**
4580      * Sets the dataIndex for a column.
4581      * @param {Number} col The column index
4582      * @param {Number} dataIndex The new dataIndex
4583      */
4584     setDataIndex : function(col, dataIndex){
4585         this.config[col].dataIndex = dataIndex;
4586     },
4587
4588     
4589     
4590     /**
4591      * Returns true if the cell is editable.
4592      * @param {Number} colIndex The column index
4593      * @param {Number} rowIndex The row index
4594      * @return {Boolean}
4595      */
4596     isCellEditable : function(colIndex, rowIndex){
4597         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4598     },
4599
4600     /**
4601      * Returns the editor defined for the cell/column.
4602      * return false or null to disable editing.
4603      * @param {Number} colIndex The column index
4604      * @param {Number} rowIndex The row index
4605      * @return {Object}
4606      */
4607     getCellEditor : function(colIndex, rowIndex){
4608         return this.config[colIndex].editor;
4609     },
4610
4611     /**
4612      * Sets if a column is editable.
4613      * @param {Number} col The column index
4614      * @param {Boolean} editable True if the column is editable
4615      */
4616     setEditable : function(col, editable){
4617         this.config[col].editable = editable;
4618     },
4619
4620
4621     /**
4622      * Returns true if the column is hidden.
4623      * @param {Number} colIndex The column index
4624      * @return {Boolean}
4625      */
4626     isHidden : function(colIndex){
4627         return this.config[colIndex].hidden;
4628     },
4629
4630
4631     /**
4632      * Returns true if the column width cannot be changed
4633      */
4634     isFixed : function(colIndex){
4635         return this.config[colIndex].fixed;
4636     },
4637
4638     /**
4639      * Returns true if the column can be resized
4640      * @return {Boolean}
4641      */
4642     isResizable : function(colIndex){
4643         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4644     },
4645     /**
4646      * Sets if a column is hidden.
4647      * @param {Number} colIndex The column index
4648      * @param {Boolean} hidden True if the column is hidden
4649      */
4650     setHidden : function(colIndex, hidden){
4651         this.config[colIndex].hidden = hidden;
4652         this.totalWidth = null;
4653         this.fireEvent("hiddenchange", this, colIndex, hidden);
4654     },
4655
4656     /**
4657      * Sets the editor for a column.
4658      * @param {Number} col The column index
4659      * @param {Object} editor The editor object
4660      */
4661     setEditor : function(col, editor){
4662         this.config[col].editor = editor;
4663     }
4664 });
4665
4666 Roo.grid.ColumnModel.defaultRenderer = function(value){
4667         if(typeof value == "string" && value.length < 1){
4668             return "&#160;";
4669         }
4670         return value;
4671 };
4672
4673 // Alias for backwards compatibility
4674 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4675 /*
4676  * Based on:
4677  * Ext JS Library 1.1.1
4678  * Copyright(c) 2006-2007, Ext JS, LLC.
4679  *
4680  * Originally Released Under LGPL - original licence link has changed is not relivant.
4681  *
4682  * Fork - LGPL
4683  * <script type="text/javascript">
4684  */
4685  
4686 /**
4687  * @class Roo.LoadMask
4688  * A simple utility class for generically masking elements while loading data.  If the element being masked has
4689  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4690  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
4691  * element's UpdateManager load indicator and will be destroyed after the initial load.
4692  * @constructor
4693  * Create a new LoadMask
4694  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4695  * @param {Object} config The config object
4696  */
4697 Roo.LoadMask = function(el, config){
4698     this.el = Roo.get(el);
4699     Roo.apply(this, config);
4700     if(this.store){
4701         this.store.on('beforeload', this.onBeforeLoad, this);
4702         this.store.on('load', this.onLoad, this);
4703         this.store.on('loadexception', this.onLoadException, this);
4704         this.removeMask = false;
4705     }else{
4706         var um = this.el.getUpdateManager();
4707         um.showLoadIndicator = false; // disable the default indicator
4708         um.on('beforeupdate', this.onBeforeLoad, this);
4709         um.on('update', this.onLoad, this);
4710         um.on('failure', this.onLoad, this);
4711         this.removeMask = true;
4712     }
4713 };
4714
4715 Roo.LoadMask.prototype = {
4716     /**
4717      * @cfg {Boolean} removeMask
4718      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4719      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
4720      */
4721     /**
4722      * @cfg {String} msg
4723      * The text to display in a centered loading message box (defaults to 'Loading...')
4724      */
4725     msg : 'Loading...',
4726     /**
4727      * @cfg {String} msgCls
4728      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4729      */
4730     msgCls : 'x-mask-loading',
4731
4732     /**
4733      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4734      * @type Boolean
4735      */
4736     disabled: false,
4737
4738     /**
4739      * Disables the mask to prevent it from being displayed
4740      */
4741     disable : function(){
4742        this.disabled = true;
4743     },
4744
4745     /**
4746      * Enables the mask so that it can be displayed
4747      */
4748     enable : function(){
4749         this.disabled = false;
4750     },
4751     
4752     onLoadException : function()
4753     {
4754         Roo.log(arguments);
4755         
4756         if (typeof(arguments[3]) != 'undefined') {
4757             Roo.MessageBox.alert("Error loading",arguments[3]);
4758         } 
4759         /*
4760         try {
4761             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
4762                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
4763             }   
4764         } catch(e) {
4765             
4766         }
4767         */
4768     
4769         
4770         
4771         this.el.unmask(this.removeMask);
4772     },
4773     // private
4774     onLoad : function()
4775     {
4776         this.el.unmask(this.removeMask);
4777     },
4778
4779     // private
4780     onBeforeLoad : function(){
4781         if(!this.disabled){
4782             this.el.mask(this.msg, this.msgCls);
4783         }
4784     },
4785
4786     // private
4787     destroy : function(){
4788         if(this.store){
4789             this.store.un('beforeload', this.onBeforeLoad, this);
4790             this.store.un('load', this.onLoad, this);
4791             this.store.un('loadexception', this.onLoadException, this);
4792         }else{
4793             var um = this.el.getUpdateManager();
4794             um.un('beforeupdate', this.onBeforeLoad, this);
4795             um.un('update', this.onLoad, this);
4796             um.un('failure', this.onLoad, this);
4797         }
4798     }
4799 };/*
4800  * - LGPL
4801  *
4802  * table
4803  * 
4804  */
4805
4806 /**
4807  * @class Roo.bootstrap.Table
4808  * @extends Roo.bootstrap.Component
4809  * Bootstrap Table class
4810  * @cfg {String} cls table class
4811  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4812  * @cfg {String} bgcolor Specifies the background color for a table
4813  * @cfg {Number} border Specifies whether the table cells should have borders or not
4814  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4815  * @cfg {Number} cellspacing Specifies the space between cells
4816  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4817  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4818  * @cfg {String} sortable Specifies that the table should be sortable
4819  * @cfg {String} summary Specifies a summary of the content of a table
4820  * @cfg {Number} width Specifies the width of a table
4821  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
4822  * 
4823  * @cfg {boolean} striped Should the rows be alternative striped
4824  * @cfg {boolean} bordered Add borders to the table
4825  * @cfg {boolean} hover Add hover highlighting
4826  * @cfg {boolean} condensed Format condensed
4827  * @cfg {boolean} responsive Format condensed
4828  * @cfg {Boolean} loadMask (true|false) default false
4829  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
4830  * @cfg {Boolean} thead (true|false) generate thead, default true
4831  * @cfg {Boolean} RowSelection (true|false) default false
4832  * @cfg {Boolean} CellSelection (true|false) default false
4833  *
4834  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
4835  
4836  * 
4837  * @constructor
4838  * Create a new Table
4839  * @param {Object} config The config object
4840  */
4841
4842 Roo.bootstrap.Table = function(config){
4843     Roo.bootstrap.Table.superclass.constructor.call(this, config);
4844     
4845     if (this.sm) {
4846         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4847         this.sm = this.selModel;
4848         this.sm.xmodule = this.xmodule || false;
4849     }
4850     if (this.cm && typeof(this.cm.config) == 'undefined') {
4851         this.colModel = new Roo.grid.ColumnModel(this.cm);
4852         this.cm = this.colModel;
4853         this.cm.xmodule = this.xmodule || false;
4854     }
4855     if (this.store) {
4856         this.store= Roo.factory(this.store, Roo.data);
4857         this.ds = this.store;
4858         this.ds.xmodule = this.xmodule || false;
4859          
4860     }
4861     if (this.footer && this.store) {
4862         this.footer.dataSource = this.ds;
4863         this.footer = Roo.factory(this.footer);
4864     }
4865     
4866     /** @private */
4867     this.addEvents({
4868         /**
4869          * @event cellclick
4870          * Fires when a cell is clicked
4871          * @param {Roo.bootstrap.Table} this
4872          * @param {Roo.Element} el
4873          * @param {Number} rowIndex
4874          * @param {Number} columnIndex
4875          * @param {Roo.EventObject} e
4876          */
4877         "cellclick" : true,
4878         /**
4879          * @event celldblclick
4880          * Fires when a cell is double clicked
4881          * @param {Roo.bootstrap.Table} this
4882          * @param {Roo.Element} el
4883          * @param {Number} rowIndex
4884          * @param {Number} columnIndex
4885          * @param {Roo.EventObject} e
4886          */
4887         "celldblclick" : true,
4888         /**
4889          * @event rowclick
4890          * Fires when a row is clicked
4891          * @param {Roo.bootstrap.Table} this
4892          * @param {Roo.Element} el
4893          * @param {Number} rowIndex
4894          * @param {Roo.EventObject} e
4895          */
4896         "rowclick" : true,
4897         /**
4898          * @event rowdblclick
4899          * Fires when a row is double clicked
4900          * @param {Roo.bootstrap.Table} this
4901          * @param {Roo.Element} el
4902          * @param {Number} rowIndex
4903          * @param {Roo.EventObject} e
4904          */
4905         "rowdblclick" : true,
4906         /**
4907          * @event mouseover
4908          * Fires when a mouseover occur
4909          * @param {Roo.bootstrap.Table} this
4910          * @param {Roo.Element} el
4911          * @param {Number} rowIndex
4912          * @param {Number} columnIndex
4913          * @param {Roo.EventObject} e
4914          */
4915         "mouseover" : true,
4916         /**
4917          * @event mouseout
4918          * Fires when a mouseout occur
4919          * @param {Roo.bootstrap.Table} this
4920          * @param {Roo.Element} el
4921          * @param {Number} rowIndex
4922          * @param {Number} columnIndex
4923          * @param {Roo.EventObject} e
4924          */
4925         "mouseout" : true,
4926         /**
4927          * @event rowclass
4928          * Fires when a row is rendered, so you can change add a style to it.
4929          * @param {Roo.bootstrap.Table} this
4930          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
4931          */
4932         'rowclass' : true
4933         
4934     });
4935 };
4936
4937 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
4938     
4939     cls: false,
4940     align: false,
4941     bgcolor: false,
4942     border: false,
4943     cellpadding: false,
4944     cellspacing: false,
4945     frame: false,
4946     rules: false,
4947     sortable: false,
4948     summary: false,
4949     width: false,
4950     striped : false,
4951     bordered: false,
4952     hover:  false,
4953     condensed : false,
4954     responsive : false,
4955     sm : false,
4956     cm : false,
4957     store : false,
4958     loadMask : false,
4959     tfoot : true,
4960     thead : true,
4961     RowSelection : false,
4962     CellSelection : false,
4963     layout : false,
4964     
4965     // Roo.Element - the tbody
4966     mainBody: false, 
4967     
4968     getAutoCreate : function(){
4969         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
4970         
4971         cfg = {
4972             tag: 'table',
4973             cls : 'table',
4974             cn : []
4975         }
4976             
4977         if (this.striped) {
4978             cfg.cls += ' table-striped';
4979         }
4980         
4981         if (this.hover) {
4982             cfg.cls += ' table-hover';
4983         }
4984         if (this.bordered) {
4985             cfg.cls += ' table-bordered';
4986         }
4987         if (this.condensed) {
4988             cfg.cls += ' table-condensed';
4989         }
4990         if (this.responsive) {
4991             cfg.cls += ' table-responsive';
4992         }
4993         
4994         if (this.cls) {
4995             cfg.cls+=  ' ' +this.cls;
4996         }
4997         
4998         // this lot should be simplifed...
4999         
5000         if (this.align) {
5001             cfg.align=this.align;
5002         }
5003         if (this.bgcolor) {
5004             cfg.bgcolor=this.bgcolor;
5005         }
5006         if (this.border) {
5007             cfg.border=this.border;
5008         }
5009         if (this.cellpadding) {
5010             cfg.cellpadding=this.cellpadding;
5011         }
5012         if (this.cellspacing) {
5013             cfg.cellspacing=this.cellspacing;
5014         }
5015         if (this.frame) {
5016             cfg.frame=this.frame;
5017         }
5018         if (this.rules) {
5019             cfg.rules=this.rules;
5020         }
5021         if (this.sortable) {
5022             cfg.sortable=this.sortable;
5023         }
5024         if (this.summary) {
5025             cfg.summary=this.summary;
5026         }
5027         if (this.width) {
5028             cfg.width=this.width;
5029         }
5030         if (this.layout) {
5031             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5032         }
5033         
5034         if(this.store || this.cm){
5035             if(this.thead){
5036                 cfg.cn.push(this.renderHeader());
5037             }
5038             
5039             cfg.cn.push(this.renderBody());
5040             
5041             if(this.tfoot){
5042                 cfg.cn.push(this.renderFooter());
5043             }
5044             
5045             cfg.cls+=  ' TableGrid';
5046         }
5047         
5048         return { cn : [ cfg ] };
5049     },
5050     
5051     initEvents : function()
5052     {   
5053         if(!this.store || !this.cm){
5054             return;
5055         }
5056         
5057         //Roo.log('initEvents with ds!!!!');
5058         
5059         this.mainBody = this.el.select('tbody', true).first();
5060         
5061         
5062         var _this = this;
5063         
5064         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5065             e.on('click', _this.sort, _this);
5066         });
5067         
5068         this.el.on("click", this.onClick, this);
5069         this.el.on("dblclick", this.onDblClick, this);
5070         
5071         this.parent().el.setStyle('position', 'relative');
5072         if (this.footer) {
5073             this.footer.parentId = this.id;
5074             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5075         }
5076         
5077         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5078         
5079         this.store.on('load', this.onLoad, this);
5080         this.store.on('beforeload', this.onBeforeLoad, this);
5081         this.store.on('update', this.onUpdate, this);
5082         
5083     },
5084     
5085     onMouseover : function(e, el)
5086     {
5087         var cell = Roo.get(el);
5088         
5089         if(!cell){
5090             return;
5091         }
5092         
5093         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5094             cell = cell.findParent('td', false, true);
5095         }
5096         
5097         var row = cell.findParent('tr', false, true);
5098         var cellIndex = cell.dom.cellIndex;
5099         var rowIndex = row.dom.rowIndex - 1; // start from 0
5100         
5101         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5102         
5103     },
5104     
5105     onMouseout : function(e, el)
5106     {
5107         var cell = Roo.get(el);
5108         
5109         if(!cell){
5110             return;
5111         }
5112         
5113         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5114             cell = cell.findParent('td', false, true);
5115         }
5116         
5117         var row = cell.findParent('tr', false, true);
5118         var cellIndex = cell.dom.cellIndex;
5119         var rowIndex = row.dom.rowIndex - 1; // start from 0
5120         
5121         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5122         
5123     },
5124     
5125     onClick : function(e, el)
5126     {
5127         var cell = Roo.get(el);
5128         
5129         if(!cell || (!this.CellSelection && !this.RowSelection)){
5130             return;
5131         }
5132         
5133         
5134         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5135             cell = cell.findParent('td', false, true);
5136         }
5137         
5138         var row = cell.findParent('tr', false, true);
5139         var cellIndex = cell.dom.cellIndex;
5140         var rowIndex = row.dom.rowIndex - 1;
5141         
5142         if(this.CellSelection){
5143             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5144         }
5145         
5146         if(this.RowSelection){
5147             this.fireEvent('rowclick', this, row, rowIndex, e);
5148         }
5149         
5150         
5151     },
5152     
5153     onDblClick : function(e,el)
5154     {
5155         var cell = Roo.get(el);
5156         
5157         if(!cell || (!this.CellSelection && !this.RowSelection)){
5158             return;
5159         }
5160         
5161         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5162             cell = cell.findParent('td', false, true);
5163         }
5164         
5165         var row = cell.findParent('tr', false, true);
5166         var cellIndex = cell.dom.cellIndex;
5167         var rowIndex = row.dom.rowIndex - 1;
5168         
5169         if(this.CellSelection){
5170             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5171         }
5172         
5173         if(this.RowSelection){
5174             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5175         }
5176     },
5177     
5178     sort : function(e,el)
5179     {
5180         var col = Roo.get(el)
5181         
5182         if(!col.hasClass('sortable')){
5183             return;
5184         }
5185         
5186         var sort = col.attr('sort');
5187         var dir = 'ASC';
5188         
5189         if(col.hasClass('glyphicon-arrow-up')){
5190             dir = 'DESC';
5191         }
5192         
5193         this.store.sortInfo = {field : sort, direction : dir};
5194         
5195         if (this.footer) {
5196             Roo.log("calling footer first");
5197             this.footer.onClick('first');
5198         } else {
5199         
5200             this.store.load({ params : { start : 0 } });
5201         }
5202     },
5203     
5204     renderHeader : function()
5205     {
5206         var header = {
5207             tag: 'thead',
5208             cn : []
5209         };
5210         
5211         var cm = this.cm;
5212         
5213         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5214             
5215             var config = cm.config[i];
5216                     
5217             var c = {
5218                 tag: 'th',
5219                 style : '',
5220                 html: cm.getColumnHeader(i)
5221             };
5222             
5223             if(typeof(config.hidden) != 'undefined' && config.hidden){
5224                 c.style += ' display:none;';
5225             }
5226             
5227             if(typeof(config.dataIndex) != 'undefined'){
5228                 c.sort = config.dataIndex;
5229             }
5230             
5231             if(typeof(config.sortable) != 'undefined' && config.sortable){
5232                 c.cls = 'sortable';
5233             }
5234             
5235             if(typeof(config.align) != 'undefined' && config.align.length){
5236                 c.style += ' text-align:' + config.align + ';';
5237             }
5238             
5239             if(typeof(config.width) != 'undefined'){
5240                 c.style += ' width:' + config.width + 'px;';
5241             }
5242             
5243             header.cn.push(c)
5244         }
5245         
5246         return header;
5247     },
5248     
5249     renderBody : function()
5250     {
5251         var body = {
5252             tag: 'tbody',
5253             cn : [
5254                 {
5255                     tag: 'tr',
5256                     cn : [
5257                         {
5258                             tag : 'td',
5259                             colspan :  this.cm.getColumnCount()
5260                         }
5261                     ]
5262                 }
5263             ]
5264         };
5265         
5266         return body;
5267     },
5268     
5269     renderFooter : function()
5270     {
5271         var footer = {
5272             tag: 'tfoot',
5273             cn : [
5274                 {
5275                     tag: 'tr',
5276                     cn : [
5277                         {
5278                             tag : 'td',
5279                             colspan :  this.cm.getColumnCount()
5280                         }
5281                     ]
5282                 }
5283             ]
5284         };
5285         
5286         return footer;
5287     },
5288     
5289     
5290     
5291     onLoad : function()
5292     {
5293         Roo.log('ds onload');
5294         this.clear();
5295         
5296         var _this = this;
5297         var cm = this.cm;
5298         var ds = this.store;
5299         
5300         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5301             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5302             
5303             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5304                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5305             }
5306             
5307             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5308                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5309             }
5310         });
5311         
5312         var tbody =  this.mainBody;
5313               
5314         if(ds.getCount() > 0){
5315             ds.data.each(function(d,rowIndex){
5316                 var row =  this.renderRow(cm, ds, rowIndex);
5317                 
5318                 tbody.createChild(row);
5319                 
5320                 var _this = this;
5321                 
5322                 if(row.cellObjects.length){
5323                     Roo.each(row.cellObjects, function(r){
5324                         _this.renderCellObject(r);
5325                     })
5326                 }
5327                 
5328             }, this);
5329         }
5330         
5331         Roo.each(this.el.select('tbody td', true).elements, function(e){
5332             e.on('mouseover', _this.onMouseover, _this);
5333         });
5334         
5335         Roo.each(this.el.select('tbody td', true).elements, function(e){
5336             e.on('mouseout', _this.onMouseout, _this);
5337         });
5338
5339         //if(this.loadMask){
5340         //    this.maskEl.hide();
5341         //}
5342     },
5343     
5344     
5345     onUpdate : function(ds,record)
5346     {
5347         this.refreshRow(record);
5348     },
5349     onRemove : function(ds, record, index, isUpdate){
5350         if(isUpdate !== true){
5351             this.fireEvent("beforerowremoved", this, index, record);
5352         }
5353         var bt = this.mainBody.dom;
5354         if(bt.rows[index]){
5355             bt.removeChild(bt.rows[index]);
5356         }
5357         
5358         if(isUpdate !== true){
5359             //this.stripeRows(index);
5360             //this.syncRowHeights(index, index);
5361             //this.layout();
5362             this.fireEvent("rowremoved", this, index, record);
5363         }
5364     },
5365     
5366     
5367     refreshRow : function(record){
5368         var ds = this.store, index;
5369         if(typeof record == 'number'){
5370             index = record;
5371             record = ds.getAt(index);
5372         }else{
5373             index = ds.indexOf(record);
5374         }
5375         this.insertRow(ds, index, true);
5376         this.onRemove(ds, record, index+1, true);
5377         //this.syncRowHeights(index, index);
5378         //this.layout();
5379         this.fireEvent("rowupdated", this, index, record);
5380     },
5381     
5382     insertRow : function(dm, rowIndex, isUpdate){
5383         
5384         if(!isUpdate){
5385             this.fireEvent("beforerowsinserted", this, rowIndex);
5386         }
5387             //var s = this.getScrollState();
5388         var row = this.renderRow(this.cm, this.store, rowIndex);
5389         // insert before rowIndex..
5390         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5391         
5392         var _this = this;
5393                 
5394         if(row.cellObjects.length){
5395             Roo.each(row.cellObjects, function(r){
5396                 _this.renderCellObject(r);
5397             })
5398         }
5399             
5400         if(!isUpdate){
5401             this.fireEvent("rowsinserted", this, rowIndex);
5402             //this.syncRowHeights(firstRow, lastRow);
5403             //this.stripeRows(firstRow);
5404             //this.layout();
5405         }
5406         
5407     },
5408     
5409     
5410     getRowDom : function(rowIndex)
5411     {
5412         // not sure if I need to check this.. but let's do it anyway..
5413         return (this.mainBody.dom.rows && (rowIndex-1) < this.mainBody.dom.rows.length ) ?
5414                 this.mainBody.dom.rows[rowIndex] : false
5415     },
5416     // returns the object tree for a tr..
5417   
5418     
5419     renderRow : function(cm, ds, rowIndex) {
5420         
5421         var d = ds.getAt(rowIndex);
5422         
5423         var row = {
5424             tag : 'tr',
5425             cn : []
5426         };
5427             
5428         var cellObjects = [];
5429         
5430         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5431             var config = cm.config[i];
5432             
5433             var renderer = cm.getRenderer(i);
5434             var value = '';
5435             var id = false;
5436             
5437             if(typeof(renderer) !== 'undefined'){
5438                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5439             }
5440             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5441             // and are rendered into the cells after the row is rendered - using the id for the element.
5442             
5443             if(typeof(value) === 'object'){
5444                 id = Roo.id();
5445                 cellObjects.push({
5446                     container : id,
5447                     cfg : value 
5448                 })
5449             }
5450             
5451             var rowcfg = {
5452                 record: d,
5453                 rowIndex : rowIndex,
5454                 colIndex : i,
5455                 rowClass : ''
5456             }
5457
5458             this.fireEvent('rowclass', this, rowcfg);
5459             
5460             var td = {
5461                 tag: 'td',
5462                 cls : rowcfg.rowClass,
5463                 style: '',
5464                 html: (typeof(value) === 'object') ? '' : value
5465             };
5466             
5467             if (id) {
5468                 td.id = id;
5469             }
5470             
5471             if(typeof(config.hidden) != 'undefined' && config.hidden){
5472                 td.style += ' display:none;';
5473             }
5474             
5475             if(typeof(config.align) != 'undefined' && config.align.length){
5476                 td.style += ' text-align:' + config.align + ';';
5477             }
5478             
5479             if(typeof(config.width) != 'undefined'){
5480                 td.style += ' width:' +  config.width + 'px;';
5481             }
5482              
5483             row.cn.push(td);
5484            
5485         }
5486         
5487         row.cellObjects = cellObjects;
5488         
5489         return row;
5490           
5491     },
5492     
5493     
5494     
5495     onBeforeLoad : function()
5496     {
5497         //Roo.log('ds onBeforeLoad');
5498         
5499         //this.clear();
5500         
5501         //if(this.loadMask){
5502         //    this.maskEl.show();
5503         //}
5504     },
5505     
5506     clear : function()
5507     {
5508         this.el.select('tbody', true).first().dom.innerHTML = '';
5509     },
5510     
5511     getSelectionModel : function(){
5512         if(!this.selModel){
5513             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5514         }
5515         return this.selModel;
5516     },
5517     /*
5518      * Render the Roo.bootstrap object from renderder
5519      */
5520     renderCellObject : function(r)
5521     {
5522         var _this = this;
5523         
5524         var t = r.cfg.render(r.container);
5525         
5526         if(r.cfg.cn){
5527             Roo.each(r.cfg.cn, function(c){
5528                 var child = {
5529                     container: t.getChildContainer(),
5530                     cfg: c
5531                 }
5532                 _this.renderCellObject(child);
5533             })
5534         }
5535     }
5536    
5537 });
5538
5539  
5540
5541  /*
5542  * - LGPL
5543  *
5544  * table cell
5545  * 
5546  */
5547
5548 /**
5549  * @class Roo.bootstrap.TableCell
5550  * @extends Roo.bootstrap.Component
5551  * Bootstrap TableCell class
5552  * @cfg {String} html cell contain text
5553  * @cfg {String} cls cell class
5554  * @cfg {String} tag cell tag (td|th) default td
5555  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5556  * @cfg {String} align Aligns the content in a cell
5557  * @cfg {String} axis Categorizes cells
5558  * @cfg {String} bgcolor Specifies the background color of a cell
5559  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5560  * @cfg {Number} colspan Specifies the number of columns a cell should span
5561  * @cfg {String} headers Specifies one or more header cells a cell is related to
5562  * @cfg {Number} height Sets the height of a cell
5563  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5564  * @cfg {Number} rowspan Sets the number of rows a cell should span
5565  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5566  * @cfg {String} valign Vertical aligns the content in a cell
5567  * @cfg {Number} width Specifies the width of a cell
5568  * 
5569  * @constructor
5570  * Create a new TableCell
5571  * @param {Object} config The config object
5572  */
5573
5574 Roo.bootstrap.TableCell = function(config){
5575     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5576 };
5577
5578 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
5579     
5580     html: false,
5581     cls: false,
5582     tag: false,
5583     abbr: false,
5584     align: false,
5585     axis: false,
5586     bgcolor: false,
5587     charoff: false,
5588     colspan: false,
5589     headers: false,
5590     height: false,
5591     nowrap: false,
5592     rowspan: false,
5593     scope: false,
5594     valign: false,
5595     width: false,
5596     
5597     
5598     getAutoCreate : function(){
5599         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5600         
5601         cfg = {
5602             tag: 'td'
5603         }
5604         
5605         if(this.tag){
5606             cfg.tag = this.tag;
5607         }
5608         
5609         if (this.html) {
5610             cfg.html=this.html
5611         }
5612         if (this.cls) {
5613             cfg.cls=this.cls
5614         }
5615         if (this.abbr) {
5616             cfg.abbr=this.abbr
5617         }
5618         if (this.align) {
5619             cfg.align=this.align
5620         }
5621         if (this.axis) {
5622             cfg.axis=this.axis
5623         }
5624         if (this.bgcolor) {
5625             cfg.bgcolor=this.bgcolor
5626         }
5627         if (this.charoff) {
5628             cfg.charoff=this.charoff
5629         }
5630         if (this.colspan) {
5631             cfg.colspan=this.colspan
5632         }
5633         if (this.headers) {
5634             cfg.headers=this.headers
5635         }
5636         if (this.height) {
5637             cfg.height=this.height
5638         }
5639         if (this.nowrap) {
5640             cfg.nowrap=this.nowrap
5641         }
5642         if (this.rowspan) {
5643             cfg.rowspan=this.rowspan
5644         }
5645         if (this.scope) {
5646             cfg.scope=this.scope
5647         }
5648         if (this.valign) {
5649             cfg.valign=this.valign
5650         }
5651         if (this.width) {
5652             cfg.width=this.width
5653         }
5654         
5655         
5656         return cfg;
5657     }
5658    
5659 });
5660
5661  
5662
5663  /*
5664  * - LGPL
5665  *
5666  * table row
5667  * 
5668  */
5669
5670 /**
5671  * @class Roo.bootstrap.TableRow
5672  * @extends Roo.bootstrap.Component
5673  * Bootstrap TableRow class
5674  * @cfg {String} cls row class
5675  * @cfg {String} align Aligns the content in a table row
5676  * @cfg {String} bgcolor Specifies a background color for a table row
5677  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5678  * @cfg {String} valign Vertical aligns the content in a table row
5679  * 
5680  * @constructor
5681  * Create a new TableRow
5682  * @param {Object} config The config object
5683  */
5684
5685 Roo.bootstrap.TableRow = function(config){
5686     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5687 };
5688
5689 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
5690     
5691     cls: false,
5692     align: false,
5693     bgcolor: false,
5694     charoff: false,
5695     valign: false,
5696     
5697     getAutoCreate : function(){
5698         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
5699         
5700         cfg = {
5701             tag: 'tr'
5702         }
5703             
5704         if(this.cls){
5705             cfg.cls = this.cls;
5706         }
5707         if(this.align){
5708             cfg.align = this.align;
5709         }
5710         if(this.bgcolor){
5711             cfg.bgcolor = this.bgcolor;
5712         }
5713         if(this.charoff){
5714             cfg.charoff = this.charoff;
5715         }
5716         if(this.valign){
5717             cfg.valign = this.valign;
5718         }
5719         
5720         return cfg;
5721     }
5722    
5723 });
5724
5725  
5726
5727  /*
5728  * - LGPL
5729  *
5730  * table body
5731  * 
5732  */
5733
5734 /**
5735  * @class Roo.bootstrap.TableBody
5736  * @extends Roo.bootstrap.Component
5737  * Bootstrap TableBody class
5738  * @cfg {String} cls element class
5739  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
5740  * @cfg {String} align Aligns the content inside the element
5741  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
5742  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
5743  * 
5744  * @constructor
5745  * Create a new TableBody
5746  * @param {Object} config The config object
5747  */
5748
5749 Roo.bootstrap.TableBody = function(config){
5750     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
5751 };
5752
5753 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
5754     
5755     cls: false,
5756     tag: false,
5757     align: false,
5758     charoff: false,
5759     valign: false,
5760     
5761     getAutoCreate : function(){
5762         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
5763         
5764         cfg = {
5765             tag: 'tbody'
5766         }
5767             
5768         if (this.cls) {
5769             cfg.cls=this.cls
5770         }
5771         if(this.tag){
5772             cfg.tag = this.tag;
5773         }
5774         
5775         if(this.align){
5776             cfg.align = this.align;
5777         }
5778         if(this.charoff){
5779             cfg.charoff = this.charoff;
5780         }
5781         if(this.valign){
5782             cfg.valign = this.valign;
5783         }
5784         
5785         return cfg;
5786     }
5787     
5788     
5789 //    initEvents : function()
5790 //    {
5791 //        
5792 //        if(!this.store){
5793 //            return;
5794 //        }
5795 //        
5796 //        this.store = Roo.factory(this.store, Roo.data);
5797 //        this.store.on('load', this.onLoad, this);
5798 //        
5799 //        this.store.load();
5800 //        
5801 //    },
5802 //    
5803 //    onLoad: function () 
5804 //    {   
5805 //        this.fireEvent('load', this);
5806 //    }
5807 //    
5808 //   
5809 });
5810
5811  
5812
5813  /*
5814  * Based on:
5815  * Ext JS Library 1.1.1
5816  * Copyright(c) 2006-2007, Ext JS, LLC.
5817  *
5818  * Originally Released Under LGPL - original licence link has changed is not relivant.
5819  *
5820  * Fork - LGPL
5821  * <script type="text/javascript">
5822  */
5823
5824 // as we use this in bootstrap.
5825 Roo.namespace('Roo.form');
5826  /**
5827  * @class Roo.form.Action
5828  * Internal Class used to handle form actions
5829  * @constructor
5830  * @param {Roo.form.BasicForm} el The form element or its id
5831  * @param {Object} config Configuration options
5832  */
5833
5834  
5835  
5836 // define the action interface
5837 Roo.form.Action = function(form, options){
5838     this.form = form;
5839     this.options = options || {};
5840 };
5841 /**
5842  * Client Validation Failed
5843  * @const 
5844  */
5845 Roo.form.Action.CLIENT_INVALID = 'client';
5846 /**
5847  * Server Validation Failed
5848  * @const 
5849  */
5850 Roo.form.Action.SERVER_INVALID = 'server';
5851  /**
5852  * Connect to Server Failed
5853  * @const 
5854  */
5855 Roo.form.Action.CONNECT_FAILURE = 'connect';
5856 /**
5857  * Reading Data from Server Failed
5858  * @const 
5859  */
5860 Roo.form.Action.LOAD_FAILURE = 'load';
5861
5862 Roo.form.Action.prototype = {
5863     type : 'default',
5864     failureType : undefined,
5865     response : undefined,
5866     result : undefined,
5867
5868     // interface method
5869     run : function(options){
5870
5871     },
5872
5873     // interface method
5874     success : function(response){
5875
5876     },
5877
5878     // interface method
5879     handleResponse : function(response){
5880
5881     },
5882
5883     // default connection failure
5884     failure : function(response){
5885         
5886         this.response = response;
5887         this.failureType = Roo.form.Action.CONNECT_FAILURE;
5888         this.form.afterAction(this, false);
5889     },
5890
5891     processResponse : function(response){
5892         this.response = response;
5893         if(!response.responseText){
5894             return true;
5895         }
5896         this.result = this.handleResponse(response);
5897         return this.result;
5898     },
5899
5900     // utility functions used internally
5901     getUrl : function(appendParams){
5902         var url = this.options.url || this.form.url || this.form.el.dom.action;
5903         if(appendParams){
5904             var p = this.getParams();
5905             if(p){
5906                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
5907             }
5908         }
5909         return url;
5910     },
5911
5912     getMethod : function(){
5913         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
5914     },
5915
5916     getParams : function(){
5917         var bp = this.form.baseParams;
5918         var p = this.options.params;
5919         if(p){
5920             if(typeof p == "object"){
5921                 p = Roo.urlEncode(Roo.applyIf(p, bp));
5922             }else if(typeof p == 'string' && bp){
5923                 p += '&' + Roo.urlEncode(bp);
5924             }
5925         }else if(bp){
5926             p = Roo.urlEncode(bp);
5927         }
5928         return p;
5929     },
5930
5931     createCallback : function(){
5932         return {
5933             success: this.success,
5934             failure: this.failure,
5935             scope: this,
5936             timeout: (this.form.timeout*1000),
5937             upload: this.form.fileUpload ? this.success : undefined
5938         };
5939     }
5940 };
5941
5942 Roo.form.Action.Submit = function(form, options){
5943     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
5944 };
5945
5946 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
5947     type : 'submit',
5948
5949     haveProgress : false,
5950     uploadComplete : false,
5951     
5952     // uploadProgress indicator.
5953     uploadProgress : function()
5954     {
5955         if (!this.form.progressUrl) {
5956             return;
5957         }
5958         
5959         if (!this.haveProgress) {
5960             Roo.MessageBox.progress("Uploading", "Uploading");
5961         }
5962         if (this.uploadComplete) {
5963            Roo.MessageBox.hide();
5964            return;
5965         }
5966         
5967         this.haveProgress = true;
5968    
5969         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
5970         
5971         var c = new Roo.data.Connection();
5972         c.request({
5973             url : this.form.progressUrl,
5974             params: {
5975                 id : uid
5976             },
5977             method: 'GET',
5978             success : function(req){
5979                //console.log(data);
5980                 var rdata = false;
5981                 var edata;
5982                 try  {
5983                    rdata = Roo.decode(req.responseText)
5984                 } catch (e) {
5985                     Roo.log("Invalid data from server..");
5986                     Roo.log(edata);
5987                     return;
5988                 }
5989                 if (!rdata || !rdata.success) {
5990                     Roo.log(rdata);
5991                     Roo.MessageBox.alert(Roo.encode(rdata));
5992                     return;
5993                 }
5994                 var data = rdata.data;
5995                 
5996                 if (this.uploadComplete) {
5997                    Roo.MessageBox.hide();
5998                    return;
5999                 }
6000                    
6001                 if (data){
6002                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6003                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6004                     );
6005                 }
6006                 this.uploadProgress.defer(2000,this);
6007             },
6008        
6009             failure: function(data) {
6010                 Roo.log('progress url failed ');
6011                 Roo.log(data);
6012             },
6013             scope : this
6014         });
6015            
6016     },
6017     
6018     
6019     run : function()
6020     {
6021         // run get Values on the form, so it syncs any secondary forms.
6022         this.form.getValues();
6023         
6024         var o = this.options;
6025         var method = this.getMethod();
6026         var isPost = method == 'POST';
6027         if(o.clientValidation === false || this.form.isValid()){
6028             
6029             if (this.form.progressUrl) {
6030                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6031                     (new Date() * 1) + '' + Math.random());
6032                     
6033             } 
6034             
6035             
6036             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6037                 form:this.form.el.dom,
6038                 url:this.getUrl(!isPost),
6039                 method: method,
6040                 params:isPost ? this.getParams() : null,
6041                 isUpload: this.form.fileUpload
6042             }));
6043             
6044             this.uploadProgress();
6045
6046         }else if (o.clientValidation !== false){ // client validation failed
6047             this.failureType = Roo.form.Action.CLIENT_INVALID;
6048             this.form.afterAction(this, false);
6049         }
6050     },
6051
6052     success : function(response)
6053     {
6054         this.uploadComplete= true;
6055         if (this.haveProgress) {
6056             Roo.MessageBox.hide();
6057         }
6058         
6059         
6060         var result = this.processResponse(response);
6061         if(result === true || result.success){
6062             this.form.afterAction(this, true);
6063             return;
6064         }
6065         if(result.errors){
6066             this.form.markInvalid(result.errors);
6067             this.failureType = Roo.form.Action.SERVER_INVALID;
6068         }
6069         this.form.afterAction(this, false);
6070     },
6071     failure : function(response)
6072     {
6073         this.uploadComplete= true;
6074         if (this.haveProgress) {
6075             Roo.MessageBox.hide();
6076         }
6077         
6078         this.response = response;
6079         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6080         this.form.afterAction(this, false);
6081     },
6082     
6083     handleResponse : function(response){
6084         if(this.form.errorReader){
6085             var rs = this.form.errorReader.read(response);
6086             var errors = [];
6087             if(rs.records){
6088                 for(var i = 0, len = rs.records.length; i < len; i++) {
6089                     var r = rs.records[i];
6090                     errors[i] = r.data;
6091                 }
6092             }
6093             if(errors.length < 1){
6094                 errors = null;
6095             }
6096             return {
6097                 success : rs.success,
6098                 errors : errors
6099             };
6100         }
6101         var ret = false;
6102         try {
6103             ret = Roo.decode(response.responseText);
6104         } catch (e) {
6105             ret = {
6106                 success: false,
6107                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6108                 errors : []
6109             };
6110         }
6111         return ret;
6112         
6113     }
6114 });
6115
6116
6117 Roo.form.Action.Load = function(form, options){
6118     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6119     this.reader = this.form.reader;
6120 };
6121
6122 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6123     type : 'load',
6124
6125     run : function(){
6126         
6127         Roo.Ajax.request(Roo.apply(
6128                 this.createCallback(), {
6129                     method:this.getMethod(),
6130                     url:this.getUrl(false),
6131                     params:this.getParams()
6132         }));
6133     },
6134
6135     success : function(response){
6136         
6137         var result = this.processResponse(response);
6138         if(result === true || !result.success || !result.data){
6139             this.failureType = Roo.form.Action.LOAD_FAILURE;
6140             this.form.afterAction(this, false);
6141             return;
6142         }
6143         this.form.clearInvalid();
6144         this.form.setValues(result.data);
6145         this.form.afterAction(this, true);
6146     },
6147
6148     handleResponse : function(response){
6149         if(this.form.reader){
6150             var rs = this.form.reader.read(response);
6151             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6152             return {
6153                 success : rs.success,
6154                 data : data
6155             };
6156         }
6157         return Roo.decode(response.responseText);
6158     }
6159 });
6160
6161 Roo.form.Action.ACTION_TYPES = {
6162     'load' : Roo.form.Action.Load,
6163     'submit' : Roo.form.Action.Submit
6164 };/*
6165  * - LGPL
6166  *
6167  * form
6168  * 
6169  */
6170
6171 /**
6172  * @class Roo.bootstrap.Form
6173  * @extends Roo.bootstrap.Component
6174  * Bootstrap Form class
6175  * @cfg {String} method  GET | POST (default POST)
6176  * @cfg {String} labelAlign top | left (default top)
6177  * @cfg {String} align left  | right - for navbars
6178  * @cfg {Boolean} loadMask load mask when submit (default true)
6179
6180  * 
6181  * @constructor
6182  * Create a new Form
6183  * @param {Object} config The config object
6184  */
6185
6186
6187 Roo.bootstrap.Form = function(config){
6188     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6189     this.addEvents({
6190         /**
6191          * @event clientvalidation
6192          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6193          * @param {Form} this
6194          * @param {Boolean} valid true if the form has passed client-side validation
6195          */
6196         clientvalidation: true,
6197         /**
6198          * @event beforeaction
6199          * Fires before any action is performed. Return false to cancel the action.
6200          * @param {Form} this
6201          * @param {Action} action The action to be performed
6202          */
6203         beforeaction: true,
6204         /**
6205          * @event actionfailed
6206          * Fires when an action fails.
6207          * @param {Form} this
6208          * @param {Action} action The action that failed
6209          */
6210         actionfailed : true,
6211         /**
6212          * @event actioncomplete
6213          * Fires when an action is completed.
6214          * @param {Form} this
6215          * @param {Action} action The action that completed
6216          */
6217         actioncomplete : true
6218     });
6219     
6220 };
6221
6222 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6223       
6224      /**
6225      * @cfg {String} method
6226      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6227      */
6228     method : 'POST',
6229     /**
6230      * @cfg {String} url
6231      * The URL to use for form actions if one isn't supplied in the action options.
6232      */
6233     /**
6234      * @cfg {Boolean} fileUpload
6235      * Set to true if this form is a file upload.
6236      */
6237      
6238     /**
6239      * @cfg {Object} baseParams
6240      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6241      */
6242       
6243     /**
6244      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6245      */
6246     timeout: 30,
6247     /**
6248      * @cfg {Sting} align (left|right) for navbar forms
6249      */
6250     align : 'left',
6251
6252     // private
6253     activeAction : null,
6254  
6255     /**
6256      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6257      * element by passing it or its id or mask the form itself by passing in true.
6258      * @type Mixed
6259      */
6260     waitMsgTarget : false,
6261     
6262     loadMask : true,
6263     
6264     getAutoCreate : function(){
6265         
6266         var cfg = {
6267             tag: 'form',
6268             method : this.method || 'POST',
6269             id : this.id || Roo.id(),
6270             cls : ''
6271         }
6272         if (this.parent().xtype.match(/^Nav/)) {
6273             cfg.cls = 'navbar-form navbar-' + this.align;
6274             
6275         }
6276         
6277         if (this.labelAlign == 'left' ) {
6278             cfg.cls += ' form-horizontal';
6279         }
6280         
6281         
6282         return cfg;
6283     },
6284     initEvents : function()
6285     {
6286         this.el.on('submit', this.onSubmit, this);
6287         // this was added as random key presses on the form where triggering form submit.
6288         this.el.on('keypress', function(e) {
6289             if (e.getCharCode() != 13) {
6290                 return true;
6291             }
6292             // we might need to allow it for textareas.. and some other items.
6293             // check e.getTarget().
6294             
6295             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6296                 return true;
6297             }
6298         
6299             Roo.log("keypress blocked");
6300             
6301             e.preventDefault();
6302             return false;
6303         });
6304         
6305     },
6306     // private
6307     onSubmit : function(e){
6308         e.stopEvent();
6309     },
6310     
6311      /**
6312      * Returns true if client-side validation on the form is successful.
6313      * @return Boolean
6314      */
6315     isValid : function(){
6316         var items = this.getItems();
6317         var valid = true;
6318         items.each(function(f){
6319            if(!f.validate()){
6320                valid = false;
6321                
6322            }
6323         });
6324         return valid;
6325     },
6326     /**
6327      * Returns true if any fields in this form have changed since their original load.
6328      * @return Boolean
6329      */
6330     isDirty : function(){
6331         var dirty = false;
6332         var items = this.getItems();
6333         items.each(function(f){
6334            if(f.isDirty()){
6335                dirty = true;
6336                return false;
6337            }
6338            return true;
6339         });
6340         return dirty;
6341     },
6342      /**
6343      * Performs a predefined action (submit or load) or custom actions you define on this form.
6344      * @param {String} actionName The name of the action type
6345      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6346      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6347      * accept other config options):
6348      * <pre>
6349 Property          Type             Description
6350 ----------------  ---------------  ----------------------------------------------------------------------------------
6351 url               String           The url for the action (defaults to the form's url)
6352 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6353 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6354 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6355                                    validate the form on the client (defaults to false)
6356      * </pre>
6357      * @return {BasicForm} this
6358      */
6359     doAction : function(action, options){
6360         if(typeof action == 'string'){
6361             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6362         }
6363         if(this.fireEvent('beforeaction', this, action) !== false){
6364             this.beforeAction(action);
6365             action.run.defer(100, action);
6366         }
6367         return this;
6368     },
6369     
6370     // private
6371     beforeAction : function(action){
6372         var o = action.options;
6373         
6374         if(this.loadMask){
6375             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6376         }
6377         // not really supported yet.. ??
6378         
6379         //if(this.waitMsgTarget === true){
6380         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6381         //}else if(this.waitMsgTarget){
6382         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6383         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6384         //}else {
6385         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6386        // }
6387          
6388     },
6389
6390     // private
6391     afterAction : function(action, success){
6392         this.activeAction = null;
6393         var o = action.options;
6394         
6395         //if(this.waitMsgTarget === true){
6396             this.el.unmask();
6397         //}else if(this.waitMsgTarget){
6398         //    this.waitMsgTarget.unmask();
6399         //}else{
6400         //    Roo.MessageBox.updateProgress(1);
6401         //    Roo.MessageBox.hide();
6402        // }
6403         // 
6404         if(success){
6405             if(o.reset){
6406                 this.reset();
6407             }
6408             Roo.callback(o.success, o.scope, [this, action]);
6409             this.fireEvent('actioncomplete', this, action);
6410             
6411         }else{
6412             
6413             // failure condition..
6414             // we have a scenario where updates need confirming.
6415             // eg. if a locking scenario exists..
6416             // we look for { errors : { needs_confirm : true }} in the response.
6417             if (
6418                 (typeof(action.result) != 'undefined')  &&
6419                 (typeof(action.result.errors) != 'undefined')  &&
6420                 (typeof(action.result.errors.needs_confirm) != 'undefined')
6421            ){
6422                 var _t = this;
6423                 Roo.log("not supported yet");
6424                  /*
6425                 
6426                 Roo.MessageBox.confirm(
6427                     "Change requires confirmation",
6428                     action.result.errorMsg,
6429                     function(r) {
6430                         if (r != 'yes') {
6431                             return;
6432                         }
6433                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
6434                     }
6435                     
6436                 );
6437                 */
6438                 
6439                 
6440                 return;
6441             }
6442             
6443             Roo.callback(o.failure, o.scope, [this, action]);
6444             // show an error message if no failed handler is set..
6445             if (!this.hasListener('actionfailed')) {
6446                 Roo.log("need to add dialog support");
6447                 /*
6448                 Roo.MessageBox.alert("Error",
6449                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6450                         action.result.errorMsg :
6451                         "Saving Failed, please check your entries or try again"
6452                 );
6453                 */
6454             }
6455             
6456             this.fireEvent('actionfailed', this, action);
6457         }
6458         
6459     },
6460     /**
6461      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6462      * @param {String} id The value to search for
6463      * @return Field
6464      */
6465     findField : function(id){
6466         var items = this.getItems();
6467         var field = items.get(id);
6468         if(!field){
6469              items.each(function(f){
6470                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6471                     field = f;
6472                     return false;
6473                 }
6474                 return true;
6475             });
6476         }
6477         return field || null;
6478     },
6479      /**
6480      * Mark fields in this form invalid in bulk.
6481      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6482      * @return {BasicForm} this
6483      */
6484     markInvalid : function(errors){
6485         if(errors instanceof Array){
6486             for(var i = 0, len = errors.length; i < len; i++){
6487                 var fieldError = errors[i];
6488                 var f = this.findField(fieldError.id);
6489                 if(f){
6490                     f.markInvalid(fieldError.msg);
6491                 }
6492             }
6493         }else{
6494             var field, id;
6495             for(id in errors){
6496                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6497                     field.markInvalid(errors[id]);
6498                 }
6499             }
6500         }
6501         //Roo.each(this.childForms || [], function (f) {
6502         //    f.markInvalid(errors);
6503         //});
6504         
6505         return this;
6506     },
6507
6508     /**
6509      * Set values for fields in this form in bulk.
6510      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6511      * @return {BasicForm} this
6512      */
6513     setValues : function(values){
6514         if(values instanceof Array){ // array of objects
6515             for(var i = 0, len = values.length; i < len; i++){
6516                 var v = values[i];
6517                 var f = this.findField(v.id);
6518                 if(f){
6519                     f.setValue(v.value);
6520                     if(this.trackResetOnLoad){
6521                         f.originalValue = f.getValue();
6522                     }
6523                 }
6524             }
6525         }else{ // object hash
6526             var field, id;
6527             for(id in values){
6528                 if(typeof values[id] != 'function' && (field = this.findField(id))){
6529                     
6530                     if (field.setFromData && 
6531                         field.valueField && 
6532                         field.displayField &&
6533                         // combos' with local stores can 
6534                         // be queried via setValue()
6535                         // to set their value..
6536                         (field.store && !field.store.isLocal)
6537                         ) {
6538                         // it's a combo
6539                         var sd = { };
6540                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6541                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6542                         field.setFromData(sd);
6543                         
6544                     } else {
6545                         field.setValue(values[id]);
6546                     }
6547                     
6548                     
6549                     if(this.trackResetOnLoad){
6550                         field.originalValue = field.getValue();
6551                     }
6552                 }
6553             }
6554         }
6555          
6556         //Roo.each(this.childForms || [], function (f) {
6557         //    f.setValues(values);
6558         //});
6559                 
6560         return this;
6561     },
6562
6563     /**
6564      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6565      * they are returned as an array.
6566      * @param {Boolean} asString
6567      * @return {Object}
6568      */
6569     getValues : function(asString){
6570         //if (this.childForms) {
6571             // copy values from the child forms
6572         //    Roo.each(this.childForms, function (f) {
6573         //        this.setValues(f.getValues());
6574         //    }, this);
6575         //}
6576         
6577         
6578         
6579         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6580         if(asString === true){
6581             return fs;
6582         }
6583         return Roo.urlDecode(fs);
6584     },
6585     
6586     /**
6587      * Returns the fields in this form as an object with key/value pairs. 
6588      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6589      * @return {Object}
6590      */
6591     getFieldValues : function(with_hidden)
6592     {
6593         var items = this.getItems();
6594         var ret = {};
6595         items.each(function(f){
6596             if (!f.getName()) {
6597                 return;
6598             }
6599             var v = f.getValue();
6600             if (f.inputType =='radio') {
6601                 if (typeof(ret[f.getName()]) == 'undefined') {
6602                     ret[f.getName()] = ''; // empty..
6603                 }
6604                 
6605                 if (!f.el.dom.checked) {
6606                     return;
6607                     
6608                 }
6609                 v = f.el.dom.value;
6610                 
6611             }
6612             
6613             // not sure if this supported any more..
6614             if ((typeof(v) == 'object') && f.getRawValue) {
6615                 v = f.getRawValue() ; // dates..
6616             }
6617             // combo boxes where name != hiddenName...
6618             if (f.name != f.getName()) {
6619                 ret[f.name] = f.getRawValue();
6620             }
6621             ret[f.getName()] = v;
6622         });
6623         
6624         return ret;
6625     },
6626
6627     /**
6628      * Clears all invalid messages in this form.
6629      * @return {BasicForm} this
6630      */
6631     clearInvalid : function(){
6632         var items = this.getItems();
6633         
6634         items.each(function(f){
6635            f.clearInvalid();
6636         });
6637         
6638         
6639         
6640         return this;
6641     },
6642
6643     /**
6644      * Resets this form.
6645      * @return {BasicForm} this
6646      */
6647     reset : function(){
6648         var items = this.getItems();
6649         items.each(function(f){
6650             f.reset();
6651         });
6652         
6653         Roo.each(this.childForms || [], function (f) {
6654             f.reset();
6655         });
6656        
6657         
6658         return this;
6659     },
6660     getItems : function()
6661     {
6662         var r=new Roo.util.MixedCollection(false, function(o){
6663             return o.id || (o.id = Roo.id());
6664         });
6665         var iter = function(el) {
6666             if (el.inputEl) {
6667                 r.add(el);
6668             }
6669             if (!el.items) {
6670                 return;
6671             }
6672             Roo.each(el.items,function(e) {
6673                 iter(e);
6674             });
6675             
6676             
6677         };
6678         iter(this);
6679         return r;
6680         
6681         
6682         
6683         
6684     }
6685     
6686 });
6687
6688  
6689 /*
6690  * Based on:
6691  * Ext JS Library 1.1.1
6692  * Copyright(c) 2006-2007, Ext JS, LLC.
6693  *
6694  * Originally Released Under LGPL - original licence link has changed is not relivant.
6695  *
6696  * Fork - LGPL
6697  * <script type="text/javascript">
6698  */
6699 /**
6700  * @class Roo.form.VTypes
6701  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6702  * @singleton
6703  */
6704 Roo.form.VTypes = function(){
6705     // closure these in so they are only created once.
6706     var alpha = /^[a-zA-Z_]+$/;
6707     var alphanum = /^[a-zA-Z0-9_]+$/;
6708     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6709     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6710
6711     // All these messages and functions are configurable
6712     return {
6713         /**
6714          * The function used to validate email addresses
6715          * @param {String} value The email address
6716          */
6717         'email' : function(v){
6718             return email.test(v);
6719         },
6720         /**
6721          * The error text to display when the email validation function returns false
6722          * @type String
6723          */
6724         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
6725         /**
6726          * The keystroke filter mask to be applied on email input
6727          * @type RegExp
6728          */
6729         'emailMask' : /[a-z0-9_\.\-@]/i,
6730
6731         /**
6732          * The function used to validate URLs
6733          * @param {String} value The URL
6734          */
6735         'url' : function(v){
6736             return url.test(v);
6737         },
6738         /**
6739          * The error text to display when the url validation function returns false
6740          * @type String
6741          */
6742         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
6743         
6744         /**
6745          * The function used to validate alpha values
6746          * @param {String} value The value
6747          */
6748         'alpha' : function(v){
6749             return alpha.test(v);
6750         },
6751         /**
6752          * The error text to display when the alpha validation function returns false
6753          * @type String
6754          */
6755         'alphaText' : 'This field should only contain letters and _',
6756         /**
6757          * The keystroke filter mask to be applied on alpha input
6758          * @type RegExp
6759          */
6760         'alphaMask' : /[a-z_]/i,
6761
6762         /**
6763          * The function used to validate alphanumeric values
6764          * @param {String} value The value
6765          */
6766         'alphanum' : function(v){
6767             return alphanum.test(v);
6768         },
6769         /**
6770          * The error text to display when the alphanumeric validation function returns false
6771          * @type String
6772          */
6773         'alphanumText' : 'This field should only contain letters, numbers and _',
6774         /**
6775          * The keystroke filter mask to be applied on alphanumeric input
6776          * @type RegExp
6777          */
6778         'alphanumMask' : /[a-z0-9_]/i
6779     };
6780 }();/*
6781  * - LGPL
6782  *
6783  * Input
6784  * 
6785  */
6786
6787 /**
6788  * @class Roo.bootstrap.Input
6789  * @extends Roo.bootstrap.Component
6790  * Bootstrap Input class
6791  * @cfg {Boolean} disabled is it disabled
6792  * @cfg {String} fieldLabel - the label associated
6793  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
6794  * @cfg {String} name name of the input
6795  * @cfg {string} fieldLabel - the label associated
6796  * @cfg {string}  inputType - input / file submit ...
6797  * @cfg {string} placeholder - placeholder to put in text.
6798  * @cfg {string}  before - input group add on before
6799  * @cfg {string} after - input group add on after
6800  * @cfg {string} size - (lg|sm) or leave empty..
6801  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
6802  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
6803  * @cfg {Number} md colspan out of 12 for computer-sized screens
6804  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
6805  * @cfg {string} value default value of the input
6806  * @cfg {Number} labelWidth set the width of label (0-12)
6807  * @cfg {String} labelAlign (top|left)
6808  * @cfg {Boolean} readOnly Specifies that the field should be read-only
6809  * @cfg {String} align (left|center|right) Default left
6810  * 
6811  * 
6812  * @constructor
6813  * Create a new Input
6814  * @param {Object} config The config object
6815  */
6816
6817 Roo.bootstrap.Input = function(config){
6818     Roo.bootstrap.Input.superclass.constructor.call(this, config);
6819    
6820         this.addEvents({
6821             /**
6822              * @event focus
6823              * Fires when this field receives input focus.
6824              * @param {Roo.form.Field} this
6825              */
6826             focus : true,
6827             /**
6828              * @event blur
6829              * Fires when this field loses input focus.
6830              * @param {Roo.form.Field} this
6831              */
6832             blur : true,
6833             /**
6834              * @event specialkey
6835              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
6836              * {@link Roo.EventObject#getKey} to determine which key was pressed.
6837              * @param {Roo.form.Field} this
6838              * @param {Roo.EventObject} e The event object
6839              */
6840             specialkey : true,
6841             /**
6842              * @event change
6843              * Fires just before the field blurs if the field value has changed.
6844              * @param {Roo.form.Field} this
6845              * @param {Mixed} newValue The new value
6846              * @param {Mixed} oldValue The original value
6847              */
6848             change : true,
6849             /**
6850              * @event invalid
6851              * Fires after the field has been marked as invalid.
6852              * @param {Roo.form.Field} this
6853              * @param {String} msg The validation message
6854              */
6855             invalid : true,
6856             /**
6857              * @event valid
6858              * Fires after the field has been validated with no errors.
6859              * @param {Roo.form.Field} this
6860              */
6861             valid : true,
6862              /**
6863              * @event keyup
6864              * Fires after the key up
6865              * @param {Roo.form.Field} this
6866              * @param {Roo.EventObject}  e The event Object
6867              */
6868             keyup : true
6869         });
6870 };
6871
6872 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
6873      /**
6874      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
6875       automatic validation (defaults to "keyup").
6876      */
6877     validationEvent : "keyup",
6878      /**
6879      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
6880      */
6881     validateOnBlur : true,
6882     /**
6883      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
6884      */
6885     validationDelay : 250,
6886      /**
6887      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
6888      */
6889     focusClass : "x-form-focus",  // not needed???
6890     
6891        
6892     /**
6893      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
6894      */
6895     invalidClass : "has-error",
6896     
6897     /**
6898      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
6899      */
6900     selectOnFocus : false,
6901     
6902      /**
6903      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
6904      */
6905     maskRe : null,
6906        /**
6907      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
6908      */
6909     vtype : null,
6910     
6911       /**
6912      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
6913      */
6914     disableKeyFilter : false,
6915     
6916        /**
6917      * @cfg {Boolean} disabled True to disable the field (defaults to false).
6918      */
6919     disabled : false,
6920      /**
6921      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
6922      */
6923     allowBlank : true,
6924     /**
6925      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
6926      */
6927     blankText : "This field is required",
6928     
6929      /**
6930      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
6931      */
6932     minLength : 0,
6933     /**
6934      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
6935      */
6936     maxLength : Number.MAX_VALUE,
6937     /**
6938      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
6939      */
6940     minLengthText : "The minimum length for this field is {0}",
6941     /**
6942      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
6943      */
6944     maxLengthText : "The maximum length for this field is {0}",
6945   
6946     
6947     /**
6948      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
6949      * If available, this function will be called only after the basic validators all return true, and will be passed the
6950      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
6951      */
6952     validator : null,
6953     /**
6954      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
6955      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
6956      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
6957      */
6958     regex : null,
6959     /**
6960      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
6961      */
6962     regexText : "",
6963     
6964     
6965     
6966     fieldLabel : '',
6967     inputType : 'text',
6968     
6969     name : false,
6970     placeholder: false,
6971     before : false,
6972     after : false,
6973     size : false,
6974     // private
6975     hasFocus : false,
6976     preventMark: false,
6977     isFormField : true,
6978     value : '',
6979     labelWidth : 2,
6980     labelAlign : false,
6981     readOnly : false,
6982     align : false,
6983     formatedValue : false,
6984     
6985     parentLabelAlign : function()
6986     {
6987         var parent = this;
6988         while (parent.parent()) {
6989             parent = parent.parent();
6990             if (typeof(parent.labelAlign) !='undefined') {
6991                 return parent.labelAlign;
6992             }
6993         }
6994         return 'left';
6995         
6996     },
6997     
6998     getAutoCreate : function(){
6999         
7000         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7001         
7002         var id = Roo.id();
7003         
7004         var cfg = {};
7005         
7006         if(this.inputType != 'hidden'){
7007             cfg.cls = 'form-group' //input-group
7008         }
7009         
7010         var input =  {
7011             tag: 'input',
7012             id : id,
7013             type : this.inputType,
7014             value : this.value,
7015             cls : 'form-control',
7016             placeholder : this.placeholder || ''
7017             
7018         };
7019         
7020         if(this.align){
7021             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7022         }
7023         
7024         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7025             input.maxLength = this.maxLength;
7026         }
7027         
7028         if (this.disabled) {
7029             input.disabled=true;
7030         }
7031         
7032         if (this.readOnly) {
7033             input.readonly=true;
7034         }
7035         
7036         if (this.name) {
7037             input.name = this.name;
7038         }
7039         if (this.size) {
7040             input.cls += ' input-' + this.size;
7041         }
7042         var settings=this;
7043         ['xs','sm','md','lg'].map(function(size){
7044             if (settings[size]) {
7045                 cfg.cls += ' col-' + size + '-' + settings[size];
7046             }
7047         });
7048         
7049         var inputblock = input;
7050         
7051         if (this.before || this.after) {
7052             
7053             inputblock = {
7054                 cls : 'input-group',
7055                 cn :  [] 
7056             };
7057             if (this.before && typeof(this.before) == 'string') {
7058                 
7059                 inputblock.cn.push({
7060                     tag :'span',
7061                     cls : 'roo-input-before input-group-addon',
7062                     html : this.before
7063                 });
7064             }
7065             if (this.before && typeof(this.before) == 'object') {
7066                 this.before = Roo.factory(this.before);
7067                 Roo.log(this.before);
7068                 inputblock.cn.push({
7069                     tag :'span',
7070                     cls : 'roo-input-before input-group-' +
7071                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7072                 });
7073             }
7074             
7075             inputblock.cn.push(input);
7076             
7077             if (this.after && typeof(this.after) == 'string') {
7078                 inputblock.cn.push({
7079                     tag :'span',
7080                     cls : 'roo-input-after input-group-addon',
7081                     html : this.after
7082                 });
7083             }
7084             if (this.after && typeof(this.after) == 'object') {
7085                 this.after = Roo.factory(this.after);
7086                 Roo.log(this.after);
7087                 inputblock.cn.push({
7088                     tag :'span',
7089                     cls : 'roo-input-after input-group-' +
7090                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7091                 });
7092             }
7093         };
7094         
7095         if (align ==='left' && this.fieldLabel.length) {
7096                 Roo.log("left and has label");
7097                 cfg.cn = [
7098                     
7099                     {
7100                         tag: 'label',
7101                         'for' :  id,
7102                         cls : 'control-label col-sm-' + this.labelWidth,
7103                         html : this.fieldLabel
7104                         
7105                     },
7106                     {
7107                         cls : "col-sm-" + (12 - this.labelWidth), 
7108                         cn: [
7109                             inputblock
7110                         ]
7111                     }
7112                     
7113                 ];
7114         } else if ( this.fieldLabel.length) {
7115                 Roo.log(" label");
7116                  cfg.cn = [
7117                    
7118                     {
7119                         tag: 'label',
7120                         //cls : 'input-group-addon',
7121                         html : this.fieldLabel
7122                         
7123                     },
7124                     
7125                     inputblock
7126                     
7127                 ];
7128
7129         } else {
7130             
7131                 Roo.log(" no label && no align");
7132                 cfg.cn = [
7133                     
7134                         inputblock
7135                     
7136                 ];
7137                 
7138                 
7139         };
7140         Roo.log('input-parentType: ' + this.parentType);
7141         
7142         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7143            cfg.cls += ' navbar-form';
7144            Roo.log(cfg);
7145         }
7146         
7147         return cfg;
7148         
7149     },
7150     /**
7151      * return the real input element.
7152      */
7153     inputEl: function ()
7154     {
7155         return this.el.select('input.form-control',true).first();
7156     },
7157     setDisabled : function(v)
7158     {
7159         var i  = this.inputEl().dom;
7160         if (!v) {
7161             i.removeAttribute('disabled');
7162             return;
7163             
7164         }
7165         i.setAttribute('disabled','true');
7166     },
7167     initEvents : function()
7168     {
7169         
7170         this.inputEl().on("keydown" , this.fireKey,  this);
7171         this.inputEl().on("focus", this.onFocus,  this);
7172         this.inputEl().on("blur", this.onBlur,  this);
7173         
7174         this.inputEl().relayEvent('keyup', this);
7175
7176         // reference to original value for reset
7177         this.originalValue = this.getValue();
7178         //Roo.form.TextField.superclass.initEvents.call(this);
7179         if(this.validationEvent == 'keyup'){
7180             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7181             this.inputEl().on('keyup', this.filterValidation, this);
7182         }
7183         else if(this.validationEvent !== false){
7184             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7185         }
7186         
7187         if(this.selectOnFocus){
7188             this.on("focus", this.preFocus, this);
7189             
7190         }
7191         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7192             this.inputEl().on("keypress", this.filterKeys, this);
7193         }
7194        /* if(this.grow){
7195             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7196             this.el.on("click", this.autoSize,  this);
7197         }
7198         */
7199         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7200             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7201         }
7202         
7203         if (typeof(this.before) == 'object') {
7204             this.before.render(this.el.select('.roo-input-before',true).first());
7205         }
7206         if (typeof(this.after) == 'object') {
7207             this.after.render(this.el.select('.roo-input-after',true).first());
7208         }
7209         
7210         
7211     },
7212     filterValidation : function(e){
7213         if(!e.isNavKeyPress()){
7214             this.validationTask.delay(this.validationDelay);
7215         }
7216     },
7217      /**
7218      * Validates the field value
7219      * @return {Boolean} True if the value is valid, else false
7220      */
7221     validate : function(){
7222         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7223         if(this.disabled || this.validateValue(this.getRawValue())){
7224             this.clearInvalid();
7225             return true;
7226         }
7227         return false;
7228     },
7229     
7230     
7231     /**
7232      * Validates a value according to the field's validation rules and marks the field as invalid
7233      * if the validation fails
7234      * @param {Mixed} value The value to validate
7235      * @return {Boolean} True if the value is valid, else false
7236      */
7237     validateValue : function(value){
7238         if(value.length < 1)  { // if it's blank
7239              if(this.allowBlank){
7240                 this.clearInvalid();
7241                 return true;
7242              }else{
7243                 this.markInvalid(this.blankText);
7244                 return false;
7245              }
7246         }
7247         if(value.length < this.minLength){
7248             this.markInvalid(String.format(this.minLengthText, this.minLength));
7249             return false;
7250         }
7251         if(value.length > this.maxLength){
7252             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
7253             return false;
7254         }
7255         if(this.vtype){
7256             var vt = Roo.form.VTypes;
7257             if(!vt[this.vtype](value, this)){
7258                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
7259                 return false;
7260             }
7261         }
7262         if(typeof this.validator == "function"){
7263             var msg = this.validator(value);
7264             if(msg !== true){
7265                 this.markInvalid(msg);
7266                 return false;
7267             }
7268         }
7269         if(this.regex && !this.regex.test(value)){
7270             this.markInvalid(this.regexText);
7271             return false;
7272         }
7273         return true;
7274     },
7275
7276     
7277     
7278      // private
7279     fireKey : function(e){
7280         //Roo.log('field ' + e.getKey());
7281         if(e.isNavKeyPress()){
7282             this.fireEvent("specialkey", this, e);
7283         }
7284     },
7285     focus : function (selectText){
7286         if(this.rendered){
7287             this.inputEl().focus();
7288             if(selectText === true){
7289                 this.inputEl().dom.select();
7290             }
7291         }
7292         return this;
7293     } ,
7294     
7295     onFocus : function(){
7296         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7297            // this.el.addClass(this.focusClass);
7298         }
7299         if(!this.hasFocus){
7300             this.hasFocus = true;
7301             this.startValue = this.getValue();
7302             this.fireEvent("focus", this);
7303         }
7304     },
7305     
7306     beforeBlur : Roo.emptyFn,
7307
7308     
7309     // private
7310     onBlur : function(){
7311         this.beforeBlur();
7312         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7313             //this.el.removeClass(this.focusClass);
7314         }
7315         this.hasFocus = false;
7316         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7317             this.validate();
7318         }
7319         var v = this.getValue();
7320         if(String(v) !== String(this.startValue)){
7321             this.fireEvent('change', this, v, this.startValue);
7322         }
7323         this.fireEvent("blur", this);
7324     },
7325     
7326     /**
7327      * Resets the current field value to the originally loaded value and clears any validation messages
7328      */
7329     reset : function(){
7330         this.setValue(this.originalValue);
7331         this.clearInvalid();
7332     },
7333      /**
7334      * Returns the name of the field
7335      * @return {Mixed} name The name field
7336      */
7337     getName: function(){
7338         return this.name;
7339     },
7340      /**
7341      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
7342      * @return {Mixed} value The field value
7343      */
7344     getValue : function(){
7345         
7346         var v = this.inputEl().getValue();
7347         
7348         return v;
7349     },
7350     /**
7351      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
7352      * @return {Mixed} value The field value
7353      */
7354     getRawValue : function(){
7355         var v = this.inputEl().getValue();
7356         
7357         return v;
7358     },
7359     
7360     /**
7361      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
7362      * @param {Mixed} value The value to set
7363      */
7364     setRawValue : function(v){
7365         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7366     },
7367     
7368     selectText : function(start, end){
7369         var v = this.getRawValue();
7370         if(v.length > 0){
7371             start = start === undefined ? 0 : start;
7372             end = end === undefined ? v.length : end;
7373             var d = this.inputEl().dom;
7374             if(d.setSelectionRange){
7375                 d.setSelectionRange(start, end);
7376             }else if(d.createTextRange){
7377                 var range = d.createTextRange();
7378                 range.moveStart("character", start);
7379                 range.moveEnd("character", v.length-end);
7380                 range.select();
7381             }
7382         }
7383     },
7384     
7385     /**
7386      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
7387      * @param {Mixed} value The value to set
7388      */
7389     setValue : function(v){
7390         this.value = v;
7391         if(this.rendered){
7392             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7393             this.validate();
7394         }
7395     },
7396     
7397     /*
7398     processValue : function(value){
7399         if(this.stripCharsRe){
7400             var newValue = value.replace(this.stripCharsRe, '');
7401             if(newValue !== value){
7402                 this.setRawValue(newValue);
7403                 return newValue;
7404             }
7405         }
7406         return value;
7407     },
7408   */
7409     preFocus : function(){
7410         
7411         if(this.selectOnFocus){
7412             this.inputEl().dom.select();
7413         }
7414     },
7415     filterKeys : function(e){
7416         var k = e.getKey();
7417         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7418             return;
7419         }
7420         var c = e.getCharCode(), cc = String.fromCharCode(c);
7421         if(Roo.isIE && (e.isSpecialKey() || !cc)){
7422             return;
7423         }
7424         if(!this.maskRe.test(cc)){
7425             e.stopEvent();
7426         }
7427     },
7428      /**
7429      * Clear any invalid styles/messages for this field
7430      */
7431     clearInvalid : function(){
7432         
7433         if(!this.el || this.preventMark){ // not rendered
7434             return;
7435         }
7436         this.el.removeClass(this.invalidClass);
7437         /*
7438         switch(this.msgTarget){
7439             case 'qtip':
7440                 this.el.dom.qtip = '';
7441                 break;
7442             case 'title':
7443                 this.el.dom.title = '';
7444                 break;
7445             case 'under':
7446                 if(this.errorEl){
7447                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
7448                 }
7449                 break;
7450             case 'side':
7451                 if(this.errorIcon){
7452                     this.errorIcon.dom.qtip = '';
7453                     this.errorIcon.hide();
7454                     this.un('resize', this.alignErrorIcon, this);
7455                 }
7456                 break;
7457             default:
7458                 var t = Roo.getDom(this.msgTarget);
7459                 t.innerHTML = '';
7460                 t.style.display = 'none';
7461                 break;
7462         }
7463         */
7464         this.fireEvent('valid', this);
7465     },
7466      /**
7467      * Mark this field as invalid
7468      * @param {String} msg The validation message
7469      */
7470     markInvalid : function(msg){
7471         if(!this.el  || this.preventMark){ // not rendered
7472             return;
7473         }
7474         this.el.addClass(this.invalidClass);
7475         /*
7476         msg = msg || this.invalidText;
7477         switch(this.msgTarget){
7478             case 'qtip':
7479                 this.el.dom.qtip = msg;
7480                 this.el.dom.qclass = 'x-form-invalid-tip';
7481                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
7482                     Roo.QuickTips.enable();
7483                 }
7484                 break;
7485             case 'title':
7486                 this.el.dom.title = msg;
7487                 break;
7488             case 'under':
7489                 if(!this.errorEl){
7490                     var elp = this.el.findParent('.x-form-element', 5, true);
7491                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
7492                     this.errorEl.setWidth(elp.getWidth(true)-20);
7493                 }
7494                 this.errorEl.update(msg);
7495                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
7496                 break;
7497             case 'side':
7498                 if(!this.errorIcon){
7499                     var elp = this.el.findParent('.x-form-element', 5, true);
7500                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
7501                 }
7502                 this.alignErrorIcon();
7503                 this.errorIcon.dom.qtip = msg;
7504                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
7505                 this.errorIcon.show();
7506                 this.on('resize', this.alignErrorIcon, this);
7507                 break;
7508             default:
7509                 var t = Roo.getDom(this.msgTarget);
7510                 t.innerHTML = msg;
7511                 t.style.display = this.msgDisplay;
7512                 break;
7513         }
7514         */
7515         this.fireEvent('invalid', this, msg);
7516     },
7517     // private
7518     SafariOnKeyDown : function(event)
7519     {
7520         // this is a workaround for a password hang bug on chrome/ webkit.
7521         
7522         var isSelectAll = false;
7523         
7524         if(this.inputEl().dom.selectionEnd > 0){
7525             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7526         }
7527         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7528             event.preventDefault();
7529             this.setValue('');
7530             return;
7531         }
7532         
7533         if(isSelectAll){ // backspace and delete key
7534             
7535             event.preventDefault();
7536             // this is very hacky as keydown always get's upper case.
7537             //
7538             var cc = String.fromCharCode(event.getCharCode());
7539             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
7540             
7541         }
7542     },
7543     adjustWidth : function(tag, w){
7544         tag = tag.toLowerCase();
7545         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7546             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7547                 if(tag == 'input'){
7548                     return w + 2;
7549                 }
7550                 if(tag == 'textarea'){
7551                     return w-2;
7552                 }
7553             }else if(Roo.isOpera){
7554                 if(tag == 'input'){
7555                     return w + 2;
7556                 }
7557                 if(tag == 'textarea'){
7558                     return w-2;
7559                 }
7560             }
7561         }
7562         return w;
7563     }
7564     
7565 });
7566
7567  
7568 /*
7569  * - LGPL
7570  *
7571  * Input
7572  * 
7573  */
7574
7575 /**
7576  * @class Roo.bootstrap.TextArea
7577  * @extends Roo.bootstrap.Input
7578  * Bootstrap TextArea class
7579  * @cfg {Number} cols Specifies the visible width of a text area
7580  * @cfg {Number} rows Specifies the visible number of lines in a text area
7581  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7582  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7583  * @cfg {string} html text
7584  * 
7585  * @constructor
7586  * Create a new TextArea
7587  * @param {Object} config The config object
7588  */
7589
7590 Roo.bootstrap.TextArea = function(config){
7591     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7592    
7593 };
7594
7595 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
7596      
7597     cols : false,
7598     rows : 5,
7599     readOnly : false,
7600     warp : 'soft',
7601     resize : false,
7602     value: false,
7603     html: false,
7604     
7605     getAutoCreate : function(){
7606         
7607         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7608         
7609         var id = Roo.id();
7610         
7611         var cfg = {};
7612         
7613         var input =  {
7614             tag: 'textarea',
7615             id : id,
7616             warp : this.warp,
7617             rows : this.rows,
7618             value : this.value || '',
7619             html: this.html || '',
7620             cls : 'form-control',
7621             placeholder : this.placeholder || '' 
7622             
7623         };
7624         
7625         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7626             input.maxLength = this.maxLength;
7627         }
7628         
7629         if(this.resize){
7630             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7631         }
7632         
7633         if(this.cols){
7634             input.cols = this.cols;
7635         }
7636         
7637         if (this.readOnly) {
7638             input.readonly = true;
7639         }
7640         
7641         if (this.name) {
7642             input.name = this.name;
7643         }
7644         
7645         if (this.size) {
7646             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7647         }
7648         
7649         var settings=this;
7650         ['xs','sm','md','lg'].map(function(size){
7651             if (settings[size]) {
7652                 cfg.cls += ' col-' + size + '-' + settings[size];
7653             }
7654         });
7655         
7656         var inputblock = input;
7657         
7658         if (this.before || this.after) {
7659             
7660             inputblock = {
7661                 cls : 'input-group',
7662                 cn :  [] 
7663             };
7664             if (this.before) {
7665                 inputblock.cn.push({
7666                     tag :'span',
7667                     cls : 'input-group-addon',
7668                     html : this.before
7669                 });
7670             }
7671             inputblock.cn.push(input);
7672             if (this.after) {
7673                 inputblock.cn.push({
7674                     tag :'span',
7675                     cls : 'input-group-addon',
7676                     html : this.after
7677                 });
7678             }
7679             
7680         }
7681         
7682         if (align ==='left' && this.fieldLabel.length) {
7683                 Roo.log("left and has label");
7684                 cfg.cn = [
7685                     
7686                     {
7687                         tag: 'label',
7688                         'for' :  id,
7689                         cls : 'control-label col-sm-' + this.labelWidth,
7690                         html : this.fieldLabel
7691                         
7692                     },
7693                     {
7694                         cls : "col-sm-" + (12 - this.labelWidth), 
7695                         cn: [
7696                             inputblock
7697                         ]
7698                     }
7699                     
7700                 ];
7701         } else if ( this.fieldLabel.length) {
7702                 Roo.log(" label");
7703                  cfg.cn = [
7704                    
7705                     {
7706                         tag: 'label',
7707                         //cls : 'input-group-addon',
7708                         html : this.fieldLabel
7709                         
7710                     },
7711                     
7712                     inputblock
7713                     
7714                 ];
7715
7716         } else {
7717             
7718                    Roo.log(" no label && no align");
7719                 cfg.cn = [
7720                     
7721                         inputblock
7722                     
7723                 ];
7724                 
7725                 
7726         }
7727         
7728         if (this.disabled) {
7729             input.disabled=true;
7730         }
7731         
7732         return cfg;
7733         
7734     },
7735     /**
7736      * return the real textarea element.
7737      */
7738     inputEl: function ()
7739     {
7740         return this.el.select('textarea.form-control',true).first();
7741     }
7742 });
7743
7744  
7745 /*
7746  * - LGPL
7747  *
7748  * trigger field - base class for combo..
7749  * 
7750  */
7751  
7752 /**
7753  * @class Roo.bootstrap.TriggerField
7754  * @extends Roo.bootstrap.Input
7755  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
7756  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
7757  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
7758  * for which you can provide a custom implementation.  For example:
7759  * <pre><code>
7760 var trigger = new Roo.bootstrap.TriggerField();
7761 trigger.onTriggerClick = myTriggerFn;
7762 trigger.applyTo('my-field');
7763 </code></pre>
7764  *
7765  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
7766  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
7767  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
7768  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
7769  * @constructor
7770  * Create a new TriggerField.
7771  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
7772  * to the base TextField)
7773  */
7774 Roo.bootstrap.TriggerField = function(config){
7775     this.mimicing = false;
7776     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
7777 };
7778
7779 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
7780     /**
7781      * @cfg {String} triggerClass A CSS class to apply to the trigger
7782      */
7783      /**
7784      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
7785      */
7786     hideTrigger:false,
7787
7788     /** @cfg {Boolean} grow @hide */
7789     /** @cfg {Number} growMin @hide */
7790     /** @cfg {Number} growMax @hide */
7791
7792     /**
7793      * @hide 
7794      * @method
7795      */
7796     autoSize: Roo.emptyFn,
7797     // private
7798     monitorTab : true,
7799     // private
7800     deferHeight : true,
7801
7802     
7803     actionMode : 'wrap',
7804     
7805     
7806     
7807     getAutoCreate : function(){
7808        
7809         var align = this.labelAlign || this.parentLabelAlign();
7810         
7811         var id = Roo.id();
7812         
7813         var cfg = {
7814             cls: 'form-group' //input-group
7815         };
7816         
7817         
7818         var input =  {
7819             tag: 'input',
7820             id : id,
7821             type : this.inputType,
7822             cls : 'form-control',
7823             autocomplete: 'off',
7824             placeholder : this.placeholder || '' 
7825             
7826         };
7827         if (this.name) {
7828             input.name = this.name;
7829         }
7830         if (this.size) {
7831             input.cls += ' input-' + this.size;
7832         }
7833         
7834         if (this.disabled) {
7835             input.disabled=true;
7836         }
7837         
7838         var inputblock = input;
7839         
7840         if (this.before || this.after) {
7841             
7842             inputblock = {
7843                 cls : 'input-group',
7844                 cn :  [] 
7845             };
7846             if (this.before) {
7847                 inputblock.cn.push({
7848                     tag :'span',
7849                     cls : 'input-group-addon',
7850                     html : this.before
7851                 });
7852             }
7853             inputblock.cn.push(input);
7854             if (this.after) {
7855                 inputblock.cn.push({
7856                     tag :'span',
7857                     cls : 'input-group-addon',
7858                     html : this.after
7859                 });
7860             }
7861             
7862         };
7863         
7864         var box = {
7865             tag: 'div',
7866             cn: [
7867                 {
7868                     tag: 'input',
7869                     type : 'hidden',
7870                     cls: 'form-hidden-field'
7871                 },
7872                 inputblock
7873             ]
7874             
7875         };
7876         
7877         if(this.multiple){
7878             Roo.log('multiple');
7879             
7880             box = {
7881                 tag: 'div',
7882                 cn: [
7883                     {
7884                         tag: 'input',
7885                         type : 'hidden',
7886                         cls: 'form-hidden-field'
7887                     },
7888                     {
7889                         tag: 'ul',
7890                         cls: 'select2-choices',
7891                         cn:[
7892                             {
7893                                 tag: 'li',
7894                                 cls: 'select2-search-field',
7895                                 cn: [
7896
7897                                     inputblock
7898                                 ]
7899                             }
7900                         ]
7901                     }
7902                 ]
7903             }
7904         };
7905         
7906         var combobox = {
7907             cls: 'select2-container input-group',
7908             cn: [
7909                 box
7910 //                {
7911 //                    tag: 'ul',
7912 //                    cls: 'typeahead typeahead-long dropdown-menu',
7913 //                    style: 'display:none'
7914 //                }
7915             ]
7916         };
7917         
7918         if(!this.multiple && this.showToggleBtn){
7919             combobox.cn.push({
7920                 tag :'span',
7921                 cls : 'input-group-addon btn dropdown-toggle',
7922                 cn : [
7923                     {
7924                         tag: 'span',
7925                         cls: 'caret'
7926                     },
7927                     {
7928                         tag: 'span',
7929                         cls: 'combobox-clear',
7930                         cn  : [
7931                             {
7932                                 tag : 'i',
7933                                 cls: 'icon-remove'
7934                             }
7935                         ]
7936                     }
7937                 ]
7938
7939             })
7940         }
7941         
7942         if(this.multiple){
7943             combobox.cls += ' select2-container-multi';
7944         }
7945         
7946         if (align ==='left' && this.fieldLabel.length) {
7947             
7948                 Roo.log("left and has label");
7949                 cfg.cn = [
7950                     
7951                     {
7952                         tag: 'label',
7953                         'for' :  id,
7954                         cls : 'control-label col-sm-' + this.labelWidth,
7955                         html : this.fieldLabel
7956                         
7957                     },
7958                     {
7959                         cls : "col-sm-" + (12 - this.labelWidth), 
7960                         cn: [
7961                             combobox
7962                         ]
7963                     }
7964                     
7965                 ];
7966         } else if ( this.fieldLabel.length) {
7967                 Roo.log(" label");
7968                  cfg.cn = [
7969                    
7970                     {
7971                         tag: 'label',
7972                         //cls : 'input-group-addon',
7973                         html : this.fieldLabel
7974                         
7975                     },
7976                     
7977                     combobox
7978                     
7979                 ];
7980
7981         } else {
7982             
7983                 Roo.log(" no label && no align");
7984                 cfg = combobox
7985                      
7986                 
7987         }
7988          
7989         var settings=this;
7990         ['xs','sm','md','lg'].map(function(size){
7991             if (settings[size]) {
7992                 cfg.cls += ' col-' + size + '-' + settings[size];
7993             }
7994         });
7995         
7996         return cfg;
7997         
7998     },
7999     
8000     
8001     
8002     // private
8003     onResize : function(w, h){
8004 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8005 //        if(typeof w == 'number'){
8006 //            var x = w - this.trigger.getWidth();
8007 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8008 //            this.trigger.setStyle('left', x+'px');
8009 //        }
8010     },
8011
8012     // private
8013     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8014
8015     // private
8016     getResizeEl : function(){
8017         return this.inputEl();
8018     },
8019
8020     // private
8021     getPositionEl : function(){
8022         return this.inputEl();
8023     },
8024
8025     // private
8026     alignErrorIcon : function(){
8027         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8028     },
8029
8030     // private
8031     initEvents : function(){
8032         
8033         this.createList();
8034         
8035         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8036         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8037         if(!this.multiple && this.showToggleBtn){
8038             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8039             if(this.hideTrigger){
8040                 this.trigger.setDisplayed(false);
8041             }
8042             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8043         }
8044         
8045         if(this.multiple){
8046             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8047         }
8048         
8049         //this.trigger.addClassOnOver('x-form-trigger-over');
8050         //this.trigger.addClassOnClick('x-form-trigger-click');
8051         
8052         //if(!this.width){
8053         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8054         //}
8055     },
8056     
8057     createList : function()
8058     {
8059         this.list = Roo.get(document.body).createChild({
8060             tag: 'ul',
8061             cls: 'typeahead typeahead-long dropdown-menu',
8062             style: 'display:none'
8063         });
8064         
8065         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8066         
8067     },
8068
8069     // private
8070     initTrigger : function(){
8071        
8072     },
8073
8074     // private
8075     onDestroy : function(){
8076         if(this.trigger){
8077             this.trigger.removeAllListeners();
8078           //  this.trigger.remove();
8079         }
8080         //if(this.wrap){
8081         //    this.wrap.remove();
8082         //}
8083         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8084     },
8085
8086     // private
8087     onFocus : function(){
8088         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8089         /*
8090         if(!this.mimicing){
8091             this.wrap.addClass('x-trigger-wrap-focus');
8092             this.mimicing = true;
8093             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8094             if(this.monitorTab){
8095                 this.el.on("keydown", this.checkTab, this);
8096             }
8097         }
8098         */
8099     },
8100
8101     // private
8102     checkTab : function(e){
8103         if(e.getKey() == e.TAB){
8104             this.triggerBlur();
8105         }
8106     },
8107
8108     // private
8109     onBlur : function(){
8110         // do nothing
8111     },
8112
8113     // private
8114     mimicBlur : function(e, t){
8115         /*
8116         if(!this.wrap.contains(t) && this.validateBlur()){
8117             this.triggerBlur();
8118         }
8119         */
8120     },
8121
8122     // private
8123     triggerBlur : function(){
8124         this.mimicing = false;
8125         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8126         if(this.monitorTab){
8127             this.el.un("keydown", this.checkTab, this);
8128         }
8129         //this.wrap.removeClass('x-trigger-wrap-focus');
8130         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8131     },
8132
8133     // private
8134     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8135     validateBlur : function(e, t){
8136         return true;
8137     },
8138
8139     // private
8140     onDisable : function(){
8141         this.inputEl().dom.disabled = true;
8142         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8143         //if(this.wrap){
8144         //    this.wrap.addClass('x-item-disabled');
8145         //}
8146     },
8147
8148     // private
8149     onEnable : function(){
8150         this.inputEl().dom.disabled = false;
8151         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8152         //if(this.wrap){
8153         //    this.el.removeClass('x-item-disabled');
8154         //}
8155     },
8156
8157     // private
8158     onShow : function(){
8159         var ae = this.getActionEl();
8160         
8161         if(ae){
8162             ae.dom.style.display = '';
8163             ae.dom.style.visibility = 'visible';
8164         }
8165     },
8166
8167     // private
8168     
8169     onHide : function(){
8170         var ae = this.getActionEl();
8171         ae.dom.style.display = 'none';
8172     },
8173
8174     /**
8175      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8176      * by an implementing function.
8177      * @method
8178      * @param {EventObject} e
8179      */
8180     onTriggerClick : Roo.emptyFn
8181 });
8182  /*
8183  * Based on:
8184  * Ext JS Library 1.1.1
8185  * Copyright(c) 2006-2007, Ext JS, LLC.
8186  *
8187  * Originally Released Under LGPL - original licence link has changed is not relivant.
8188  *
8189  * Fork - LGPL
8190  * <script type="text/javascript">
8191  */
8192
8193
8194 /**
8195  * @class Roo.data.SortTypes
8196  * @singleton
8197  * Defines the default sorting (casting?) comparison functions used when sorting data.
8198  */
8199 Roo.data.SortTypes = {
8200     /**
8201      * Default sort that does nothing
8202      * @param {Mixed} s The value being converted
8203      * @return {Mixed} The comparison value
8204      */
8205     none : function(s){
8206         return s;
8207     },
8208     
8209     /**
8210      * The regular expression used to strip tags
8211      * @type {RegExp}
8212      * @property
8213      */
8214     stripTagsRE : /<\/?[^>]+>/gi,
8215     
8216     /**
8217      * Strips all HTML tags to sort on text only
8218      * @param {Mixed} s The value being converted
8219      * @return {String} The comparison value
8220      */
8221     asText : function(s){
8222         return String(s).replace(this.stripTagsRE, "");
8223     },
8224     
8225     /**
8226      * Strips all HTML tags to sort on text only - Case insensitive
8227      * @param {Mixed} s The value being converted
8228      * @return {String} The comparison value
8229      */
8230     asUCText : function(s){
8231         return String(s).toUpperCase().replace(this.stripTagsRE, "");
8232     },
8233     
8234     /**
8235      * Case insensitive string
8236      * @param {Mixed} s The value being converted
8237      * @return {String} The comparison value
8238      */
8239     asUCString : function(s) {
8240         return String(s).toUpperCase();
8241     },
8242     
8243     /**
8244      * Date sorting
8245      * @param {Mixed} s The value being converted
8246      * @return {Number} The comparison value
8247      */
8248     asDate : function(s) {
8249         if(!s){
8250             return 0;
8251         }
8252         if(s instanceof Date){
8253             return s.getTime();
8254         }
8255         return Date.parse(String(s));
8256     },
8257     
8258     /**
8259      * Float sorting
8260      * @param {Mixed} s The value being converted
8261      * @return {Float} The comparison value
8262      */
8263     asFloat : function(s) {
8264         var val = parseFloat(String(s).replace(/,/g, ""));
8265         if(isNaN(val)) val = 0;
8266         return val;
8267     },
8268     
8269     /**
8270      * Integer sorting
8271      * @param {Mixed} s The value being converted
8272      * @return {Number} The comparison value
8273      */
8274     asInt : function(s) {
8275         var val = parseInt(String(s).replace(/,/g, ""));
8276         if(isNaN(val)) val = 0;
8277         return val;
8278     }
8279 };/*
8280  * Based on:
8281  * Ext JS Library 1.1.1
8282  * Copyright(c) 2006-2007, Ext JS, LLC.
8283  *
8284  * Originally Released Under LGPL - original licence link has changed is not relivant.
8285  *
8286  * Fork - LGPL
8287  * <script type="text/javascript">
8288  */
8289
8290 /**
8291 * @class Roo.data.Record
8292  * Instances of this class encapsulate both record <em>definition</em> information, and record
8293  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8294  * to access Records cached in an {@link Roo.data.Store} object.<br>
8295  * <p>
8296  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8297  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8298  * objects.<br>
8299  * <p>
8300  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8301  * @constructor
8302  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8303  * {@link #create}. The parameters are the same.
8304  * @param {Array} data An associative Array of data values keyed by the field name.
8305  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8306  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8307  * not specified an integer id is generated.
8308  */
8309 Roo.data.Record = function(data, id){
8310     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8311     this.data = data;
8312 };
8313
8314 /**
8315  * Generate a constructor for a specific record layout.
8316  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8317  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8318  * Each field definition object may contain the following properties: <ul>
8319  * <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,
8320  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8321  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8322  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8323  * is being used, then this is a string containing the javascript expression to reference the data relative to 
8324  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8325  * to the data item relative to the record element. If the mapping expression is the same as the field name,
8326  * this may be omitted.</p></li>
8327  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8328  * <ul><li>auto (Default, implies no conversion)</li>
8329  * <li>string</li>
8330  * <li>int</li>
8331  * <li>float</li>
8332  * <li>boolean</li>
8333  * <li>date</li></ul></p></li>
8334  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8335  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8336  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8337  * by the Reader into an object that will be stored in the Record. It is passed the
8338  * following parameters:<ul>
8339  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8340  * </ul></p></li>
8341  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8342  * </ul>
8343  * <br>usage:<br><pre><code>
8344 var TopicRecord = Roo.data.Record.create(
8345     {name: 'title', mapping: 'topic_title'},
8346     {name: 'author', mapping: 'username'},
8347     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8348     {name: 'lastPost', mapping: 'post_time', type: 'date'},
8349     {name: 'lastPoster', mapping: 'user2'},
8350     {name: 'excerpt', mapping: 'post_text'}
8351 );
8352
8353 var myNewRecord = new TopicRecord({
8354     title: 'Do my job please',
8355     author: 'noobie',
8356     totalPosts: 1,
8357     lastPost: new Date(),
8358     lastPoster: 'Animal',
8359     excerpt: 'No way dude!'
8360 });
8361 myStore.add(myNewRecord);
8362 </code></pre>
8363  * @method create
8364  * @static
8365  */
8366 Roo.data.Record.create = function(o){
8367     var f = function(){
8368         f.superclass.constructor.apply(this, arguments);
8369     };
8370     Roo.extend(f, Roo.data.Record);
8371     var p = f.prototype;
8372     p.fields = new Roo.util.MixedCollection(false, function(field){
8373         return field.name;
8374     });
8375     for(var i = 0, len = o.length; i < len; i++){
8376         p.fields.add(new Roo.data.Field(o[i]));
8377     }
8378     f.getField = function(name){
8379         return p.fields.get(name);  
8380     };
8381     return f;
8382 };
8383
8384 Roo.data.Record.AUTO_ID = 1000;
8385 Roo.data.Record.EDIT = 'edit';
8386 Roo.data.Record.REJECT = 'reject';
8387 Roo.data.Record.COMMIT = 'commit';
8388
8389 Roo.data.Record.prototype = {
8390     /**
8391      * Readonly flag - true if this record has been modified.
8392      * @type Boolean
8393      */
8394     dirty : false,
8395     editing : false,
8396     error: null,
8397     modified: null,
8398
8399     // private
8400     join : function(store){
8401         this.store = store;
8402     },
8403
8404     /**
8405      * Set the named field to the specified value.
8406      * @param {String} name The name of the field to set.
8407      * @param {Object} value The value to set the field to.
8408      */
8409     set : function(name, value){
8410         if(this.data[name] == value){
8411             return;
8412         }
8413         this.dirty = true;
8414         if(!this.modified){
8415             this.modified = {};
8416         }
8417         if(typeof this.modified[name] == 'undefined'){
8418             this.modified[name] = this.data[name];
8419         }
8420         this.data[name] = value;
8421         if(!this.editing && this.store){
8422             this.store.afterEdit(this);
8423         }       
8424     },
8425
8426     /**
8427      * Get the value of the named field.
8428      * @param {String} name The name of the field to get the value of.
8429      * @return {Object} The value of the field.
8430      */
8431     get : function(name){
8432         return this.data[name]; 
8433     },
8434
8435     // private
8436     beginEdit : function(){
8437         this.editing = true;
8438         this.modified = {}; 
8439     },
8440
8441     // private
8442     cancelEdit : function(){
8443         this.editing = false;
8444         delete this.modified;
8445     },
8446
8447     // private
8448     endEdit : function(){
8449         this.editing = false;
8450         if(this.dirty && this.store){
8451             this.store.afterEdit(this);
8452         }
8453     },
8454
8455     /**
8456      * Usually called by the {@link Roo.data.Store} which owns the Record.
8457      * Rejects all changes made to the Record since either creation, or the last commit operation.
8458      * Modified fields are reverted to their original values.
8459      * <p>
8460      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8461      * of reject operations.
8462      */
8463     reject : function(){
8464         var m = this.modified;
8465         for(var n in m){
8466             if(typeof m[n] != "function"){
8467                 this.data[n] = m[n];
8468             }
8469         }
8470         this.dirty = false;
8471         delete this.modified;
8472         this.editing = false;
8473         if(this.store){
8474             this.store.afterReject(this);
8475         }
8476     },
8477
8478     /**
8479      * Usually called by the {@link Roo.data.Store} which owns the Record.
8480      * Commits all changes made to the Record since either creation, or the last commit operation.
8481      * <p>
8482      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8483      * of commit operations.
8484      */
8485     commit : function(){
8486         this.dirty = false;
8487         delete this.modified;
8488         this.editing = false;
8489         if(this.store){
8490             this.store.afterCommit(this);
8491         }
8492     },
8493
8494     // private
8495     hasError : function(){
8496         return this.error != null;
8497     },
8498
8499     // private
8500     clearError : function(){
8501         this.error = null;
8502     },
8503
8504     /**
8505      * Creates a copy of this record.
8506      * @param {String} id (optional) A new record id if you don't want to use this record's id
8507      * @return {Record}
8508      */
8509     copy : function(newId) {
8510         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8511     }
8512 };/*
8513  * Based on:
8514  * Ext JS Library 1.1.1
8515  * Copyright(c) 2006-2007, Ext JS, LLC.
8516  *
8517  * Originally Released Under LGPL - original licence link has changed is not relivant.
8518  *
8519  * Fork - LGPL
8520  * <script type="text/javascript">
8521  */
8522
8523
8524
8525 /**
8526  * @class Roo.data.Store
8527  * @extends Roo.util.Observable
8528  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8529  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8530  * <p>
8531  * 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
8532  * has no knowledge of the format of the data returned by the Proxy.<br>
8533  * <p>
8534  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8535  * instances from the data object. These records are cached and made available through accessor functions.
8536  * @constructor
8537  * Creates a new Store.
8538  * @param {Object} config A config object containing the objects needed for the Store to access data,
8539  * and read the data into Records.
8540  */
8541 Roo.data.Store = function(config){
8542     this.data = new Roo.util.MixedCollection(false);
8543     this.data.getKey = function(o){
8544         return o.id;
8545     };
8546     this.baseParams = {};
8547     // private
8548     this.paramNames = {
8549         "start" : "start",
8550         "limit" : "limit",
8551         "sort" : "sort",
8552         "dir" : "dir",
8553         "multisort" : "_multisort"
8554     };
8555
8556     if(config && config.data){
8557         this.inlineData = config.data;
8558         delete config.data;
8559     }
8560
8561     Roo.apply(this, config);
8562     
8563     if(this.reader){ // reader passed
8564         this.reader = Roo.factory(this.reader, Roo.data);
8565         this.reader.xmodule = this.xmodule || false;
8566         if(!this.recordType){
8567             this.recordType = this.reader.recordType;
8568         }
8569         if(this.reader.onMetaChange){
8570             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8571         }
8572     }
8573
8574     if(this.recordType){
8575         this.fields = this.recordType.prototype.fields;
8576     }
8577     this.modified = [];
8578
8579     this.addEvents({
8580         /**
8581          * @event datachanged
8582          * Fires when the data cache has changed, and a widget which is using this Store
8583          * as a Record cache should refresh its view.
8584          * @param {Store} this
8585          */
8586         datachanged : true,
8587         /**
8588          * @event metachange
8589          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8590          * @param {Store} this
8591          * @param {Object} meta The JSON metadata
8592          */
8593         metachange : true,
8594         /**
8595          * @event add
8596          * Fires when Records have been added to the Store
8597          * @param {Store} this
8598          * @param {Roo.data.Record[]} records The array of Records added
8599          * @param {Number} index The index at which the record(s) were added
8600          */
8601         add : true,
8602         /**
8603          * @event remove
8604          * Fires when a Record has been removed from the Store
8605          * @param {Store} this
8606          * @param {Roo.data.Record} record The Record that was removed
8607          * @param {Number} index The index at which the record was removed
8608          */
8609         remove : true,
8610         /**
8611          * @event update
8612          * Fires when a Record has been updated
8613          * @param {Store} this
8614          * @param {Roo.data.Record} record The Record that was updated
8615          * @param {String} operation The update operation being performed.  Value may be one of:
8616          * <pre><code>
8617  Roo.data.Record.EDIT
8618  Roo.data.Record.REJECT
8619  Roo.data.Record.COMMIT
8620          * </code></pre>
8621          */
8622         update : true,
8623         /**
8624          * @event clear
8625          * Fires when the data cache has been cleared.
8626          * @param {Store} this
8627          */
8628         clear : true,
8629         /**
8630          * @event beforeload
8631          * Fires before a request is made for a new data object.  If the beforeload handler returns false
8632          * the load action will be canceled.
8633          * @param {Store} this
8634          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8635          */
8636         beforeload : true,
8637         /**
8638          * @event beforeloadadd
8639          * Fires after a new set of Records has been loaded.
8640          * @param {Store} this
8641          * @param {Roo.data.Record[]} records The Records that were loaded
8642          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8643          */
8644         beforeloadadd : true,
8645         /**
8646          * @event load
8647          * Fires after a new set of Records has been loaded, before they are added to the store.
8648          * @param {Store} this
8649          * @param {Roo.data.Record[]} records The Records that were loaded
8650          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8651          * @params {Object} return from reader
8652          */
8653         load : true,
8654         /**
8655          * @event loadexception
8656          * Fires if an exception occurs in the Proxy during loading.
8657          * Called with the signature of the Proxy's "loadexception" event.
8658          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
8659          * 
8660          * @param {Proxy} 
8661          * @param {Object} return from JsonData.reader() - success, totalRecords, records
8662          * @param {Object} load options 
8663          * @param {Object} jsonData from your request (normally this contains the Exception)
8664          */
8665         loadexception : true
8666     });
8667     
8668     if(this.proxy){
8669         this.proxy = Roo.factory(this.proxy, Roo.data);
8670         this.proxy.xmodule = this.xmodule || false;
8671         this.relayEvents(this.proxy,  ["loadexception"]);
8672     }
8673     this.sortToggle = {};
8674     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
8675
8676     Roo.data.Store.superclass.constructor.call(this);
8677
8678     if(this.inlineData){
8679         this.loadData(this.inlineData);
8680         delete this.inlineData;
8681     }
8682 };
8683
8684 Roo.extend(Roo.data.Store, Roo.util.Observable, {
8685      /**
8686     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
8687     * without a remote query - used by combo/forms at present.
8688     */
8689     
8690     /**
8691     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
8692     */
8693     /**
8694     * @cfg {Array} data Inline data to be loaded when the store is initialized.
8695     */
8696     /**
8697     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
8698     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
8699     */
8700     /**
8701     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
8702     * on any HTTP request
8703     */
8704     /**
8705     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
8706     */
8707     /**
8708     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
8709     */
8710     multiSort: false,
8711     /**
8712     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
8713     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
8714     */
8715     remoteSort : false,
8716
8717     /**
8718     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
8719      * loaded or when a record is removed. (defaults to false).
8720     */
8721     pruneModifiedRecords : false,
8722
8723     // private
8724     lastOptions : null,
8725
8726     /**
8727      * Add Records to the Store and fires the add event.
8728      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8729      */
8730     add : function(records){
8731         records = [].concat(records);
8732         for(var i = 0, len = records.length; i < len; i++){
8733             records[i].join(this);
8734         }
8735         var index = this.data.length;
8736         this.data.addAll(records);
8737         this.fireEvent("add", this, records, index);
8738     },
8739
8740     /**
8741      * Remove a Record from the Store and fires the remove event.
8742      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
8743      */
8744     remove : function(record){
8745         var index = this.data.indexOf(record);
8746         this.data.removeAt(index);
8747         if(this.pruneModifiedRecords){
8748             this.modified.remove(record);
8749         }
8750         this.fireEvent("remove", this, record, index);
8751     },
8752
8753     /**
8754      * Remove all Records from the Store and fires the clear event.
8755      */
8756     removeAll : function(){
8757         this.data.clear();
8758         if(this.pruneModifiedRecords){
8759             this.modified = [];
8760         }
8761         this.fireEvent("clear", this);
8762     },
8763
8764     /**
8765      * Inserts Records to the Store at the given index and fires the add event.
8766      * @param {Number} index The start index at which to insert the passed Records.
8767      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8768      */
8769     insert : function(index, records){
8770         records = [].concat(records);
8771         for(var i = 0, len = records.length; i < len; i++){
8772             this.data.insert(index, records[i]);
8773             records[i].join(this);
8774         }
8775         this.fireEvent("add", this, records, index);
8776     },
8777
8778     /**
8779      * Get the index within the cache of the passed Record.
8780      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
8781      * @return {Number} The index of the passed Record. Returns -1 if not found.
8782      */
8783     indexOf : function(record){
8784         return this.data.indexOf(record);
8785     },
8786
8787     /**
8788      * Get the index within the cache of the Record with the passed id.
8789      * @param {String} id The id of the Record to find.
8790      * @return {Number} The index of the Record. Returns -1 if not found.
8791      */
8792     indexOfId : function(id){
8793         return this.data.indexOfKey(id);
8794     },
8795
8796     /**
8797      * Get the Record with the specified id.
8798      * @param {String} id The id of the Record to find.
8799      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
8800      */
8801     getById : function(id){
8802         return this.data.key(id);
8803     },
8804
8805     /**
8806      * Get the Record at the specified index.
8807      * @param {Number} index The index of the Record to find.
8808      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
8809      */
8810     getAt : function(index){
8811         return this.data.itemAt(index);
8812     },
8813
8814     /**
8815      * Returns a range of Records between specified indices.
8816      * @param {Number} startIndex (optional) The starting index (defaults to 0)
8817      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
8818      * @return {Roo.data.Record[]} An array of Records
8819      */
8820     getRange : function(start, end){
8821         return this.data.getRange(start, end);
8822     },
8823
8824     // private
8825     storeOptions : function(o){
8826         o = Roo.apply({}, o);
8827         delete o.callback;
8828         delete o.scope;
8829         this.lastOptions = o;
8830     },
8831
8832     /**
8833      * Loads the Record cache from the configured Proxy using the configured Reader.
8834      * <p>
8835      * If using remote paging, then the first load call must specify the <em>start</em>
8836      * and <em>limit</em> properties in the options.params property to establish the initial
8837      * position within the dataset, and the number of Records to cache on each read from the Proxy.
8838      * <p>
8839      * <strong>It is important to note that for remote data sources, loading is asynchronous,
8840      * and this call will return before the new data has been loaded. Perform any post-processing
8841      * in a callback function, or in a "load" event handler.</strong>
8842      * <p>
8843      * @param {Object} options An object containing properties which control loading options:<ul>
8844      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
8845      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
8846      * passed the following arguments:<ul>
8847      * <li>r : Roo.data.Record[]</li>
8848      * <li>options: Options object from the load call</li>
8849      * <li>success: Boolean success indicator</li></ul></li>
8850      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
8851      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
8852      * </ul>
8853      */
8854     load : function(options){
8855         options = options || {};
8856         if(this.fireEvent("beforeload", this, options) !== false){
8857             this.storeOptions(options);
8858             var p = Roo.apply(options.params || {}, this.baseParams);
8859             // if meta was not loaded from remote source.. try requesting it.
8860             if (!this.reader.metaFromRemote) {
8861                 p._requestMeta = 1;
8862             }
8863             if(this.sortInfo && this.remoteSort){
8864                 var pn = this.paramNames;
8865                 p[pn["sort"]] = this.sortInfo.field;
8866                 p[pn["dir"]] = this.sortInfo.direction;
8867             }
8868             if (this.multiSort) {
8869                 var pn = this.paramNames;
8870                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
8871             }
8872             
8873             this.proxy.load(p, this.reader, this.loadRecords, this, options);
8874         }
8875     },
8876
8877     /**
8878      * Reloads the Record cache from the configured Proxy using the configured Reader and
8879      * the options from the last load operation performed.
8880      * @param {Object} options (optional) An object containing properties which may override the options
8881      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
8882      * the most recently used options are reused).
8883      */
8884     reload : function(options){
8885         this.load(Roo.applyIf(options||{}, this.lastOptions));
8886     },
8887
8888     // private
8889     // Called as a callback by the Reader during a load operation.
8890     loadRecords : function(o, options, success){
8891         if(!o || success === false){
8892             if(success !== false){
8893                 this.fireEvent("load", this, [], options, o);
8894             }
8895             if(options.callback){
8896                 options.callback.call(options.scope || this, [], options, false);
8897             }
8898             return;
8899         }
8900         // if data returned failure - throw an exception.
8901         if (o.success === false) {
8902             // show a message if no listener is registered.
8903             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
8904                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
8905             }
8906             // loadmask wil be hooked into this..
8907             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
8908             return;
8909         }
8910         var r = o.records, t = o.totalRecords || r.length;
8911         
8912         this.fireEvent("beforeloadadd", this, r, options, o);
8913         
8914         if(!options || options.add !== true){
8915             if(this.pruneModifiedRecords){
8916                 this.modified = [];
8917             }
8918             for(var i = 0, len = r.length; i < len; i++){
8919                 r[i].join(this);
8920             }
8921             if(this.snapshot){
8922                 this.data = this.snapshot;
8923                 delete this.snapshot;
8924             }
8925             this.data.clear();
8926             this.data.addAll(r);
8927             this.totalLength = t;
8928             this.applySort();
8929             this.fireEvent("datachanged", this);
8930         }else{
8931             this.totalLength = Math.max(t, this.data.length+r.length);
8932             this.add(r);
8933         }
8934         this.fireEvent("load", this, r, options, o);
8935         if(options.callback){
8936             options.callback.call(options.scope || this, r, options, true);
8937         }
8938     },
8939
8940
8941     /**
8942      * Loads data from a passed data block. A Reader which understands the format of the data
8943      * must have been configured in the constructor.
8944      * @param {Object} data The data block from which to read the Records.  The format of the data expected
8945      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
8946      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
8947      */
8948     loadData : function(o, append){
8949         var r = this.reader.readRecords(o);
8950         this.loadRecords(r, {add: append}, true);
8951     },
8952
8953     /**
8954      * Gets the number of cached records.
8955      * <p>
8956      * <em>If using paging, this may not be the total size of the dataset. If the data object
8957      * used by the Reader contains the dataset size, then the getTotalCount() function returns
8958      * the data set size</em>
8959      */
8960     getCount : function(){
8961         return this.data.length || 0;
8962     },
8963
8964     /**
8965      * Gets the total number of records in the dataset as returned by the server.
8966      * <p>
8967      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
8968      * the dataset size</em>
8969      */
8970     getTotalCount : function(){
8971         return this.totalLength || 0;
8972     },
8973
8974     /**
8975      * Returns the sort state of the Store as an object with two properties:
8976      * <pre><code>
8977  field {String} The name of the field by which the Records are sorted
8978  direction {String} The sort order, "ASC" or "DESC"
8979      * </code></pre>
8980      */
8981     getSortState : function(){
8982         return this.sortInfo;
8983     },
8984
8985     // private
8986     applySort : function(){
8987         if(this.sortInfo && !this.remoteSort){
8988             var s = this.sortInfo, f = s.field;
8989             var st = this.fields.get(f).sortType;
8990             var fn = function(r1, r2){
8991                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
8992                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
8993             };
8994             this.data.sort(s.direction, fn);
8995             if(this.snapshot && this.snapshot != this.data){
8996                 this.snapshot.sort(s.direction, fn);
8997             }
8998         }
8999     },
9000
9001     /**
9002      * Sets the default sort column and order to be used by the next load operation.
9003      * @param {String} fieldName The name of the field to sort by.
9004      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9005      */
9006     setDefaultSort : function(field, dir){
9007         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9008     },
9009
9010     /**
9011      * Sort the Records.
9012      * If remote sorting is used, the sort is performed on the server, and the cache is
9013      * reloaded. If local sorting is used, the cache is sorted internally.
9014      * @param {String} fieldName The name of the field to sort by.
9015      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9016      */
9017     sort : function(fieldName, dir){
9018         var f = this.fields.get(fieldName);
9019         if(!dir){
9020             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9021             
9022             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9023                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9024             }else{
9025                 dir = f.sortDir;
9026             }
9027         }
9028         this.sortToggle[f.name] = dir;
9029         this.sortInfo = {field: f.name, direction: dir};
9030         if(!this.remoteSort){
9031             this.applySort();
9032             this.fireEvent("datachanged", this);
9033         }else{
9034             this.load(this.lastOptions);
9035         }
9036     },
9037
9038     /**
9039      * Calls the specified function for each of the Records in the cache.
9040      * @param {Function} fn The function to call. The Record is passed as the first parameter.
9041      * Returning <em>false</em> aborts and exits the iteration.
9042      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9043      */
9044     each : function(fn, scope){
9045         this.data.each(fn, scope);
9046     },
9047
9048     /**
9049      * Gets all records modified since the last commit.  Modified records are persisted across load operations
9050      * (e.g., during paging).
9051      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9052      */
9053     getModifiedRecords : function(){
9054         return this.modified;
9055     },
9056
9057     // private
9058     createFilterFn : function(property, value, anyMatch){
9059         if(!value.exec){ // not a regex
9060             value = String(value);
9061             if(value.length == 0){
9062                 return false;
9063             }
9064             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9065         }
9066         return function(r){
9067             return value.test(r.data[property]);
9068         };
9069     },
9070
9071     /**
9072      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9073      * @param {String} property A field on your records
9074      * @param {Number} start The record index to start at (defaults to 0)
9075      * @param {Number} end The last record index to include (defaults to length - 1)
9076      * @return {Number} The sum
9077      */
9078     sum : function(property, start, end){
9079         var rs = this.data.items, v = 0;
9080         start = start || 0;
9081         end = (end || end === 0) ? end : rs.length-1;
9082
9083         for(var i = start; i <= end; i++){
9084             v += (rs[i].data[property] || 0);
9085         }
9086         return v;
9087     },
9088
9089     /**
9090      * Filter the records by a specified property.
9091      * @param {String} field A field on your records
9092      * @param {String/RegExp} value Either a string that the field
9093      * should start with or a RegExp to test against the field
9094      * @param {Boolean} anyMatch True to match any part not just the beginning
9095      */
9096     filter : function(property, value, anyMatch){
9097         var fn = this.createFilterFn(property, value, anyMatch);
9098         return fn ? this.filterBy(fn) : this.clearFilter();
9099     },
9100
9101     /**
9102      * Filter by a function. The specified function will be called with each
9103      * record in this data source. If the function returns true the record is included,
9104      * otherwise it is filtered.
9105      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9106      * @param {Object} scope (optional) The scope of the function (defaults to this)
9107      */
9108     filterBy : function(fn, scope){
9109         this.snapshot = this.snapshot || this.data;
9110         this.data = this.queryBy(fn, scope||this);
9111         this.fireEvent("datachanged", this);
9112     },
9113
9114     /**
9115      * Query the records by a specified property.
9116      * @param {String} field A field on your records
9117      * @param {String/RegExp} value Either a string that the field
9118      * should start with or a RegExp to test against the field
9119      * @param {Boolean} anyMatch True to match any part not just the beginning
9120      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9121      */
9122     query : function(property, value, anyMatch){
9123         var fn = this.createFilterFn(property, value, anyMatch);
9124         return fn ? this.queryBy(fn) : this.data.clone();
9125     },
9126
9127     /**
9128      * Query by a function. The specified function will be called with each
9129      * record in this data source. If the function returns true the record is included
9130      * in the results.
9131      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9132      * @param {Object} scope (optional) The scope of the function (defaults to this)
9133       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9134      **/
9135     queryBy : function(fn, scope){
9136         var data = this.snapshot || this.data;
9137         return data.filterBy(fn, scope||this);
9138     },
9139
9140     /**
9141      * Collects unique values for a particular dataIndex from this store.
9142      * @param {String} dataIndex The property to collect
9143      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9144      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9145      * @return {Array} An array of the unique values
9146      **/
9147     collect : function(dataIndex, allowNull, bypassFilter){
9148         var d = (bypassFilter === true && this.snapshot) ?
9149                 this.snapshot.items : this.data.items;
9150         var v, sv, r = [], l = {};
9151         for(var i = 0, len = d.length; i < len; i++){
9152             v = d[i].data[dataIndex];
9153             sv = String(v);
9154             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9155                 l[sv] = true;
9156                 r[r.length] = v;
9157             }
9158         }
9159         return r;
9160     },
9161
9162     /**
9163      * Revert to a view of the Record cache with no filtering applied.
9164      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9165      */
9166     clearFilter : function(suppressEvent){
9167         if(this.snapshot && this.snapshot != this.data){
9168             this.data = this.snapshot;
9169             delete this.snapshot;
9170             if(suppressEvent !== true){
9171                 this.fireEvent("datachanged", this);
9172             }
9173         }
9174     },
9175
9176     // private
9177     afterEdit : function(record){
9178         if(this.modified.indexOf(record) == -1){
9179             this.modified.push(record);
9180         }
9181         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9182     },
9183     
9184     // private
9185     afterReject : function(record){
9186         this.modified.remove(record);
9187         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9188     },
9189
9190     // private
9191     afterCommit : function(record){
9192         this.modified.remove(record);
9193         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9194     },
9195
9196     /**
9197      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9198      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9199      */
9200     commitChanges : function(){
9201         var m = this.modified.slice(0);
9202         this.modified = [];
9203         for(var i = 0, len = m.length; i < len; i++){
9204             m[i].commit();
9205         }
9206     },
9207
9208     /**
9209      * Cancel outstanding changes on all changed records.
9210      */
9211     rejectChanges : function(){
9212         var m = this.modified.slice(0);
9213         this.modified = [];
9214         for(var i = 0, len = m.length; i < len; i++){
9215             m[i].reject();
9216         }
9217     },
9218
9219     onMetaChange : function(meta, rtype, o){
9220         this.recordType = rtype;
9221         this.fields = rtype.prototype.fields;
9222         delete this.snapshot;
9223         this.sortInfo = meta.sortInfo || this.sortInfo;
9224         this.modified = [];
9225         this.fireEvent('metachange', this, this.reader.meta);
9226     },
9227     
9228     moveIndex : function(data, type)
9229     {
9230         var index = this.indexOf(data);
9231         
9232         var newIndex = index + type;
9233         
9234         this.remove(data);
9235         
9236         this.insert(newIndex, data);
9237         
9238     }
9239 });/*
9240  * Based on:
9241  * Ext JS Library 1.1.1
9242  * Copyright(c) 2006-2007, Ext JS, LLC.
9243  *
9244  * Originally Released Under LGPL - original licence link has changed is not relivant.
9245  *
9246  * Fork - LGPL
9247  * <script type="text/javascript">
9248  */
9249
9250 /**
9251  * @class Roo.data.SimpleStore
9252  * @extends Roo.data.Store
9253  * Small helper class to make creating Stores from Array data easier.
9254  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9255  * @cfg {Array} fields An array of field definition objects, or field name strings.
9256  * @cfg {Array} data The multi-dimensional array of data
9257  * @constructor
9258  * @param {Object} config
9259  */
9260 Roo.data.SimpleStore = function(config){
9261     Roo.data.SimpleStore.superclass.constructor.call(this, {
9262         isLocal : true,
9263         reader: new Roo.data.ArrayReader({
9264                 id: config.id
9265             },
9266             Roo.data.Record.create(config.fields)
9267         ),
9268         proxy : new Roo.data.MemoryProxy(config.data)
9269     });
9270     this.load();
9271 };
9272 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9273  * Based on:
9274  * Ext JS Library 1.1.1
9275  * Copyright(c) 2006-2007, Ext JS, LLC.
9276  *
9277  * Originally Released Under LGPL - original licence link has changed is not relivant.
9278  *
9279  * Fork - LGPL
9280  * <script type="text/javascript">
9281  */
9282
9283 /**
9284 /**
9285  * @extends Roo.data.Store
9286  * @class Roo.data.JsonStore
9287  * Small helper class to make creating Stores for JSON data easier. <br/>
9288 <pre><code>
9289 var store = new Roo.data.JsonStore({
9290     url: 'get-images.php',
9291     root: 'images',
9292     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9293 });
9294 </code></pre>
9295  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9296  * JsonReader and HttpProxy (unless inline data is provided).</b>
9297  * @cfg {Array} fields An array of field definition objects, or field name strings.
9298  * @constructor
9299  * @param {Object} config
9300  */
9301 Roo.data.JsonStore = function(c){
9302     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9303         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9304         reader: new Roo.data.JsonReader(c, c.fields)
9305     }));
9306 };
9307 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9308  * Based on:
9309  * Ext JS Library 1.1.1
9310  * Copyright(c) 2006-2007, Ext JS, LLC.
9311  *
9312  * Originally Released Under LGPL - original licence link has changed is not relivant.
9313  *
9314  * Fork - LGPL
9315  * <script type="text/javascript">
9316  */
9317
9318  
9319 Roo.data.Field = function(config){
9320     if(typeof config == "string"){
9321         config = {name: config};
9322     }
9323     Roo.apply(this, config);
9324     
9325     if(!this.type){
9326         this.type = "auto";
9327     }
9328     
9329     var st = Roo.data.SortTypes;
9330     // named sortTypes are supported, here we look them up
9331     if(typeof this.sortType == "string"){
9332         this.sortType = st[this.sortType];
9333     }
9334     
9335     // set default sortType for strings and dates
9336     if(!this.sortType){
9337         switch(this.type){
9338             case "string":
9339                 this.sortType = st.asUCString;
9340                 break;
9341             case "date":
9342                 this.sortType = st.asDate;
9343                 break;
9344             default:
9345                 this.sortType = st.none;
9346         }
9347     }
9348
9349     // define once
9350     var stripRe = /[\$,%]/g;
9351
9352     // prebuilt conversion function for this field, instead of
9353     // switching every time we're reading a value
9354     if(!this.convert){
9355         var cv, dateFormat = this.dateFormat;
9356         switch(this.type){
9357             case "":
9358             case "auto":
9359             case undefined:
9360                 cv = function(v){ return v; };
9361                 break;
9362             case "string":
9363                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9364                 break;
9365             case "int":
9366                 cv = function(v){
9367                     return v !== undefined && v !== null && v !== '' ?
9368                            parseInt(String(v).replace(stripRe, ""), 10) : '';
9369                     };
9370                 break;
9371             case "float":
9372                 cv = function(v){
9373                     return v !== undefined && v !== null && v !== '' ?
9374                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
9375                     };
9376                 break;
9377             case "bool":
9378             case "boolean":
9379                 cv = function(v){ return v === true || v === "true" || v == 1; };
9380                 break;
9381             case "date":
9382                 cv = function(v){
9383                     if(!v){
9384                         return '';
9385                     }
9386                     if(v instanceof Date){
9387                         return v;
9388                     }
9389                     if(dateFormat){
9390                         if(dateFormat == "timestamp"){
9391                             return new Date(v*1000);
9392                         }
9393                         return Date.parseDate(v, dateFormat);
9394                     }
9395                     var parsed = Date.parse(v);
9396                     return parsed ? new Date(parsed) : null;
9397                 };
9398              break;
9399             
9400         }
9401         this.convert = cv;
9402     }
9403 };
9404
9405 Roo.data.Field.prototype = {
9406     dateFormat: null,
9407     defaultValue: "",
9408     mapping: null,
9409     sortType : null,
9410     sortDir : "ASC"
9411 };/*
9412  * Based on:
9413  * Ext JS Library 1.1.1
9414  * Copyright(c) 2006-2007, Ext JS, LLC.
9415  *
9416  * Originally Released Under LGPL - original licence link has changed is not relivant.
9417  *
9418  * Fork - LGPL
9419  * <script type="text/javascript">
9420  */
9421  
9422 // Base class for reading structured data from a data source.  This class is intended to be
9423 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9424
9425 /**
9426  * @class Roo.data.DataReader
9427  * Base class for reading structured data from a data source.  This class is intended to be
9428  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9429  */
9430
9431 Roo.data.DataReader = function(meta, recordType){
9432     
9433     this.meta = meta;
9434     
9435     this.recordType = recordType instanceof Array ? 
9436         Roo.data.Record.create(recordType) : recordType;
9437 };
9438
9439 Roo.data.DataReader.prototype = {
9440      /**
9441      * Create an empty record
9442      * @param {Object} data (optional) - overlay some values
9443      * @return {Roo.data.Record} record created.
9444      */
9445     newRow :  function(d) {
9446         var da =  {};
9447         this.recordType.prototype.fields.each(function(c) {
9448             switch( c.type) {
9449                 case 'int' : da[c.name] = 0; break;
9450                 case 'date' : da[c.name] = new Date(); break;
9451                 case 'float' : da[c.name] = 0.0; break;
9452                 case 'boolean' : da[c.name] = false; break;
9453                 default : da[c.name] = ""; break;
9454             }
9455             
9456         });
9457         return new this.recordType(Roo.apply(da, d));
9458     }
9459     
9460 };/*
9461  * Based on:
9462  * Ext JS Library 1.1.1
9463  * Copyright(c) 2006-2007, Ext JS, LLC.
9464  *
9465  * Originally Released Under LGPL - original licence link has changed is not relivant.
9466  *
9467  * Fork - LGPL
9468  * <script type="text/javascript">
9469  */
9470
9471 /**
9472  * @class Roo.data.DataProxy
9473  * @extends Roo.data.Observable
9474  * This class is an abstract base class for implementations which provide retrieval of
9475  * unformatted data objects.<br>
9476  * <p>
9477  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9478  * (of the appropriate type which knows how to parse the data object) to provide a block of
9479  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9480  * <p>
9481  * Custom implementations must implement the load method as described in
9482  * {@link Roo.data.HttpProxy#load}.
9483  */
9484 Roo.data.DataProxy = function(){
9485     this.addEvents({
9486         /**
9487          * @event beforeload
9488          * Fires before a network request is made to retrieve a data object.
9489          * @param {Object} This DataProxy object.
9490          * @param {Object} params The params parameter to the load function.
9491          */
9492         beforeload : true,
9493         /**
9494          * @event load
9495          * Fires before the load method's callback is called.
9496          * @param {Object} This DataProxy object.
9497          * @param {Object} o The data object.
9498          * @param {Object} arg The callback argument object passed to the load function.
9499          */
9500         load : true,
9501         /**
9502          * @event loadexception
9503          * Fires if an Exception occurs during data retrieval.
9504          * @param {Object} This DataProxy object.
9505          * @param {Object} o The data object.
9506          * @param {Object} arg The callback argument object passed to the load function.
9507          * @param {Object} e The Exception.
9508          */
9509         loadexception : true
9510     });
9511     Roo.data.DataProxy.superclass.constructor.call(this);
9512 };
9513
9514 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9515
9516     /**
9517      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9518      */
9519 /*
9520  * Based on:
9521  * Ext JS Library 1.1.1
9522  * Copyright(c) 2006-2007, Ext JS, LLC.
9523  *
9524  * Originally Released Under LGPL - original licence link has changed is not relivant.
9525  *
9526  * Fork - LGPL
9527  * <script type="text/javascript">
9528  */
9529 /**
9530  * @class Roo.data.MemoryProxy
9531  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9532  * to the Reader when its load method is called.
9533  * @constructor
9534  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9535  */
9536 Roo.data.MemoryProxy = function(data){
9537     if (data.data) {
9538         data = data.data;
9539     }
9540     Roo.data.MemoryProxy.superclass.constructor.call(this);
9541     this.data = data;
9542 };
9543
9544 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9545     /**
9546      * Load data from the requested source (in this case an in-memory
9547      * data object passed to the constructor), read the data object into
9548      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9549      * process that block using the passed callback.
9550      * @param {Object} params This parameter is not used by the MemoryProxy class.
9551      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9552      * object into a block of Roo.data.Records.
9553      * @param {Function} callback The function into which to pass the block of Roo.data.records.
9554      * The function must be passed <ul>
9555      * <li>The Record block object</li>
9556      * <li>The "arg" argument from the load function</li>
9557      * <li>A boolean success indicator</li>
9558      * </ul>
9559      * @param {Object} scope The scope in which to call the callback
9560      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9561      */
9562     load : function(params, reader, callback, scope, arg){
9563         params = params || {};
9564         var result;
9565         try {
9566             result = reader.readRecords(this.data);
9567         }catch(e){
9568             this.fireEvent("loadexception", this, arg, null, e);
9569             callback.call(scope, null, arg, false);
9570             return;
9571         }
9572         callback.call(scope, result, arg, true);
9573     },
9574     
9575     // private
9576     update : function(params, records){
9577         
9578     }
9579 });/*
9580  * Based on:
9581  * Ext JS Library 1.1.1
9582  * Copyright(c) 2006-2007, Ext JS, LLC.
9583  *
9584  * Originally Released Under LGPL - original licence link has changed is not relivant.
9585  *
9586  * Fork - LGPL
9587  * <script type="text/javascript">
9588  */
9589 /**
9590  * @class Roo.data.HttpProxy
9591  * @extends Roo.data.DataProxy
9592  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9593  * configured to reference a certain URL.<br><br>
9594  * <p>
9595  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9596  * from which the running page was served.<br><br>
9597  * <p>
9598  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9599  * <p>
9600  * Be aware that to enable the browser to parse an XML document, the server must set
9601  * the Content-Type header in the HTTP response to "text/xml".
9602  * @constructor
9603  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9604  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
9605  * will be used to make the request.
9606  */
9607 Roo.data.HttpProxy = function(conn){
9608     Roo.data.HttpProxy.superclass.constructor.call(this);
9609     // is conn a conn config or a real conn?
9610     this.conn = conn;
9611     this.useAjax = !conn || !conn.events;
9612   
9613 };
9614
9615 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9616     // thse are take from connection...
9617     
9618     /**
9619      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9620      */
9621     /**
9622      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9623      * extra parameters to each request made by this object. (defaults to undefined)
9624      */
9625     /**
9626      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9627      *  to each request made by this object. (defaults to undefined)
9628      */
9629     /**
9630      * @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)
9631      */
9632     /**
9633      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9634      */
9635      /**
9636      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9637      * @type Boolean
9638      */
9639   
9640
9641     /**
9642      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9643      * @type Boolean
9644      */
9645     /**
9646      * Return the {@link Roo.data.Connection} object being used by this Proxy.
9647      * @return {Connection} The Connection object. This object may be used to subscribe to events on
9648      * a finer-grained basis than the DataProxy events.
9649      */
9650     getConnection : function(){
9651         return this.useAjax ? Roo.Ajax : this.conn;
9652     },
9653
9654     /**
9655      * Load data from the configured {@link Roo.data.Connection}, read the data object into
9656      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
9657      * process that block using the passed callback.
9658      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9659      * for the request to the remote server.
9660      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9661      * object into a block of Roo.data.Records.
9662      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9663      * The function must be passed <ul>
9664      * <li>The Record block object</li>
9665      * <li>The "arg" argument from the load function</li>
9666      * <li>A boolean success indicator</li>
9667      * </ul>
9668      * @param {Object} scope The scope in which to call the callback
9669      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9670      */
9671     load : function(params, reader, callback, scope, arg){
9672         if(this.fireEvent("beforeload", this, params) !== false){
9673             var  o = {
9674                 params : params || {},
9675                 request: {
9676                     callback : callback,
9677                     scope : scope,
9678                     arg : arg
9679                 },
9680                 reader: reader,
9681                 callback : this.loadResponse,
9682                 scope: this
9683             };
9684             if(this.useAjax){
9685                 Roo.applyIf(o, this.conn);
9686                 if(this.activeRequest){
9687                     Roo.Ajax.abort(this.activeRequest);
9688                 }
9689                 this.activeRequest = Roo.Ajax.request(o);
9690             }else{
9691                 this.conn.request(o);
9692             }
9693         }else{
9694             callback.call(scope||this, null, arg, false);
9695         }
9696     },
9697
9698     // private
9699     loadResponse : function(o, success, response){
9700         delete this.activeRequest;
9701         if(!success){
9702             this.fireEvent("loadexception", this, o, response);
9703             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9704             return;
9705         }
9706         var result;
9707         try {
9708             result = o.reader.read(response);
9709         }catch(e){
9710             this.fireEvent("loadexception", this, o, response, e);
9711             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9712             return;
9713         }
9714         
9715         this.fireEvent("load", this, o, o.request.arg);
9716         o.request.callback.call(o.request.scope, result, o.request.arg, true);
9717     },
9718
9719     // private
9720     update : function(dataSet){
9721
9722     },
9723
9724     // private
9725     updateResponse : function(dataSet){
9726
9727     }
9728 });/*
9729  * Based on:
9730  * Ext JS Library 1.1.1
9731  * Copyright(c) 2006-2007, Ext JS, LLC.
9732  *
9733  * Originally Released Under LGPL - original licence link has changed is not relivant.
9734  *
9735  * Fork - LGPL
9736  * <script type="text/javascript">
9737  */
9738
9739 /**
9740  * @class Roo.data.ScriptTagProxy
9741  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
9742  * other than the originating domain of the running page.<br><br>
9743  * <p>
9744  * <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
9745  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
9746  * <p>
9747  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
9748  * source code that is used as the source inside a &lt;script> tag.<br><br>
9749  * <p>
9750  * In order for the browser to process the returned data, the server must wrap the data object
9751  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
9752  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
9753  * depending on whether the callback name was passed:
9754  * <p>
9755  * <pre><code>
9756 boolean scriptTag = false;
9757 String cb = request.getParameter("callback");
9758 if (cb != null) {
9759     scriptTag = true;
9760     response.setContentType("text/javascript");
9761 } else {
9762     response.setContentType("application/x-json");
9763 }
9764 Writer out = response.getWriter();
9765 if (scriptTag) {
9766     out.write(cb + "(");
9767 }
9768 out.print(dataBlock.toJsonString());
9769 if (scriptTag) {
9770     out.write(");");
9771 }
9772 </pre></code>
9773  *
9774  * @constructor
9775  * @param {Object} config A configuration object.
9776  */
9777 Roo.data.ScriptTagProxy = function(config){
9778     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
9779     Roo.apply(this, config);
9780     this.head = document.getElementsByTagName("head")[0];
9781 };
9782
9783 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
9784
9785 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
9786     /**
9787      * @cfg {String} url The URL from which to request the data object.
9788      */
9789     /**
9790      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
9791      */
9792     timeout : 30000,
9793     /**
9794      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
9795      * the server the name of the callback function set up by the load call to process the returned data object.
9796      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
9797      * javascript output which calls this named function passing the data object as its only parameter.
9798      */
9799     callbackParam : "callback",
9800     /**
9801      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
9802      * name to the request.
9803      */
9804     nocache : true,
9805
9806     /**
9807      * Load data from the configured URL, read the data object into
9808      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9809      * process that block using the passed callback.
9810      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9811      * for the request to the remote server.
9812      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9813      * object into a block of Roo.data.Records.
9814      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9815      * The function must be passed <ul>
9816      * <li>The Record block object</li>
9817      * <li>The "arg" argument from the load function</li>
9818      * <li>A boolean success indicator</li>
9819      * </ul>
9820      * @param {Object} scope The scope in which to call the callback
9821      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9822      */
9823     load : function(params, reader, callback, scope, arg){
9824         if(this.fireEvent("beforeload", this, params) !== false){
9825
9826             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
9827
9828             var url = this.url;
9829             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
9830             if(this.nocache){
9831                 url += "&_dc=" + (new Date().getTime());
9832             }
9833             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
9834             var trans = {
9835                 id : transId,
9836                 cb : "stcCallback"+transId,
9837                 scriptId : "stcScript"+transId,
9838                 params : params,
9839                 arg : arg,
9840                 url : url,
9841                 callback : callback,
9842                 scope : scope,
9843                 reader : reader
9844             };
9845             var conn = this;
9846
9847             window[trans.cb] = function(o){
9848                 conn.handleResponse(o, trans);
9849             };
9850
9851             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
9852
9853             if(this.autoAbort !== false){
9854                 this.abort();
9855             }
9856
9857             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
9858
9859             var script = document.createElement("script");
9860             script.setAttribute("src", url);
9861             script.setAttribute("type", "text/javascript");
9862             script.setAttribute("id", trans.scriptId);
9863             this.head.appendChild(script);
9864
9865             this.trans = trans;
9866         }else{
9867             callback.call(scope||this, null, arg, false);
9868         }
9869     },
9870
9871     // private
9872     isLoading : function(){
9873         return this.trans ? true : false;
9874     },
9875
9876     /**
9877      * Abort the current server request.
9878      */
9879     abort : function(){
9880         if(this.isLoading()){
9881             this.destroyTrans(this.trans);
9882         }
9883     },
9884
9885     // private
9886     destroyTrans : function(trans, isLoaded){
9887         this.head.removeChild(document.getElementById(trans.scriptId));
9888         clearTimeout(trans.timeoutId);
9889         if(isLoaded){
9890             window[trans.cb] = undefined;
9891             try{
9892                 delete window[trans.cb];
9893             }catch(e){}
9894         }else{
9895             // if hasn't been loaded, wait for load to remove it to prevent script error
9896             window[trans.cb] = function(){
9897                 window[trans.cb] = undefined;
9898                 try{
9899                     delete window[trans.cb];
9900                 }catch(e){}
9901             };
9902         }
9903     },
9904
9905     // private
9906     handleResponse : function(o, trans){
9907         this.trans = false;
9908         this.destroyTrans(trans, true);
9909         var result;
9910         try {
9911             result = trans.reader.readRecords(o);
9912         }catch(e){
9913             this.fireEvent("loadexception", this, o, trans.arg, e);
9914             trans.callback.call(trans.scope||window, null, trans.arg, false);
9915             return;
9916         }
9917         this.fireEvent("load", this, o, trans.arg);
9918         trans.callback.call(trans.scope||window, result, trans.arg, true);
9919     },
9920
9921     // private
9922     handleFailure : function(trans){
9923         this.trans = false;
9924         this.destroyTrans(trans, false);
9925         this.fireEvent("loadexception", this, null, trans.arg);
9926         trans.callback.call(trans.scope||window, null, trans.arg, false);
9927     }
9928 });/*
9929  * Based on:
9930  * Ext JS Library 1.1.1
9931  * Copyright(c) 2006-2007, Ext JS, LLC.
9932  *
9933  * Originally Released Under LGPL - original licence link has changed is not relivant.
9934  *
9935  * Fork - LGPL
9936  * <script type="text/javascript">
9937  */
9938
9939 /**
9940  * @class Roo.data.JsonReader
9941  * @extends Roo.data.DataReader
9942  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
9943  * based on mappings in a provided Roo.data.Record constructor.
9944  * 
9945  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
9946  * in the reply previously. 
9947  * 
9948  * <p>
9949  * Example code:
9950  * <pre><code>
9951 var RecordDef = Roo.data.Record.create([
9952     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
9953     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
9954 ]);
9955 var myReader = new Roo.data.JsonReader({
9956     totalProperty: "results",    // The property which contains the total dataset size (optional)
9957     root: "rows",                // The property which contains an Array of row objects
9958     id: "id"                     // The property within each row object that provides an ID for the record (optional)
9959 }, RecordDef);
9960 </code></pre>
9961  * <p>
9962  * This would consume a JSON file like this:
9963  * <pre><code>
9964 { 'results': 2, 'rows': [
9965     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
9966     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
9967 }
9968 </code></pre>
9969  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
9970  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
9971  * paged from the remote server.
9972  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
9973  * @cfg {String} root name of the property which contains the Array of row objects.
9974  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
9975  * @constructor
9976  * Create a new JsonReader
9977  * @param {Object} meta Metadata configuration options
9978  * @param {Object} recordType Either an Array of field definition objects,
9979  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
9980  */
9981 Roo.data.JsonReader = function(meta, recordType){
9982     
9983     meta = meta || {};
9984     // set some defaults:
9985     Roo.applyIf(meta, {
9986         totalProperty: 'total',
9987         successProperty : 'success',
9988         root : 'data',
9989         id : 'id'
9990     });
9991     
9992     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
9993 };
9994 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
9995     
9996     /**
9997      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
9998      * Used by Store query builder to append _requestMeta to params.
9999      * 
10000      */
10001     metaFromRemote : false,
10002     /**
10003      * This method is only used by a DataProxy which has retrieved data from a remote server.
10004      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10005      * @return {Object} data A data block which is used by an Roo.data.Store object as
10006      * a cache of Roo.data.Records.
10007      */
10008     read : function(response){
10009         var json = response.responseText;
10010        
10011         var o = /* eval:var:o */ eval("("+json+")");
10012         if(!o) {
10013             throw {message: "JsonReader.read: Json object not found"};
10014         }
10015         
10016         if(o.metaData){
10017             
10018             delete this.ef;
10019             this.metaFromRemote = true;
10020             this.meta = o.metaData;
10021             this.recordType = Roo.data.Record.create(o.metaData.fields);
10022             this.onMetaChange(this.meta, this.recordType, o);
10023         }
10024         return this.readRecords(o);
10025     },
10026
10027     // private function a store will implement
10028     onMetaChange : function(meta, recordType, o){
10029
10030     },
10031
10032     /**
10033          * @ignore
10034          */
10035     simpleAccess: function(obj, subsc) {
10036         return obj[subsc];
10037     },
10038
10039         /**
10040          * @ignore
10041          */
10042     getJsonAccessor: function(){
10043         var re = /[\[\.]/;
10044         return function(expr) {
10045             try {
10046                 return(re.test(expr))
10047                     ? new Function("obj", "return obj." + expr)
10048                     : function(obj){
10049                         return obj[expr];
10050                     };
10051             } catch(e){}
10052             return Roo.emptyFn;
10053         };
10054     }(),
10055
10056     /**
10057      * Create a data block containing Roo.data.Records from an XML document.
10058      * @param {Object} o An object which contains an Array of row objects in the property specified
10059      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10060      * which contains the total size of the dataset.
10061      * @return {Object} data A data block which is used by an Roo.data.Store object as
10062      * a cache of Roo.data.Records.
10063      */
10064     readRecords : function(o){
10065         /**
10066          * After any data loads, the raw JSON data is available for further custom processing.
10067          * @type Object
10068          */
10069         this.o = o;
10070         var s = this.meta, Record = this.recordType,
10071             f = Record.prototype.fields, fi = f.items, fl = f.length;
10072
10073 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10074         if (!this.ef) {
10075             if(s.totalProperty) {
10076                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10077                 }
10078                 if(s.successProperty) {
10079                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10080                 }
10081                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10082                 if (s.id) {
10083                         var g = this.getJsonAccessor(s.id);
10084                         this.getId = function(rec) {
10085                                 var r = g(rec);
10086                                 return (r === undefined || r === "") ? null : r;
10087                         };
10088                 } else {
10089                         this.getId = function(){return null;};
10090                 }
10091             this.ef = [];
10092             for(var jj = 0; jj < fl; jj++){
10093                 f = fi[jj];
10094                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10095                 this.ef[jj] = this.getJsonAccessor(map);
10096             }
10097         }
10098
10099         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10100         if(s.totalProperty){
10101             var vt = parseInt(this.getTotal(o), 10);
10102             if(!isNaN(vt)){
10103                 totalRecords = vt;
10104             }
10105         }
10106         if(s.successProperty){
10107             var vs = this.getSuccess(o);
10108             if(vs === false || vs === 'false'){
10109                 success = false;
10110             }
10111         }
10112         var records = [];
10113             for(var i = 0; i < c; i++){
10114                     var n = root[i];
10115                 var values = {};
10116                 var id = this.getId(n);
10117                 for(var j = 0; j < fl; j++){
10118                     f = fi[j];
10119                 var v = this.ef[j](n);
10120                 if (!f.convert) {
10121                     Roo.log('missing convert for ' + f.name);
10122                     Roo.log(f);
10123                     continue;
10124                 }
10125                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10126                 }
10127                 var record = new Record(values, id);
10128                 record.json = n;
10129                 records[i] = record;
10130             }
10131             return {
10132             raw : o,
10133                 success : success,
10134                 records : records,
10135                 totalRecords : totalRecords
10136             };
10137     }
10138 });/*
10139  * Based on:
10140  * Ext JS Library 1.1.1
10141  * Copyright(c) 2006-2007, Ext JS, LLC.
10142  *
10143  * Originally Released Under LGPL - original licence link has changed is not relivant.
10144  *
10145  * Fork - LGPL
10146  * <script type="text/javascript">
10147  */
10148
10149 /**
10150  * @class Roo.data.ArrayReader
10151  * @extends Roo.data.DataReader
10152  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10153  * Each element of that Array represents a row of data fields. The
10154  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10155  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10156  * <p>
10157  * Example code:.
10158  * <pre><code>
10159 var RecordDef = Roo.data.Record.create([
10160     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10161     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10162 ]);
10163 var myReader = new Roo.data.ArrayReader({
10164     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10165 }, RecordDef);
10166 </code></pre>
10167  * <p>
10168  * This would consume an Array like this:
10169  * <pre><code>
10170 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10171   </code></pre>
10172  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10173  * @constructor
10174  * Create a new JsonReader
10175  * @param {Object} meta Metadata configuration options.
10176  * @param {Object} recordType Either an Array of field definition objects
10177  * as specified to {@link Roo.data.Record#create},
10178  * or an {@link Roo.data.Record} object
10179  * created using {@link Roo.data.Record#create}.
10180  */
10181 Roo.data.ArrayReader = function(meta, recordType){
10182     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10183 };
10184
10185 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10186     /**
10187      * Create a data block containing Roo.data.Records from an XML document.
10188      * @param {Object} o An Array of row objects which represents the dataset.
10189      * @return {Object} data A data block which is used by an Roo.data.Store object as
10190      * a cache of Roo.data.Records.
10191      */
10192     readRecords : function(o){
10193         var sid = this.meta ? this.meta.id : null;
10194         var recordType = this.recordType, fields = recordType.prototype.fields;
10195         var records = [];
10196         var root = o;
10197             for(var i = 0; i < root.length; i++){
10198                     var n = root[i];
10199                 var values = {};
10200                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10201                 for(var j = 0, jlen = fields.length; j < jlen; j++){
10202                 var f = fields.items[j];
10203                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10204                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10205                 v = f.convert(v);
10206                 values[f.name] = v;
10207             }
10208                 var record = new recordType(values, id);
10209                 record.json = n;
10210                 records[records.length] = record;
10211             }
10212             return {
10213                 records : records,
10214                 totalRecords : records.length
10215             };
10216     }
10217 });/*
10218  * - LGPL
10219  * * 
10220  */
10221
10222 /**
10223  * @class Roo.bootstrap.ComboBox
10224  * @extends Roo.bootstrap.TriggerField
10225  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10226  * @cfg {Boolean} append (true|false) default false
10227  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10228  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10229  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10230  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
10231  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10232  * @constructor
10233  * Create a new ComboBox.
10234  * @param {Object} config Configuration options
10235  */
10236 Roo.bootstrap.ComboBox = function(config){
10237     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10238     this.addEvents({
10239         /**
10240          * @event expand
10241          * Fires when the dropdown list is expanded
10242              * @param {Roo.bootstrap.ComboBox} combo This combo box
10243              */
10244         'expand' : true,
10245         /**
10246          * @event collapse
10247          * Fires when the dropdown list is collapsed
10248              * @param {Roo.bootstrap.ComboBox} combo This combo box
10249              */
10250         'collapse' : true,
10251         /**
10252          * @event beforeselect
10253          * Fires before a list item is selected. Return false to cancel the selection.
10254              * @param {Roo.bootstrap.ComboBox} combo This combo box
10255              * @param {Roo.data.Record} record The data record returned from the underlying store
10256              * @param {Number} index The index of the selected item in the dropdown list
10257              */
10258         'beforeselect' : true,
10259         /**
10260          * @event select
10261          * Fires when a list item is selected
10262              * @param {Roo.bootstrap.ComboBox} combo This combo box
10263              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10264              * @param {Number} index The index of the selected item in the dropdown list
10265              */
10266         'select' : true,
10267         /**
10268          * @event beforequery
10269          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10270          * The event object passed has these properties:
10271              * @param {Roo.bootstrap.ComboBox} combo This combo box
10272              * @param {String} query The query
10273              * @param {Boolean} forceAll true to force "all" query
10274              * @param {Boolean} cancel true to cancel the query
10275              * @param {Object} e The query event object
10276              */
10277         'beforequery': true,
10278          /**
10279          * @event add
10280          * Fires when the 'add' icon is pressed (add a listener to enable add button)
10281              * @param {Roo.bootstrap.ComboBox} combo This combo box
10282              */
10283         'add' : true,
10284         /**
10285          * @event edit
10286          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10287              * @param {Roo.bootstrap.ComboBox} combo This combo box
10288              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10289              */
10290         'edit' : true,
10291         /**
10292          * @event remove
10293          * Fires when the remove value from the combobox array
10294              * @param {Roo.bootstrap.ComboBox} combo This combo box
10295              */
10296         'remove' : true
10297         
10298     });
10299     
10300     this.item = [];
10301     this.tickItems = [];
10302     
10303     this.selectedIndex = -1;
10304     if(this.mode == 'local'){
10305         if(config.queryDelay === undefined){
10306             this.queryDelay = 10;
10307         }
10308         if(config.minChars === undefined){
10309             this.minChars = 0;
10310         }
10311     }
10312 };
10313
10314 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10315      
10316     /**
10317      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10318      * rendering into an Roo.Editor, defaults to false)
10319      */
10320     /**
10321      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10322      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10323      */
10324     /**
10325      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10326      */
10327     /**
10328      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10329      * the dropdown list (defaults to undefined, with no header element)
10330      */
10331
10332      /**
10333      * @cfg {String/Roo.Template} tpl The template to use to render the output
10334      */
10335      
10336      /**
10337      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10338      */
10339     listWidth: undefined,
10340     /**
10341      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10342      * mode = 'remote' or 'text' if mode = 'local')
10343      */
10344     displayField: undefined,
10345     /**
10346      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10347      * mode = 'remote' or 'value' if mode = 'local'). 
10348      * Note: use of a valueField requires the user make a selection
10349      * in order for a value to be mapped.
10350      */
10351     valueField: undefined,
10352     
10353     
10354     /**
10355      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10356      * field's data value (defaults to the underlying DOM element's name)
10357      */
10358     hiddenName: undefined,
10359     /**
10360      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10361      */
10362     listClass: '',
10363     /**
10364      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10365      */
10366     selectedClass: 'active',
10367     
10368     /**
10369      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10370      */
10371     shadow:'sides',
10372     /**
10373      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10374      * anchor positions (defaults to 'tl-bl')
10375      */
10376     listAlign: 'tl-bl?',
10377     /**
10378      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10379      */
10380     maxHeight: 300,
10381     /**
10382      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
10383      * query specified by the allQuery config option (defaults to 'query')
10384      */
10385     triggerAction: 'query',
10386     /**
10387      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10388      * (defaults to 4, does not apply if editable = false)
10389      */
10390     minChars : 4,
10391     /**
10392      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10393      * delay (typeAheadDelay) if it matches a known value (defaults to false)
10394      */
10395     typeAhead: false,
10396     /**
10397      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10398      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10399      */
10400     queryDelay: 500,
10401     /**
10402      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10403      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
10404      */
10405     pageSize: 0,
10406     /**
10407      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
10408      * when editable = true (defaults to false)
10409      */
10410     selectOnFocus:false,
10411     /**
10412      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10413      */
10414     queryParam: 'query',
10415     /**
10416      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
10417      * when mode = 'remote' (defaults to 'Loading...')
10418      */
10419     loadingText: 'Loading...',
10420     /**
10421      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10422      */
10423     resizable: false,
10424     /**
10425      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10426      */
10427     handleHeight : 8,
10428     /**
10429      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10430      * traditional select (defaults to true)
10431      */
10432     editable: true,
10433     /**
10434      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10435      */
10436     allQuery: '',
10437     /**
10438      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10439      */
10440     mode: 'remote',
10441     /**
10442      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10443      * listWidth has a higher value)
10444      */
10445     minListWidth : 70,
10446     /**
10447      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10448      * allow the user to set arbitrary text into the field (defaults to false)
10449      */
10450     forceSelection:false,
10451     /**
10452      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10453      * if typeAhead = true (defaults to 250)
10454      */
10455     typeAheadDelay : 250,
10456     /**
10457      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10458      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10459      */
10460     valueNotFoundText : undefined,
10461     /**
10462      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10463      */
10464     blockFocus : false,
10465     
10466     /**
10467      * @cfg {Boolean} disableClear Disable showing of clear button.
10468      */
10469     disableClear : false,
10470     /**
10471      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
10472      */
10473     alwaysQuery : false,
10474     
10475     /**
10476      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
10477      */
10478     multiple : false,
10479     
10480     //private
10481     addicon : false,
10482     editicon: false,
10483     
10484     page: 0,
10485     hasQuery: false,
10486     append: false,
10487     loadNext: false,
10488     autoFocus : true,
10489     tickable : false,
10490     btnPosition : 'right',
10491     triggerList : true,
10492     showToggleBtn : true,
10493     // element that contains real text value.. (when hidden is used..)
10494     
10495     getAutoCreate : function()
10496     {
10497         var cfg = false;
10498         
10499         /*
10500          *  Normal ComboBox
10501          */
10502         if(!this.tickable){
10503             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10504             return cfg;
10505         }
10506         
10507         /*
10508          *  ComboBox with tickable selections
10509          */
10510              
10511         var align = this.labelAlign || this.parentLabelAlign();
10512         
10513         cfg = {
10514             cls : 'form-group roo-combobox-tickable' //input-group
10515         };
10516         
10517         
10518         var buttons = {
10519             tag : 'div',
10520             cls : 'tickable-buttons',
10521             cn : [
10522                 {
10523                     tag : 'button',
10524                     type : 'button',
10525                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
10526                     html : 'Edit'
10527                 },
10528                 {
10529                     tag : 'button',
10530                     type : 'button',
10531                     name : 'ok',
10532                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
10533                     html : 'Done'
10534                 },
10535                 {
10536                     tag : 'button',
10537                     type : 'button',
10538                     name : 'cancel',
10539                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
10540                     html : 'Cancel'
10541                 }
10542             ]
10543         };
10544         
10545         var _this = this;
10546         Roo.each(buttons.cn, function(c){
10547             if (_this.size) {
10548                 c.cls += ' btn-' + _this.size;
10549             }
10550
10551             if (_this.disabled) {
10552                 c.disabled = true;
10553             }
10554         });
10555         
10556         var box = {
10557             tag: 'div',
10558             cn: [
10559                 {
10560                     tag: 'input',
10561                     type : 'hidden',
10562                     cls: 'form-hidden-field'
10563                 },
10564                 {
10565                     tag: 'ul',
10566                     cls: 'select2-choices',
10567                     cn:[
10568                         {
10569                             tag: 'li',
10570                             cls: 'select2-search-field',
10571                             cn: [
10572
10573                                 buttons
10574                             ]
10575                         }
10576                     ]
10577                 }
10578             ]
10579         }
10580         
10581         var combobox = {
10582             cls: 'select2-container input-group select2-container-multi',
10583             cn: [
10584                 box
10585 //                {
10586 //                    tag: 'ul',
10587 //                    cls: 'typeahead typeahead-long dropdown-menu',
10588 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
10589 //                }
10590             ]
10591         };
10592         
10593         if (align ==='left' && this.fieldLabel.length) {
10594             
10595                 Roo.log("left and has label");
10596                 cfg.cn = [
10597                     
10598                     {
10599                         tag: 'label',
10600                         'for' :  id,
10601                         cls : 'control-label col-sm-' + this.labelWidth,
10602                         html : this.fieldLabel
10603                         
10604                     },
10605                     {
10606                         cls : "col-sm-" + (12 - this.labelWidth), 
10607                         cn: [
10608                             combobox
10609                         ]
10610                     }
10611                     
10612                 ];
10613         } else if ( this.fieldLabel.length) {
10614                 Roo.log(" label");
10615                  cfg.cn = [
10616                    
10617                     {
10618                         tag: 'label',
10619                         //cls : 'input-group-addon',
10620                         html : this.fieldLabel
10621                         
10622                     },
10623                     
10624                     combobox
10625                     
10626                 ];
10627
10628         } else {
10629             
10630                 Roo.log(" no label && no align");
10631                 cfg = combobox
10632                      
10633                 
10634         }
10635          
10636         var settings=this;
10637         ['xs','sm','md','lg'].map(function(size){
10638             if (settings[size]) {
10639                 cfg.cls += ' col-' + size + '-' + settings[size];
10640             }
10641         });
10642         
10643         return cfg;
10644         
10645     },
10646     
10647     // private
10648     initEvents: function()
10649     {
10650         
10651         if (!this.store) {
10652             throw "can not find store for combo";
10653         }
10654         this.store = Roo.factory(this.store, Roo.data);
10655         
10656         if(this.tickable){
10657             this.initTickableEvents();
10658             return;
10659         }
10660         
10661         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
10662         
10663         if(this.hiddenName){
10664             
10665             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10666             
10667             this.hiddenField.dom.value =
10668                 this.hiddenValue !== undefined ? this.hiddenValue :
10669                 this.value !== undefined ? this.value : '';
10670
10671             // prevent input submission
10672             this.el.dom.removeAttribute('name');
10673             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10674              
10675              
10676         }
10677         //if(Roo.isGecko){
10678         //    this.el.dom.setAttribute('autocomplete', 'off');
10679         //}
10680         
10681         var cls = 'x-combo-list';
10682         
10683         //this.list = new Roo.Layer({
10684         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
10685         //});
10686         
10687         var _this = this;
10688         
10689         (function(){
10690             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10691             _this.list.setWidth(lw);
10692         }).defer(100);
10693         
10694         this.list.on('mouseover', this.onViewOver, this);
10695         this.list.on('mousemove', this.onViewMove, this);
10696         
10697         this.list.on('scroll', this.onViewScroll, this);
10698         
10699         /*
10700         this.list.swallowEvent('mousewheel');
10701         this.assetHeight = 0;
10702
10703         if(this.title){
10704             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
10705             this.assetHeight += this.header.getHeight();
10706         }
10707
10708         this.innerList = this.list.createChild({cls:cls+'-inner'});
10709         this.innerList.on('mouseover', this.onViewOver, this);
10710         this.innerList.on('mousemove', this.onViewMove, this);
10711         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10712         
10713         if(this.allowBlank && !this.pageSize && !this.disableClear){
10714             this.footer = this.list.createChild({cls:cls+'-ft'});
10715             this.pageTb = new Roo.Toolbar(this.footer);
10716            
10717         }
10718         if(this.pageSize){
10719             this.footer = this.list.createChild({cls:cls+'-ft'});
10720             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
10721                     {pageSize: this.pageSize});
10722             
10723         }
10724         
10725         if (this.pageTb && this.allowBlank && !this.disableClear) {
10726             var _this = this;
10727             this.pageTb.add(new Roo.Toolbar.Fill(), {
10728                 cls: 'x-btn-icon x-btn-clear',
10729                 text: '&#160;',
10730                 handler: function()
10731                 {
10732                     _this.collapse();
10733                     _this.clearValue();
10734                     _this.onSelect(false, -1);
10735                 }
10736             });
10737         }
10738         if (this.footer) {
10739             this.assetHeight += this.footer.getHeight();
10740         }
10741         */
10742             
10743         if(!this.tpl){
10744             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
10745         }
10746
10747         this.view = new Roo.View(this.list, this.tpl, {
10748             singleSelect:true, store: this.store, selectedClass: this.selectedClass
10749         });
10750         //this.view.wrapEl.setDisplayed(false);
10751         this.view.on('click', this.onViewClick, this);
10752         
10753         
10754         
10755         this.store.on('beforeload', this.onBeforeLoad, this);
10756         this.store.on('load', this.onLoad, this);
10757         this.store.on('loadexception', this.onLoadException, this);
10758         /*
10759         if(this.resizable){
10760             this.resizer = new Roo.Resizable(this.list,  {
10761                pinned:true, handles:'se'
10762             });
10763             this.resizer.on('resize', function(r, w, h){
10764                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
10765                 this.listWidth = w;
10766                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
10767                 this.restrictHeight();
10768             }, this);
10769             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
10770         }
10771         */
10772         if(!this.editable){
10773             this.editable = true;
10774             this.setEditable(false);
10775         }
10776         
10777         /*
10778         
10779         if (typeof(this.events.add.listeners) != 'undefined') {
10780             
10781             this.addicon = this.wrap.createChild(
10782                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
10783        
10784             this.addicon.on('click', function(e) {
10785                 this.fireEvent('add', this);
10786             }, this);
10787         }
10788         if (typeof(this.events.edit.listeners) != 'undefined') {
10789             
10790             this.editicon = this.wrap.createChild(
10791                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
10792             if (this.addicon) {
10793                 this.editicon.setStyle('margin-left', '40px');
10794             }
10795             this.editicon.on('click', function(e) {
10796                 
10797                 // we fire even  if inothing is selected..
10798                 this.fireEvent('edit', this, this.lastData );
10799                 
10800             }, this);
10801         }
10802         */
10803         
10804         this.keyNav = new Roo.KeyNav(this.inputEl(), {
10805             "up" : function(e){
10806                 this.inKeyMode = true;
10807                 this.selectPrev();
10808             },
10809
10810             "down" : function(e){
10811                 if(!this.isExpanded()){
10812                     this.onTriggerClick();
10813                 }else{
10814                     this.inKeyMode = true;
10815                     this.selectNext();
10816                 }
10817             },
10818
10819             "enter" : function(e){
10820 //                this.onViewClick();
10821                 //return true;
10822                 this.collapse();
10823                 
10824                 if(this.fireEvent("specialkey", this, e)){
10825                     this.onViewClick(false);
10826                 }
10827                 
10828                 return true;
10829             },
10830
10831             "esc" : function(e){
10832                 this.collapse();
10833             },
10834
10835             "tab" : function(e){
10836                 this.collapse();
10837                 
10838                 if(this.fireEvent("specialkey", this, e)){
10839                     this.onViewClick(false);
10840                 }
10841                 
10842                 return true;
10843             },
10844
10845             scope : this,
10846
10847             doRelay : function(foo, bar, hname){
10848                 if(hname == 'down' || this.scope.isExpanded()){
10849                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10850                 }
10851                 return true;
10852             },
10853
10854             forceKeyDown: true
10855         });
10856         
10857         
10858         this.queryDelay = Math.max(this.queryDelay || 10,
10859                 this.mode == 'local' ? 10 : 250);
10860         
10861         
10862         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10863         
10864         if(this.typeAhead){
10865             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10866         }
10867         if(this.editable !== false){
10868             this.inputEl().on("keyup", this.onKeyUp, this);
10869         }
10870         if(this.forceSelection){
10871             this.inputEl().on('blur', this.doForce, this);
10872         }
10873         
10874         if(this.multiple){
10875             this.choices = this.el.select('ul.select2-choices', true).first();
10876             this.searchField = this.el.select('ul li.select2-search-field', true).first();
10877         }
10878     },
10879     
10880     initTickableEvents: function()
10881     {   
10882         this.createList();
10883         
10884         if(this.hiddenName){
10885             
10886             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10887             
10888             this.hiddenField.dom.value =
10889                 this.hiddenValue !== undefined ? this.hiddenValue :
10890                 this.value !== undefined ? this.value : '';
10891
10892             // prevent input submission
10893             this.el.dom.removeAttribute('name');
10894             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10895              
10896              
10897         }
10898         
10899 //        this.list = this.el.select('ul.dropdown-menu',true).first();
10900         
10901         this.choices = this.el.select('ul.select2-choices', true).first();
10902         this.searchField = this.el.select('ul li.select2-search-field', true).first();
10903         if(this.triggerList){
10904             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
10905         }
10906          
10907         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
10908         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
10909         
10910         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
10911         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
10912         
10913         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
10914         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
10915         
10916         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
10917         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
10918         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
10919         
10920         this.okBtn.hide();
10921         this.cancelBtn.hide();
10922         
10923         var _this = this;
10924         
10925         (function(){
10926             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10927             _this.list.setWidth(lw);
10928         }).defer(100);
10929         
10930         this.list.on('mouseover', this.onViewOver, this);
10931         this.list.on('mousemove', this.onViewMove, this);
10932         
10933         this.list.on('scroll', this.onViewScroll, this);
10934         
10935         if(!this.tpl){
10936             this.tpl = '<li class="select2-result"><div class="checkbox"><input id="{roo-id}" type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></li>';
10937         }
10938
10939         this.view = new Roo.View(this.list, this.tpl, {
10940             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
10941         });
10942         
10943         //this.view.wrapEl.setDisplayed(false);
10944         this.view.on('click', this.onViewClick, this);
10945         
10946         
10947         
10948         this.store.on('beforeload', this.onBeforeLoad, this);
10949         this.store.on('load', this.onLoad, this);
10950         this.store.on('loadexception', this.onLoadException, this);
10951         
10952 //        this.keyNav = new Roo.KeyNav(this.inputEl(), {
10953 //            "up" : function(e){
10954 //                this.inKeyMode = true;
10955 //                this.selectPrev();
10956 //            },
10957 //
10958 //            "down" : function(e){
10959 //                if(!this.isExpanded()){
10960 //                    this.onTriggerClick();
10961 //                }else{
10962 //                    this.inKeyMode = true;
10963 //                    this.selectNext();
10964 //                }
10965 //            },
10966 //
10967 //            "enter" : function(e){
10968 ////                this.onViewClick();
10969 //                //return true;
10970 //                this.collapse();
10971 //                
10972 //                if(this.fireEvent("specialkey", this, e)){
10973 //                    this.onViewClick(false);
10974 //                }
10975 //                
10976 //                return true;
10977 //            },
10978 //
10979 //            "esc" : function(e){
10980 //                this.collapse();
10981 //            },
10982 //
10983 //            "tab" : function(e){
10984 //                this.collapse();
10985 //                
10986 //                if(this.fireEvent("specialkey", this, e)){
10987 //                    this.onViewClick(false);
10988 //                }
10989 //                
10990 //                return true;
10991 //            },
10992 //
10993 //            scope : this,
10994 //
10995 //            doRelay : function(foo, bar, hname){
10996 //                if(hname == 'down' || this.scope.isExpanded()){
10997 //                   return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10998 //                }
10999 //                return true;
11000 //            },
11001 //
11002 //            forceKeyDown: true
11003 //        });
11004         
11005         
11006         this.queryDelay = Math.max(this.queryDelay || 10,
11007                 this.mode == 'local' ? 10 : 250);
11008         
11009         
11010         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11011         
11012         if(this.typeAhead){
11013             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11014         }
11015     },
11016
11017     onDestroy : function(){
11018         if(this.view){
11019             this.view.setStore(null);
11020             this.view.el.removeAllListeners();
11021             this.view.el.remove();
11022             this.view.purgeListeners();
11023         }
11024         if(this.list){
11025             this.list.dom.innerHTML  = '';
11026         }
11027         
11028         if(this.store){
11029             this.store.un('beforeload', this.onBeforeLoad, this);
11030             this.store.un('load', this.onLoad, this);
11031             this.store.un('loadexception', this.onLoadException, this);
11032         }
11033         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11034     },
11035
11036     // private
11037     fireKey : function(e){
11038         if(e.isNavKeyPress() && !this.list.isVisible()){
11039             this.fireEvent("specialkey", this, e);
11040         }
11041     },
11042
11043     // private
11044     onResize: function(w, h){
11045 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11046 //        
11047 //        if(typeof w != 'number'){
11048 //            // we do not handle it!?!?
11049 //            return;
11050 //        }
11051 //        var tw = this.trigger.getWidth();
11052 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11053 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11054 //        var x = w - tw;
11055 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11056 //            
11057 //        //this.trigger.setStyle('left', x+'px');
11058 //        
11059 //        if(this.list && this.listWidth === undefined){
11060 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11061 //            this.list.setWidth(lw);
11062 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11063 //        }
11064         
11065     
11066         
11067     },
11068
11069     /**
11070      * Allow or prevent the user from directly editing the field text.  If false is passed,
11071      * the user will only be able to select from the items defined in the dropdown list.  This method
11072      * is the runtime equivalent of setting the 'editable' config option at config time.
11073      * @param {Boolean} value True to allow the user to directly edit the field text
11074      */
11075     setEditable : function(value){
11076         if(value == this.editable){
11077             return;
11078         }
11079         this.editable = value;
11080         if(!value){
11081             this.inputEl().dom.setAttribute('readOnly', true);
11082             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11083             this.inputEl().addClass('x-combo-noedit');
11084         }else{
11085             this.inputEl().dom.setAttribute('readOnly', false);
11086             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11087             this.inputEl().removeClass('x-combo-noedit');
11088         }
11089     },
11090
11091     // private
11092     
11093     onBeforeLoad : function(combo,opts){
11094         if(!this.hasFocus){
11095             return;
11096         }
11097          if (!opts.add) {
11098             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11099          }
11100 //        this.restrictHeight();
11101         this.selectedIndex = -1;
11102     },
11103
11104     // private
11105     onLoad : function(){
11106         
11107         this.hasQuery = false;
11108         
11109         if(!this.hasFocus){
11110             return;
11111         }
11112         
11113         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11114             this.loading.hide();
11115         }
11116         
11117         if(this.store.getCount() > 0){
11118             this.expand();
11119 //            this.restrictHeight();
11120             if(this.lastQuery == this.allQuery){
11121                 if(this.editable && !this.tickable){
11122                     this.inputEl().dom.select();
11123                 }
11124                 
11125                 if(
11126                     !this.selectByValue(this.value, true) &&
11127                     this.autoFocus && (typeof(this.store.lastOptions.add) == 'undefined' || 
11128                     this.store.lastOptions.add != true)
11129                 ){
11130                     this.select(0, true);
11131                 }
11132             }else{
11133                 if(this.autoFocus){
11134                     this.selectNext();
11135                 }
11136                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11137                     this.taTask.delay(this.typeAheadDelay);
11138                 }
11139             }
11140         }else{
11141             this.onEmptyResults();
11142         }
11143         
11144         //this.el.focus();
11145     },
11146     // private
11147     onLoadException : function()
11148     {
11149         this.hasQuery = false;
11150         
11151         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11152             this.loading.hide();
11153         }
11154         
11155         this.collapse();
11156         Roo.log(this.store.reader.jsonData);
11157         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11158             // fixme
11159             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11160         }
11161         
11162         
11163     },
11164     // private
11165     onTypeAhead : function(){
11166         if(this.store.getCount() > 0){
11167             var r = this.store.getAt(0);
11168             var newValue = r.data[this.displayField];
11169             var len = newValue.length;
11170             var selStart = this.getRawValue().length;
11171             
11172             if(selStart != len){
11173                 this.setRawValue(newValue);
11174                 this.selectText(selStart, newValue.length);
11175             }
11176         }
11177     },
11178
11179     // private
11180     onSelect : function(record, index){
11181         
11182         if(this.fireEvent('beforeselect', this, record, index) !== false){
11183         
11184             this.setFromData(index > -1 ? record.data : false);
11185             
11186             this.collapse();
11187             this.fireEvent('select', this, record, index);
11188         }
11189     },
11190
11191     /**
11192      * Returns the currently selected field value or empty string if no value is set.
11193      * @return {String} value The selected value
11194      */
11195     getValue : function(){
11196         
11197         if(this.multiple){
11198             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11199         }
11200         
11201         if(this.valueField){
11202             return typeof this.value != 'undefined' ? this.value : '';
11203         }else{
11204             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11205         }
11206     },
11207
11208     /**
11209      * Clears any text/value currently set in the field
11210      */
11211     clearValue : function(){
11212         if(this.hiddenField){
11213             this.hiddenField.dom.value = '';
11214         }
11215         this.value = '';
11216         this.setRawValue('');
11217         this.lastSelectionText = '';
11218         
11219     },
11220
11221     /**
11222      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
11223      * will be displayed in the field.  If the value does not match the data value of an existing item,
11224      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11225      * Otherwise the field will be blank (although the value will still be set).
11226      * @param {String} value The value to match
11227      */
11228     setValue : function(v){
11229         if(this.multiple){
11230             this.syncValue();
11231             return;
11232         }
11233         
11234         var text = v;
11235         if(this.valueField){
11236             var r = this.findRecord(this.valueField, v);
11237             if(r){
11238                 text = r.data[this.displayField];
11239             }else if(this.valueNotFoundText !== undefined){
11240                 text = this.valueNotFoundText;
11241             }
11242         }
11243         this.lastSelectionText = text;
11244         if(this.hiddenField){
11245             this.hiddenField.dom.value = v;
11246         }
11247         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11248         this.value = v;
11249     },
11250     /**
11251      * @property {Object} the last set data for the element
11252      */
11253     
11254     lastData : false,
11255     /**
11256      * Sets the value of the field based on a object which is related to the record format for the store.
11257      * @param {Object} value the value to set as. or false on reset?
11258      */
11259     setFromData : function(o){
11260         
11261         if(this.multiple){
11262             if(typeof o.display_name !== 'string'){
11263                 for(var i=0;i<o.display_name.length;i++){
11264                     this.addItem({'id':o.id[i],'display_name':o.display_name[i]});
11265                 }
11266                 return;
11267             }
11268             this.addItem(o);
11269             return;
11270         }
11271             
11272         var dv = ''; // display value
11273         var vv = ''; // value value..
11274         this.lastData = o;
11275         if (this.displayField) {
11276             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11277         } else {
11278             // this is an error condition!!!
11279             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11280         }
11281         
11282         if(this.valueField){
11283             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11284         }
11285         
11286         if(this.hiddenField){
11287             this.hiddenField.dom.value = vv;
11288             
11289             this.lastSelectionText = dv;
11290             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11291             this.value = vv;
11292             return;
11293         }
11294         // no hidden field.. - we store the value in 'value', but still display
11295         // display field!!!!
11296         this.lastSelectionText = dv;
11297         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11298         this.value = vv;
11299         
11300         
11301     },
11302     // private
11303     reset : function(){
11304         // overridden so that last data is reset..
11305         this.setValue(this.originalValue);
11306         this.clearInvalid();
11307         this.lastData = false;
11308         if (this.view) {
11309             this.view.clearSelections();
11310         }
11311     },
11312     // private
11313     findRecord : function(prop, value){
11314         var record;
11315         if(this.store.getCount() > 0){
11316             this.store.each(function(r){
11317                 if(r.data[prop] == value){
11318                     record = r;
11319                     return false;
11320                 }
11321                 return true;
11322             });
11323         }
11324         return record;
11325     },
11326     
11327     getName: function()
11328     {
11329         // returns hidden if it's set..
11330         if (!this.rendered) {return ''};
11331         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
11332         
11333     },
11334     // private
11335     onViewMove : function(e, t){
11336         this.inKeyMode = false;
11337     },
11338
11339     // private
11340     onViewOver : function(e, t){
11341         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11342             return;
11343         }
11344         var item = this.view.findItemFromChild(t);
11345         
11346         if(item){
11347             var index = this.view.indexOf(item);
11348             this.select(index, false);
11349         }
11350     },
11351
11352     // private
11353     onViewClick : function(view, doFocus, el, e)
11354     {
11355         var index = this.view.getSelectedIndexes()[0];
11356         
11357         var r = this.store.getAt(index);
11358         
11359         if(this.tickable){
11360             
11361             if(e.getTarget().nodeName.toLowerCase() != 'input'){
11362                 return;
11363             }
11364             
11365             var rm = false;
11366             var _this = this;
11367             
11368             Roo.each(this.tickItems, function(v,k){
11369                 
11370                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11371                     _this.tickItems.splice(k, 1);
11372                     rm = true;
11373                     return;
11374                 }
11375             })
11376             
11377             if(rm){
11378                 return;
11379             }
11380             
11381             this.tickItems.push(r.data);
11382             return;
11383         }
11384         
11385         if(r){
11386             this.onSelect(r, index);
11387         }
11388         if(doFocus !== false && !this.blockFocus){
11389             this.inputEl().focus();
11390         }
11391     },
11392
11393     // private
11394     restrictHeight : function(){
11395         //this.innerList.dom.style.height = '';
11396         //var inner = this.innerList.dom;
11397         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11398         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11399         //this.list.beginUpdate();
11400         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11401         this.list.alignTo(this.inputEl(), this.listAlign);
11402         this.list.alignTo(this.inputEl(), this.listAlign);
11403         //this.list.endUpdate();
11404     },
11405
11406     // private
11407     onEmptyResults : function(){
11408         this.collapse();
11409     },
11410
11411     /**
11412      * Returns true if the dropdown list is expanded, else false.
11413      */
11414     isExpanded : function(){
11415         return this.list.isVisible();
11416     },
11417
11418     /**
11419      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11420      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11421      * @param {String} value The data value of the item to select
11422      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11423      * selected item if it is not currently in view (defaults to true)
11424      * @return {Boolean} True if the value matched an item in the list, else false
11425      */
11426     selectByValue : function(v, scrollIntoView){
11427         if(v !== undefined && v !== null){
11428             var r = this.findRecord(this.valueField || this.displayField, v);
11429             if(r){
11430                 this.select(this.store.indexOf(r), scrollIntoView);
11431                 return true;
11432             }
11433         }
11434         return false;
11435     },
11436
11437     /**
11438      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11439      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11440      * @param {Number} index The zero-based index of the list item to select
11441      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11442      * selected item if it is not currently in view (defaults to true)
11443      */
11444     select : function(index, scrollIntoView){
11445         this.selectedIndex = index;
11446         this.view.select(index);
11447         if(scrollIntoView !== false){
11448             var el = this.view.getNode(index);
11449             if(el && !this.multiple && !this.tickable){
11450                 this.list.scrollChildIntoView(el, false);
11451             }
11452         }
11453     },
11454
11455     // private
11456     selectNext : function(){
11457         var ct = this.store.getCount();
11458         if(ct > 0){
11459             if(this.selectedIndex == -1){
11460                 this.select(0);
11461             }else if(this.selectedIndex < ct-1){
11462                 this.select(this.selectedIndex+1);
11463             }
11464         }
11465     },
11466
11467     // private
11468     selectPrev : function(){
11469         var ct = this.store.getCount();
11470         if(ct > 0){
11471             if(this.selectedIndex == -1){
11472                 this.select(0);
11473             }else if(this.selectedIndex != 0){
11474                 this.select(this.selectedIndex-1);
11475             }
11476         }
11477     },
11478
11479     // private
11480     onKeyUp : function(e){
11481         if(this.editable !== false && !e.isSpecialKey()){
11482             this.lastKey = e.getKey();
11483             this.dqTask.delay(this.queryDelay);
11484         }
11485     },
11486
11487     // private
11488     validateBlur : function(){
11489         return !this.list || !this.list.isVisible();   
11490     },
11491
11492     // private
11493     initQuery : function(){
11494         this.doQuery(this.getRawValue());
11495     },
11496
11497     // private
11498     doForce : function(){
11499         if(this.inputEl().dom.value.length > 0){
11500             this.inputEl().dom.value =
11501                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11502              
11503         }
11504     },
11505
11506     /**
11507      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
11508      * query allowing the query action to be canceled if needed.
11509      * @param {String} query The SQL query to execute
11510      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11511      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
11512      * saved in the current store (defaults to false)
11513      */
11514     doQuery : function(q, forceAll){
11515         
11516         if(q === undefined || q === null){
11517             q = '';
11518         }
11519         var qe = {
11520             query: q,
11521             forceAll: forceAll,
11522             combo: this,
11523             cancel:false
11524         };
11525         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11526             return false;
11527         }
11528         q = qe.query;
11529         
11530         forceAll = qe.forceAll;
11531         if(forceAll === true || (q.length >= this.minChars)){
11532             
11533             this.hasQuery = true;
11534             
11535             if(this.lastQuery != q || this.alwaysQuery){
11536                 this.lastQuery = q;
11537                 if(this.mode == 'local'){
11538                     this.selectedIndex = -1;
11539                     if(forceAll){
11540                         this.store.clearFilter();
11541                     }else{
11542                         this.store.filter(this.displayField, q);
11543                     }
11544                     this.onLoad();
11545                 }else{
11546                     this.store.baseParams[this.queryParam] = q;
11547                     
11548                     var options = {params : this.getParams(q)};
11549                     
11550                     if(this.loadNext){
11551                         options.add = true;
11552                         options.params.start = this.page * this.pageSize;
11553                     }
11554                     
11555                     this.store.load(options);
11556                     /*
11557                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
11558                      *  we should expand the list on onLoad
11559                      *  so command out it
11560                      */
11561 //                    this.expand();
11562                 }
11563             }else{
11564                 this.selectedIndex = -1;
11565                 this.onLoad();   
11566             }
11567         }
11568         
11569         this.loadNext = false;
11570     },
11571
11572     // private
11573     getParams : function(q){
11574         var p = {};
11575         //p[this.queryParam] = q;
11576         
11577         if(this.pageSize){
11578             p.start = 0;
11579             p.limit = this.pageSize;
11580         }
11581         return p;
11582     },
11583
11584     /**
11585      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
11586      */
11587     collapse : function(){
11588         if(!this.isExpanded()){
11589             return;
11590         }
11591         
11592         this.list.hide();
11593         
11594         if(this.tickable){
11595             this.okBtn.hide();
11596             this.cancelBtn.hide();
11597             this.trigger.show();
11598         }
11599         
11600         Roo.get(document).un('mousedown', this.collapseIf, this);
11601         Roo.get(document).un('mousewheel', this.collapseIf, this);
11602         if (!this.editable) {
11603             Roo.get(document).un('keydown', this.listKeyPress, this);
11604         }
11605         this.fireEvent('collapse', this);
11606     },
11607
11608     // private
11609     collapseIf : function(e){
11610         var in_combo  = e.within(this.el);
11611         var in_list =  e.within(this.list);
11612         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
11613         
11614         if (in_combo || in_list || is_list) {
11615             //e.stopPropagation();
11616             return;
11617         }
11618         
11619         if(this.tickable){
11620             this.onTickableFooterButtonClick(e, false, false);
11621         }
11622
11623         this.collapse();
11624         
11625     },
11626
11627     /**
11628      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
11629      */
11630     expand : function(){
11631        
11632         if(this.isExpanded() || !this.hasFocus){
11633             return;
11634         }
11635         
11636         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
11637         this.list.setWidth(lw);
11638         
11639         
11640          Roo.log('expand');
11641         
11642         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
11643         this.list.setWidth(lw);
11644             
11645         this.list.show();
11646         
11647         this.restrictHeight();
11648         
11649         if(this.tickable){
11650             
11651             this.tickItems = Roo.apply([], this.item);
11652             
11653             this.okBtn.show();
11654             this.cancelBtn.show();
11655             this.trigger.hide();
11656             
11657         }
11658         
11659         Roo.get(document).on('mousedown', this.collapseIf, this);
11660         Roo.get(document).on('mousewheel', this.collapseIf, this);
11661         if (!this.editable) {
11662             Roo.get(document).on('keydown', this.listKeyPress, this);
11663         }
11664         
11665         this.fireEvent('expand', this);
11666     },
11667
11668     // private
11669     // Implements the default empty TriggerField.onTriggerClick function
11670     onTriggerClick : function(e)
11671     {
11672         Roo.log('trigger click');
11673         
11674         if(this.disabled || !this.triggerList){
11675             return;
11676         }
11677         
11678         this.page = 0;
11679         this.loadNext = false;
11680         
11681         if(this.isExpanded()){
11682             this.collapse();
11683             if (!this.blockFocus) {
11684                 this.inputEl().focus();
11685             }
11686             
11687         }else {
11688             this.hasFocus = true;
11689             if(this.triggerAction == 'all') {
11690                 this.doQuery(this.allQuery, true);
11691             } else {
11692                 this.doQuery(this.getRawValue());
11693             }
11694             if (!this.blockFocus) {
11695                 this.inputEl().focus();
11696             }
11697         }
11698     },
11699     
11700     onTickableTriggerClick : function(e)
11701     {
11702         if(this.disabled){
11703             return;
11704         }
11705         
11706         this.page = 0;
11707         this.loadNext = false;
11708         this.hasFocus = true;
11709         
11710         if(this.triggerAction == 'all') {
11711             this.doQuery(this.allQuery, true);
11712         } else {
11713             this.doQuery(this.getRawValue());
11714         }
11715     },
11716     
11717     onSearchFieldClick : function(e)
11718     {
11719         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
11720             return;
11721         }
11722         
11723         this.page = 0;
11724         this.loadNext = false;
11725         this.hasFocus = true;
11726         
11727         if(this.triggerAction == 'all') {
11728             this.doQuery(this.allQuery, true);
11729         } else {
11730             this.doQuery(this.getRawValue());
11731         }
11732     },
11733     
11734     listKeyPress : function(e)
11735     {
11736         //Roo.log('listkeypress');
11737         // scroll to first matching element based on key pres..
11738         if (e.isSpecialKey()) {
11739             return false;
11740         }
11741         var k = String.fromCharCode(e.getKey()).toUpperCase();
11742         //Roo.log(k);
11743         var match  = false;
11744         var csel = this.view.getSelectedNodes();
11745         var cselitem = false;
11746         if (csel.length) {
11747             var ix = this.view.indexOf(csel[0]);
11748             cselitem  = this.store.getAt(ix);
11749             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
11750                 cselitem = false;
11751             }
11752             
11753         }
11754         
11755         this.store.each(function(v) { 
11756             if (cselitem) {
11757                 // start at existing selection.
11758                 if (cselitem.id == v.id) {
11759                     cselitem = false;
11760                 }
11761                 return true;
11762             }
11763                 
11764             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
11765                 match = this.store.indexOf(v);
11766                 return false;
11767             }
11768             return true;
11769         }, this);
11770         
11771         if (match === false) {
11772             return true; // no more action?
11773         }
11774         // scroll to?
11775         this.view.select(match);
11776         var sn = Roo.get(this.view.getSelectedNodes()[0])
11777         //sn.scrollIntoView(sn.dom.parentNode, false);
11778     },
11779     
11780     onViewScroll : function(e, t){
11781         
11782         if(this.view.el.getScroll().top == 0 ||this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
11783             return;
11784         }
11785         
11786         this.hasQuery = true;
11787         
11788         this.loading = this.list.select('.loading', true).first();
11789         
11790         if(this.loading === null){
11791             this.list.createChild({
11792                 tag: 'div',
11793                 cls: 'loading select2-more-results select2-active',
11794                 html: 'Loading more results...'
11795             })
11796             
11797             this.loading = this.list.select('.loading', true).first();
11798             
11799             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
11800             
11801             this.loading.hide();
11802         }
11803         
11804         this.loading.show();
11805         
11806         var _combo = this;
11807         
11808         this.page++;
11809         this.loadNext = true;
11810         
11811         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
11812         
11813         return;
11814     },
11815     
11816     addItem : function(o)
11817     {   
11818         var dv = ''; // display value
11819         
11820         if (this.displayField) {
11821             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11822         } else {
11823             // this is an error condition!!!
11824             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11825         }
11826         
11827         if(!dv.length){
11828             return;
11829         }
11830         
11831         var choice = this.choices.createChild({
11832             tag: 'li',
11833             cls: 'select2-search-choice',
11834             cn: [
11835                 {
11836                     tag: 'div',
11837                     html: dv
11838                 },
11839                 {
11840                     tag: 'a',
11841                     href: '#',
11842                     cls: 'select2-search-choice-close',
11843                     tabindex: '-1'
11844                 }
11845             ]
11846             
11847         }, this.searchField);
11848         
11849         var close = choice.select('a.select2-search-choice-close', true).first()
11850         
11851         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
11852         
11853         this.item.push(o);
11854         
11855         this.lastData = o;
11856         
11857         this.syncValue();
11858         
11859         this.inputEl().dom.value = '';
11860         
11861     },
11862     
11863     onRemoveItem : function(e, _self, o)
11864     {
11865         e.preventDefault();
11866         var index = this.item.indexOf(o.data) * 1;
11867         
11868         if( index < 0){
11869             Roo.log('not this item?!');
11870             return;
11871         }
11872         
11873         this.item.splice(index, 1);
11874         o.item.remove();
11875         
11876         this.syncValue();
11877         
11878         this.fireEvent('remove', this, e);
11879         
11880     },
11881     
11882     syncValue : function()
11883     {
11884         if(!this.item.length){
11885             this.clearValue();
11886             return;
11887         }
11888             
11889         var value = [];
11890         var _this = this;
11891         Roo.each(this.item, function(i){
11892             if(_this.valueField){
11893                 value.push(i[_this.valueField]);
11894                 return;
11895             }
11896
11897             value.push(i);
11898         });
11899
11900         this.value = value.join(',');
11901
11902         if(this.hiddenField){
11903             this.hiddenField.dom.value = this.value;
11904         }
11905     },
11906     
11907     clearItem : function()
11908     {
11909         if(!this.multiple){
11910             return;
11911         }
11912         
11913         this.item = [];
11914         
11915         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
11916            c.remove();
11917         });
11918         
11919         this.syncValue();
11920     },
11921     
11922     inputEl: function ()
11923     {
11924         if(this.tickable){
11925             return this.searchField;
11926         }
11927         return this.el.select('input.form-control',true).first();
11928     },
11929     
11930     
11931     onTickableFooterButtonClick : function(e, btn, el)
11932     {
11933         e.preventDefault();
11934         
11935         if(btn && btn.name == 'cancel'){
11936             this.tickItems = Roo.apply([], this.item);
11937             this.collapse();
11938             return;
11939         }
11940         
11941         this.clearItem();
11942         
11943         var _this = this;
11944         
11945         Roo.each(this.tickItems, function(o){
11946             _this.addItem(o);
11947         });
11948         
11949         this.collapse();
11950         
11951     }
11952     
11953     
11954
11955     /** 
11956     * @cfg {Boolean} grow 
11957     * @hide 
11958     */
11959     /** 
11960     * @cfg {Number} growMin 
11961     * @hide 
11962     */
11963     /** 
11964     * @cfg {Number} growMax 
11965     * @hide 
11966     */
11967     /**
11968      * @hide
11969      * @method autoSize
11970      */
11971 });
11972 /*
11973  * Based on:
11974  * Ext JS Library 1.1.1
11975  * Copyright(c) 2006-2007, Ext JS, LLC.
11976  *
11977  * Originally Released Under LGPL - original licence link has changed is not relivant.
11978  *
11979  * Fork - LGPL
11980  * <script type="text/javascript">
11981  */
11982
11983 /**
11984  * @class Roo.View
11985  * @extends Roo.util.Observable
11986  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
11987  * This class also supports single and multi selection modes. <br>
11988  * Create a data model bound view:
11989  <pre><code>
11990  var store = new Roo.data.Store(...);
11991
11992  var view = new Roo.View({
11993     el : "my-element",
11994     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
11995  
11996     singleSelect: true,
11997     selectedClass: "ydataview-selected",
11998     store: store
11999  });
12000
12001  // listen for node click?
12002  view.on("click", function(vw, index, node, e){
12003  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
12004  });
12005
12006  // load XML data
12007  dataModel.load("foobar.xml");
12008  </code></pre>
12009  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
12010  * <br><br>
12011  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
12012  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
12013  * 
12014  * Note: old style constructor is still suported (container, template, config)
12015  * 
12016  * @constructor
12017  * Create a new View
12018  * @param {Object} config The config object
12019  * 
12020  */
12021 Roo.View = function(config, depreciated_tpl, depreciated_config){
12022     
12023     this.parent = false;
12024     
12025     if (typeof(depreciated_tpl) == 'undefined') {
12026         // new way.. - universal constructor.
12027         Roo.apply(this, config);
12028         this.el  = Roo.get(this.el);
12029     } else {
12030         // old format..
12031         this.el  = Roo.get(config);
12032         this.tpl = depreciated_tpl;
12033         Roo.apply(this, depreciated_config);
12034     }
12035     this.wrapEl  = this.el.wrap().wrap();
12036     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
12037     
12038     
12039     if(typeof(this.tpl) == "string"){
12040         this.tpl = new Roo.Template(this.tpl);
12041     } else {
12042         // support xtype ctors..
12043         this.tpl = new Roo.factory(this.tpl, Roo);
12044     }
12045     
12046     
12047     this.tpl.compile();
12048     
12049     /** @private */
12050     this.addEvents({
12051         /**
12052          * @event beforeclick
12053          * Fires before a click is processed. Returns false to cancel the default action.
12054          * @param {Roo.View} this
12055          * @param {Number} index The index of the target node
12056          * @param {HTMLElement} node The target node
12057          * @param {Roo.EventObject} e The raw event object
12058          */
12059             "beforeclick" : true,
12060         /**
12061          * @event click
12062          * Fires when a template node is clicked.
12063          * @param {Roo.View} this
12064          * @param {Number} index The index of the target node
12065          * @param {HTMLElement} node The target node
12066          * @param {Roo.EventObject} e The raw event object
12067          */
12068             "click" : true,
12069         /**
12070          * @event dblclick
12071          * Fires when a template node is double clicked.
12072          * @param {Roo.View} this
12073          * @param {Number} index The index of the target node
12074          * @param {HTMLElement} node The target node
12075          * @param {Roo.EventObject} e The raw event object
12076          */
12077             "dblclick" : true,
12078         /**
12079          * @event contextmenu
12080          * Fires when a template node is right clicked.
12081          * @param {Roo.View} this
12082          * @param {Number} index The index of the target node
12083          * @param {HTMLElement} node The target node
12084          * @param {Roo.EventObject} e The raw event object
12085          */
12086             "contextmenu" : true,
12087         /**
12088          * @event selectionchange
12089          * Fires when the selected nodes change.
12090          * @param {Roo.View} this
12091          * @param {Array} selections Array of the selected nodes
12092          */
12093             "selectionchange" : true,
12094     
12095         /**
12096          * @event beforeselect
12097          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
12098          * @param {Roo.View} this
12099          * @param {HTMLElement} node The node to be selected
12100          * @param {Array} selections Array of currently selected nodes
12101          */
12102             "beforeselect" : true,
12103         /**
12104          * @event preparedata
12105          * Fires on every row to render, to allow you to change the data.
12106          * @param {Roo.View} this
12107          * @param {Object} data to be rendered (change this)
12108          */
12109           "preparedata" : true
12110           
12111           
12112         });
12113
12114
12115
12116     this.el.on({
12117         "click": this.onClick,
12118         "dblclick": this.onDblClick,
12119         "contextmenu": this.onContextMenu,
12120         scope:this
12121     });
12122
12123     this.selections = [];
12124     this.nodes = [];
12125     this.cmp = new Roo.CompositeElementLite([]);
12126     if(this.store){
12127         this.store = Roo.factory(this.store, Roo.data);
12128         this.setStore(this.store, true);
12129     }
12130     
12131     if ( this.footer && this.footer.xtype) {
12132            
12133          var fctr = this.wrapEl.appendChild(document.createElement("div"));
12134         
12135         this.footer.dataSource = this.store
12136         this.footer.container = fctr;
12137         this.footer = Roo.factory(this.footer, Roo);
12138         fctr.insertFirst(this.el);
12139         
12140         // this is a bit insane - as the paging toolbar seems to detach the el..
12141 //        dom.parentNode.parentNode.parentNode
12142          // they get detached?
12143     }
12144     
12145     
12146     Roo.View.superclass.constructor.call(this);
12147     
12148     
12149 };
12150
12151 Roo.extend(Roo.View, Roo.util.Observable, {
12152     
12153      /**
12154      * @cfg {Roo.data.Store} store Data store to load data from.
12155      */
12156     store : false,
12157     
12158     /**
12159      * @cfg {String|Roo.Element} el The container element.
12160      */
12161     el : '',
12162     
12163     /**
12164      * @cfg {String|Roo.Template} tpl The template used by this View 
12165      */
12166     tpl : false,
12167     /**
12168      * @cfg {String} dataName the named area of the template to use as the data area
12169      *                          Works with domtemplates roo-name="name"
12170      */
12171     dataName: false,
12172     /**
12173      * @cfg {String} selectedClass The css class to add to selected nodes
12174      */
12175     selectedClass : "x-view-selected",
12176      /**
12177      * @cfg {String} emptyText The empty text to show when nothing is loaded.
12178      */
12179     emptyText : "",
12180     
12181     /**
12182      * @cfg {String} text to display on mask (default Loading)
12183      */
12184     mask : false,
12185     /**
12186      * @cfg {Boolean} multiSelect Allow multiple selection
12187      */
12188     multiSelect : false,
12189     /**
12190      * @cfg {Boolean} singleSelect Allow single selection
12191      */
12192     singleSelect:  false,
12193     
12194     /**
12195      * @cfg {Boolean} toggleSelect - selecting 
12196      */
12197     toggleSelect : false,
12198     
12199     /**
12200      * @cfg {Boolean} tickable - selecting 
12201      */
12202     tickable : false,
12203     
12204     /**
12205      * Returns the element this view is bound to.
12206      * @return {Roo.Element}
12207      */
12208     getEl : function(){
12209         return this.wrapEl;
12210     },
12211     
12212     
12213
12214     /**
12215      * Refreshes the view. - called by datachanged on the store. - do not call directly.
12216      */
12217     refresh : function(){
12218         Roo.log('refresh');
12219         var t = this.tpl;
12220         
12221         // if we are using something like 'domtemplate', then
12222         // the what gets used is:
12223         // t.applySubtemplate(NAME, data, wrapping data..)
12224         // the outer template then get' applied with
12225         //     the store 'extra data'
12226         // and the body get's added to the
12227         //      roo-name="data" node?
12228         //      <span class='roo-tpl-{name}'></span> ?????
12229         
12230         
12231         
12232         this.clearSelections();
12233         this.el.update("");
12234         var html = [];
12235         var records = this.store.getRange();
12236         if(records.length < 1) {
12237             
12238             // is this valid??  = should it render a template??
12239             
12240             this.el.update(this.emptyText);
12241             return;
12242         }
12243         var el = this.el;
12244         if (this.dataName) {
12245             this.el.update(t.apply(this.store.meta)); //????
12246             el = this.el.child('.roo-tpl-' + this.dataName);
12247         }
12248         
12249         for(var i = 0, len = records.length; i < len; i++){
12250             var data = this.prepareData(records[i].data, i, records[i]);
12251             this.fireEvent("preparedata", this, data, i, records[i]);
12252             
12253             var d = Roo.apply({}, data);
12254             
12255             if(this.tickable){
12256                 Roo.apply(d, {'roo-id' : Roo.id()});
12257                 
12258                 var _this = this;
12259             
12260                 Roo.each(this.parent.item, function(item){
12261                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12262                         return;
12263                     }
12264                     Roo.apply(d, {'roo-data-checked' : 'checked'});
12265                 });
12266             }
12267             
12268             html[html.length] = Roo.util.Format.trim(
12269                 this.dataName ?
12270                     t.applySubtemplate(this.dataName, d, this.store.meta) :
12271                     t.apply(d)
12272             );
12273         }
12274         
12275         
12276         
12277         el.update(html.join(""));
12278         this.nodes = el.dom.childNodes;
12279         this.updateIndexes(0);
12280     },
12281     
12282
12283     /**
12284      * Function to override to reformat the data that is sent to
12285      * the template for each node.
12286      * DEPRICATED - use the preparedata event handler.
12287      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12288      * a JSON object for an UpdateManager bound view).
12289      */
12290     prepareData : function(data, index, record)
12291     {
12292         this.fireEvent("preparedata", this, data, index, record);
12293         return data;
12294     },
12295
12296     onUpdate : function(ds, record){
12297          Roo.log('on update');   
12298         this.clearSelections();
12299         var index = this.store.indexOf(record);
12300         var n = this.nodes[index];
12301         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12302         n.parentNode.removeChild(n);
12303         this.updateIndexes(index, index);
12304     },
12305
12306     
12307     
12308 // --------- FIXME     
12309     onAdd : function(ds, records, index)
12310     {
12311         Roo.log(['on Add', ds, records, index] );        
12312         this.clearSelections();
12313         if(this.nodes.length == 0){
12314             this.refresh();
12315             return;
12316         }
12317         var n = this.nodes[index];
12318         for(var i = 0, len = records.length; i < len; i++){
12319             var d = this.prepareData(records[i].data, i, records[i]);
12320             if(n){
12321                 this.tpl.insertBefore(n, d);
12322             }else{
12323                 
12324                 this.tpl.append(this.el, d);
12325             }
12326         }
12327         this.updateIndexes(index);
12328     },
12329
12330     onRemove : function(ds, record, index){
12331         Roo.log('onRemove');
12332         this.clearSelections();
12333         var el = this.dataName  ?
12334             this.el.child('.roo-tpl-' + this.dataName) :
12335             this.el; 
12336         
12337         el.dom.removeChild(this.nodes[index]);
12338         this.updateIndexes(index);
12339     },
12340
12341     /**
12342      * Refresh an individual node.
12343      * @param {Number} index
12344      */
12345     refreshNode : function(index){
12346         this.onUpdate(this.store, this.store.getAt(index));
12347     },
12348
12349     updateIndexes : function(startIndex, endIndex){
12350         var ns = this.nodes;
12351         startIndex = startIndex || 0;
12352         endIndex = endIndex || ns.length - 1;
12353         for(var i = startIndex; i <= endIndex; i++){
12354             ns[i].nodeIndex = i;
12355         }
12356     },
12357
12358     /**
12359      * Changes the data store this view uses and refresh the view.
12360      * @param {Store} store
12361      */
12362     setStore : function(store, initial){
12363         if(!initial && this.store){
12364             this.store.un("datachanged", this.refresh);
12365             this.store.un("add", this.onAdd);
12366             this.store.un("remove", this.onRemove);
12367             this.store.un("update", this.onUpdate);
12368             this.store.un("clear", this.refresh);
12369             this.store.un("beforeload", this.onBeforeLoad);
12370             this.store.un("load", this.onLoad);
12371             this.store.un("loadexception", this.onLoad);
12372         }
12373         if(store){
12374           
12375             store.on("datachanged", this.refresh, this);
12376             store.on("add", this.onAdd, this);
12377             store.on("remove", this.onRemove, this);
12378             store.on("update", this.onUpdate, this);
12379             store.on("clear", this.refresh, this);
12380             store.on("beforeload", this.onBeforeLoad, this);
12381             store.on("load", this.onLoad, this);
12382             store.on("loadexception", this.onLoad, this);
12383         }
12384         
12385         if(store){
12386             this.refresh();
12387         }
12388     },
12389     /**
12390      * onbeforeLoad - masks the loading area.
12391      *
12392      */
12393     onBeforeLoad : function(store,opts)
12394     {
12395          Roo.log('onBeforeLoad');   
12396         if (!opts.add) {
12397             this.el.update("");
12398         }
12399         this.el.mask(this.mask ? this.mask : "Loading" ); 
12400     },
12401     onLoad : function ()
12402     {
12403         this.el.unmask();
12404     },
12405     
12406
12407     /**
12408      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12409      * @param {HTMLElement} node
12410      * @return {HTMLElement} The template node
12411      */
12412     findItemFromChild : function(node){
12413         var el = this.dataName  ?
12414             this.el.child('.roo-tpl-' + this.dataName,true) :
12415             this.el.dom; 
12416         
12417         if(!node || node.parentNode == el){
12418                     return node;
12419             }
12420             var p = node.parentNode;
12421             while(p && p != el){
12422             if(p.parentNode == el){
12423                 return p;
12424             }
12425             p = p.parentNode;
12426         }
12427             return null;
12428     },
12429
12430     /** @ignore */
12431     onClick : function(e){
12432         var item = this.findItemFromChild(e.getTarget());
12433         if(item){
12434             var index = this.indexOf(item);
12435             if(this.onItemClick(item, index, e) !== false){
12436                 this.fireEvent("click", this, index, item, e);
12437             }
12438         }else{
12439             this.clearSelections();
12440         }
12441     },
12442
12443     /** @ignore */
12444     onContextMenu : function(e){
12445         var item = this.findItemFromChild(e.getTarget());
12446         if(item){
12447             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12448         }
12449     },
12450
12451     /** @ignore */
12452     onDblClick : function(e){
12453         var item = this.findItemFromChild(e.getTarget());
12454         if(item){
12455             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12456         }
12457     },
12458
12459     onItemClick : function(item, index, e)
12460     {
12461         if(this.fireEvent("beforeclick", this, index, item, e) === false){
12462             return false;
12463         }
12464         if (this.toggleSelect) {
12465             var m = this.isSelected(item) ? 'unselect' : 'select';
12466             Roo.log(m);
12467             var _t = this;
12468             _t[m](item, true, false);
12469             return true;
12470         }
12471         if(this.multiSelect || this.singleSelect){
12472             if(this.multiSelect && e.shiftKey && this.lastSelection){
12473                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12474             }else{
12475                 this.select(item, this.multiSelect && e.ctrlKey);
12476                 this.lastSelection = item;
12477             }
12478             
12479             if(!this.tickable){
12480                 e.preventDefault();
12481             }
12482             
12483         }
12484         return true;
12485     },
12486
12487     /**
12488      * Get the number of selected nodes.
12489      * @return {Number}
12490      */
12491     getSelectionCount : function(){
12492         return this.selections.length;
12493     },
12494
12495     /**
12496      * Get the currently selected nodes.
12497      * @return {Array} An array of HTMLElements
12498      */
12499     getSelectedNodes : function(){
12500         return this.selections;
12501     },
12502
12503     /**
12504      * Get the indexes of the selected nodes.
12505      * @return {Array}
12506      */
12507     getSelectedIndexes : function(){
12508         var indexes = [], s = this.selections;
12509         for(var i = 0, len = s.length; i < len; i++){
12510             indexes.push(s[i].nodeIndex);
12511         }
12512         return indexes;
12513     },
12514
12515     /**
12516      * Clear all selections
12517      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12518      */
12519     clearSelections : function(suppressEvent){
12520         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12521             this.cmp.elements = this.selections;
12522             this.cmp.removeClass(this.selectedClass);
12523             this.selections = [];
12524             if(!suppressEvent){
12525                 this.fireEvent("selectionchange", this, this.selections);
12526             }
12527         }
12528     },
12529
12530     /**
12531      * Returns true if the passed node is selected
12532      * @param {HTMLElement/Number} node The node or node index
12533      * @return {Boolean}
12534      */
12535     isSelected : function(node){
12536         var s = this.selections;
12537         if(s.length < 1){
12538             return false;
12539         }
12540         node = this.getNode(node);
12541         return s.indexOf(node) !== -1;
12542     },
12543
12544     /**
12545      * Selects nodes.
12546      * @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
12547      * @param {Boolean} keepExisting (optional) true to keep existing selections
12548      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12549      */
12550     select : function(nodeInfo, keepExisting, suppressEvent){
12551         if(nodeInfo instanceof Array){
12552             if(!keepExisting){
12553                 this.clearSelections(true);
12554             }
12555             for(var i = 0, len = nodeInfo.length; i < len; i++){
12556                 this.select(nodeInfo[i], true, true);
12557             }
12558             return;
12559         } 
12560         var node = this.getNode(nodeInfo);
12561         if(!node || this.isSelected(node)){
12562             return; // already selected.
12563         }
12564         if(!keepExisting){
12565             this.clearSelections(true);
12566         }
12567         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12568             Roo.fly(node).addClass(this.selectedClass);
12569             this.selections.push(node);
12570             if(!suppressEvent){
12571                 this.fireEvent("selectionchange", this, this.selections);
12572             }
12573         }
12574         
12575         
12576     },
12577       /**
12578      * Unselects nodes.
12579      * @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
12580      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
12581      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12582      */
12583     unselect : function(nodeInfo, keepExisting, suppressEvent)
12584     {
12585         if(nodeInfo instanceof Array){
12586             Roo.each(this.selections, function(s) {
12587                 this.unselect(s, nodeInfo);
12588             }, this);
12589             return;
12590         }
12591         var node = this.getNode(nodeInfo);
12592         if(!node || !this.isSelected(node)){
12593             Roo.log("not selected");
12594             return; // not selected.
12595         }
12596         // fireevent???
12597         var ns = [];
12598         Roo.each(this.selections, function(s) {
12599             if (s == node ) {
12600                 Roo.fly(node).removeClass(this.selectedClass);
12601
12602                 return;
12603             }
12604             ns.push(s);
12605         },this);
12606         
12607         this.selections= ns;
12608         this.fireEvent("selectionchange", this, this.selections);
12609     },
12610
12611     /**
12612      * Gets a template node.
12613      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12614      * @return {HTMLElement} The node or null if it wasn't found
12615      */
12616     getNode : function(nodeInfo){
12617         if(typeof nodeInfo == "string"){
12618             return document.getElementById(nodeInfo);
12619         }else if(typeof nodeInfo == "number"){
12620             return this.nodes[nodeInfo];
12621         }
12622         return nodeInfo;
12623     },
12624
12625     /**
12626      * Gets a range template nodes.
12627      * @param {Number} startIndex
12628      * @param {Number} endIndex
12629      * @return {Array} An array of nodes
12630      */
12631     getNodes : function(start, end){
12632         var ns = this.nodes;
12633         start = start || 0;
12634         end = typeof end == "undefined" ? ns.length - 1 : end;
12635         var nodes = [];
12636         if(start <= end){
12637             for(var i = start; i <= end; i++){
12638                 nodes.push(ns[i]);
12639             }
12640         } else{
12641             for(var i = start; i >= end; i--){
12642                 nodes.push(ns[i]);
12643             }
12644         }
12645         return nodes;
12646     },
12647
12648     /**
12649      * Finds the index of the passed node
12650      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12651      * @return {Number} The index of the node or -1
12652      */
12653     indexOf : function(node){
12654         node = this.getNode(node);
12655         if(typeof node.nodeIndex == "number"){
12656             return node.nodeIndex;
12657         }
12658         var ns = this.nodes;
12659         for(var i = 0, len = ns.length; i < len; i++){
12660             if(ns[i] == node){
12661                 return i;
12662             }
12663         }
12664         return -1;
12665     }
12666 });
12667 /*
12668  * - LGPL
12669  *
12670  * based on jquery fullcalendar
12671  * 
12672  */
12673
12674 Roo.bootstrap = Roo.bootstrap || {};
12675 /**
12676  * @class Roo.bootstrap.Calendar
12677  * @extends Roo.bootstrap.Component
12678  * Bootstrap Calendar class
12679  * @cfg {Boolean} loadMask (true|false) default false
12680  * @cfg {Object} header generate the user specific header of the calendar, default false
12681
12682  * @constructor
12683  * Create a new Container
12684  * @param {Object} config The config object
12685  */
12686
12687
12688
12689 Roo.bootstrap.Calendar = function(config){
12690     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
12691      this.addEvents({
12692         /**
12693              * @event select
12694              * Fires when a date is selected
12695              * @param {DatePicker} this
12696              * @param {Date} date The selected date
12697              */
12698         'select': true,
12699         /**
12700              * @event monthchange
12701              * Fires when the displayed month changes 
12702              * @param {DatePicker} this
12703              * @param {Date} date The selected month
12704              */
12705         'monthchange': true,
12706         /**
12707              * @event evententer
12708              * Fires when mouse over an event
12709              * @param {Calendar} this
12710              * @param {event} Event
12711              */
12712         'evententer': true,
12713         /**
12714              * @event eventleave
12715              * Fires when the mouse leaves an
12716              * @param {Calendar} this
12717              * @param {event}
12718              */
12719         'eventleave': true,
12720         /**
12721              * @event eventclick
12722              * Fires when the mouse click an
12723              * @param {Calendar} this
12724              * @param {event}
12725              */
12726         'eventclick': true
12727         
12728     });
12729
12730 };
12731
12732 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
12733     
12734      /**
12735      * @cfg {Number} startDay
12736      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
12737      */
12738     startDay : 0,
12739     
12740     loadMask : false,
12741     
12742     header : false,
12743       
12744     getAutoCreate : function(){
12745         
12746         
12747         var fc_button = function(name, corner, style, content ) {
12748             return Roo.apply({},{
12749                 tag : 'span',
12750                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
12751                          (corner.length ?
12752                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
12753                             ''
12754                         ),
12755                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
12756                 unselectable: 'on'
12757             });
12758         };
12759         
12760         var header = {};
12761         
12762         if(!this.header){
12763             header = {
12764                 tag : 'table',
12765                 cls : 'fc-header',
12766                 style : 'width:100%',
12767                 cn : [
12768                     {
12769                         tag: 'tr',
12770                         cn : [
12771                             {
12772                                 tag : 'td',
12773                                 cls : 'fc-header-left',
12774                                 cn : [
12775                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
12776                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
12777                                     { tag: 'span', cls: 'fc-header-space' },
12778                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
12779
12780
12781                                 ]
12782                             },
12783
12784                             {
12785                                 tag : 'td',
12786                                 cls : 'fc-header-center',
12787                                 cn : [
12788                                     {
12789                                         tag: 'span',
12790                                         cls: 'fc-header-title',
12791                                         cn : {
12792                                             tag: 'H2',
12793                                             html : 'month / year'
12794                                         }
12795                                     }
12796
12797                                 ]
12798                             },
12799                             {
12800                                 tag : 'td',
12801                                 cls : 'fc-header-right',
12802                                 cn : [
12803                               /*      fc_button('month', 'left', '', 'month' ),
12804                                     fc_button('week', '', '', 'week' ),
12805                                     fc_button('day', 'right', '', 'day' )
12806                                 */    
12807
12808                                 ]
12809                             }
12810
12811                         ]
12812                     }
12813                 ]
12814             };
12815         }
12816         
12817         header = this.header;
12818         
12819        
12820         var cal_heads = function() {
12821             var ret = [];
12822             // fixme - handle this.
12823             
12824             for (var i =0; i < Date.dayNames.length; i++) {
12825                 var d = Date.dayNames[i];
12826                 ret.push({
12827                     tag: 'th',
12828                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
12829                     html : d.substring(0,3)
12830                 });
12831                 
12832             }
12833             ret[0].cls += ' fc-first';
12834             ret[6].cls += ' fc-last';
12835             return ret;
12836         };
12837         var cal_cell = function(n) {
12838             return  {
12839                 tag: 'td',
12840                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
12841                 cn : [
12842                     {
12843                         cn : [
12844                             {
12845                                 cls: 'fc-day-number',
12846                                 html: 'D'
12847                             },
12848                             {
12849                                 cls: 'fc-day-content',
12850                              
12851                                 cn : [
12852                                      {
12853                                         style: 'position: relative;' // height: 17px;
12854                                     }
12855                                 ]
12856                             }
12857                             
12858                             
12859                         ]
12860                     }
12861                 ]
12862                 
12863             }
12864         };
12865         var cal_rows = function() {
12866             
12867             var ret = []
12868             for (var r = 0; r < 6; r++) {
12869                 var row= {
12870                     tag : 'tr',
12871                     cls : 'fc-week',
12872                     cn : []
12873                 };
12874                 
12875                 for (var i =0; i < Date.dayNames.length; i++) {
12876                     var d = Date.dayNames[i];
12877                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
12878
12879                 }
12880                 row.cn[0].cls+=' fc-first';
12881                 row.cn[0].cn[0].style = 'min-height:90px';
12882                 row.cn[6].cls+=' fc-last';
12883                 ret.push(row);
12884                 
12885             }
12886             ret[0].cls += ' fc-first';
12887             ret[4].cls += ' fc-prev-last';
12888             ret[5].cls += ' fc-last';
12889             return ret;
12890             
12891         };
12892         
12893         var cal_table = {
12894             tag: 'table',
12895             cls: 'fc-border-separate',
12896             style : 'width:100%',
12897             cellspacing  : 0,
12898             cn : [
12899                 { 
12900                     tag: 'thead',
12901                     cn : [
12902                         { 
12903                             tag: 'tr',
12904                             cls : 'fc-first fc-last',
12905                             cn : cal_heads()
12906                         }
12907                     ]
12908                 },
12909                 { 
12910                     tag: 'tbody',
12911                     cn : cal_rows()
12912                 }
12913                   
12914             ]
12915         };
12916          
12917          var cfg = {
12918             cls : 'fc fc-ltr',
12919             cn : [
12920                 header,
12921                 {
12922                     cls : 'fc-content',
12923                     style : "position: relative;",
12924                     cn : [
12925                         {
12926                             cls : 'fc-view fc-view-month fc-grid',
12927                             style : 'position: relative',
12928                             unselectable : 'on',
12929                             cn : [
12930                                 {
12931                                     cls : 'fc-event-container',
12932                                     style : 'position:absolute;z-index:8;top:0;left:0;'
12933                                 },
12934                                 cal_table
12935                             ]
12936                         }
12937                     ]
12938     
12939                 }
12940            ] 
12941             
12942         };
12943         
12944          
12945         
12946         return cfg;
12947     },
12948     
12949     
12950     initEvents : function()
12951     {
12952         if(!this.store){
12953             throw "can not find store for calendar";
12954         }
12955         
12956         var mark = {
12957             tag: "div",
12958             cls:"x-dlg-mask",
12959             style: "text-align:center",
12960             cn: [
12961                 {
12962                     tag: "div",
12963                     style: "background-color:white;width:50%;margin:250 auto",
12964                     cn: [
12965                         {
12966                             tag: "img",
12967                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
12968                         },
12969                         {
12970                             tag: "span",
12971                             html: "Loading"
12972                         }
12973                         
12974                     ]
12975                 }
12976             ]
12977         }
12978         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
12979         
12980         var size = this.el.select('.fc-content', true).first().getSize();
12981         this.maskEl.setSize(size.width, size.height);
12982         this.maskEl.enableDisplayMode("block");
12983         if(!this.loadMask){
12984             this.maskEl.hide();
12985         }
12986         
12987         this.store = Roo.factory(this.store, Roo.data);
12988         this.store.on('load', this.onLoad, this);
12989         this.store.on('beforeload', this.onBeforeLoad, this);
12990         
12991         this.resize();
12992         
12993         this.cells = this.el.select('.fc-day',true);
12994         //Roo.log(this.cells);
12995         this.textNodes = this.el.query('.fc-day-number');
12996         this.cells.addClassOnOver('fc-state-hover');
12997         
12998         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
12999         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
13000         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
13001         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
13002         
13003         this.on('monthchange', this.onMonthChange, this);
13004         
13005         this.update(new Date().clearTime());
13006     },
13007     
13008     resize : function() {
13009         var sz  = this.el.getSize();
13010         
13011         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
13012         this.el.select('.fc-day-content div',true).setHeight(34);
13013     },
13014     
13015     
13016     // private
13017     showPrevMonth : function(e){
13018         this.update(this.activeDate.add("mo", -1));
13019     },
13020     showToday : function(e){
13021         this.update(new Date().clearTime());
13022     },
13023     // private
13024     showNextMonth : function(e){
13025         this.update(this.activeDate.add("mo", 1));
13026     },
13027
13028     // private
13029     showPrevYear : function(){
13030         this.update(this.activeDate.add("y", -1));
13031     },
13032
13033     // private
13034     showNextYear : function(){
13035         this.update(this.activeDate.add("y", 1));
13036     },
13037
13038     
13039    // private
13040     update : function(date)
13041     {
13042         var vd = this.activeDate;
13043         this.activeDate = date;
13044 //        if(vd && this.el){
13045 //            var t = date.getTime();
13046 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
13047 //                Roo.log('using add remove');
13048 //                
13049 //                this.fireEvent('monthchange', this, date);
13050 //                
13051 //                this.cells.removeClass("fc-state-highlight");
13052 //                this.cells.each(function(c){
13053 //                   if(c.dateValue == t){
13054 //                       c.addClass("fc-state-highlight");
13055 //                       setTimeout(function(){
13056 //                            try{c.dom.firstChild.focus();}catch(e){}
13057 //                       }, 50);
13058 //                       return false;
13059 //                   }
13060 //                   return true;
13061 //                });
13062 //                return;
13063 //            }
13064 //        }
13065         
13066         var days = date.getDaysInMonth();
13067         
13068         var firstOfMonth = date.getFirstDateOfMonth();
13069         var startingPos = firstOfMonth.getDay()-this.startDay;
13070         
13071         if(startingPos < this.startDay){
13072             startingPos += 7;
13073         }
13074         
13075         var pm = date.add(Date.MONTH, -1);
13076         var prevStart = pm.getDaysInMonth()-startingPos;
13077 //        
13078         this.cells = this.el.select('.fc-day',true);
13079         this.textNodes = this.el.query('.fc-day-number');
13080         this.cells.addClassOnOver('fc-state-hover');
13081         
13082         var cells = this.cells.elements;
13083         var textEls = this.textNodes;
13084         
13085         Roo.each(cells, function(cell){
13086             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
13087         });
13088         
13089         days += startingPos;
13090
13091         // convert everything to numbers so it's fast
13092         var day = 86400000;
13093         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
13094         //Roo.log(d);
13095         //Roo.log(pm);
13096         //Roo.log(prevStart);
13097         
13098         var today = new Date().clearTime().getTime();
13099         var sel = date.clearTime().getTime();
13100         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
13101         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
13102         var ddMatch = this.disabledDatesRE;
13103         var ddText = this.disabledDatesText;
13104         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
13105         var ddaysText = this.disabledDaysText;
13106         var format = this.format;
13107         
13108         var setCellClass = function(cal, cell){
13109             cell.row = 0;
13110             cell.events = [];
13111             cell.more = [];
13112             //Roo.log('set Cell Class');
13113             cell.title = "";
13114             var t = d.getTime();
13115             
13116             //Roo.log(d);
13117             
13118             cell.dateValue = t;
13119             if(t == today){
13120                 cell.className += " fc-today";
13121                 cell.className += " fc-state-highlight";
13122                 cell.title = cal.todayText;
13123             }
13124             if(t == sel){
13125                 // disable highlight in other month..
13126                 //cell.className += " fc-state-highlight";
13127                 
13128             }
13129             // disabling
13130             if(t < min) {
13131                 cell.className = " fc-state-disabled";
13132                 cell.title = cal.minText;
13133                 return;
13134             }
13135             if(t > max) {
13136                 cell.className = " fc-state-disabled";
13137                 cell.title = cal.maxText;
13138                 return;
13139             }
13140             if(ddays){
13141                 if(ddays.indexOf(d.getDay()) != -1){
13142                     cell.title = ddaysText;
13143                     cell.className = " fc-state-disabled";
13144                 }
13145             }
13146             if(ddMatch && format){
13147                 var fvalue = d.dateFormat(format);
13148                 if(ddMatch.test(fvalue)){
13149                     cell.title = ddText.replace("%0", fvalue);
13150                     cell.className = " fc-state-disabled";
13151                 }
13152             }
13153             
13154             if (!cell.initialClassName) {
13155                 cell.initialClassName = cell.dom.className;
13156             }
13157             
13158             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
13159         };
13160
13161         var i = 0;
13162         
13163         for(; i < startingPos; i++) {
13164             textEls[i].innerHTML = (++prevStart);
13165             d.setDate(d.getDate()+1);
13166             
13167             cells[i].className = "fc-past fc-other-month";
13168             setCellClass(this, cells[i]);
13169         }
13170         
13171         var intDay = 0;
13172         
13173         for(; i < days; i++){
13174             intDay = i - startingPos + 1;
13175             textEls[i].innerHTML = (intDay);
13176             d.setDate(d.getDate()+1);
13177             
13178             cells[i].className = ''; // "x-date-active";
13179             setCellClass(this, cells[i]);
13180         }
13181         var extraDays = 0;
13182         
13183         for(; i < 42; i++) {
13184             textEls[i].innerHTML = (++extraDays);
13185             d.setDate(d.getDate()+1);
13186             
13187             cells[i].className = "fc-future fc-other-month";
13188             setCellClass(this, cells[i]);
13189         }
13190         
13191         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13192         
13193         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13194         
13195         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13196         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13197         
13198         if(totalRows != 6){
13199             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13200             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13201         }
13202         
13203         this.fireEvent('monthchange', this, date);
13204         
13205         
13206         /*
13207         if(!this.internalRender){
13208             var main = this.el.dom.firstChild;
13209             var w = main.offsetWidth;
13210             this.el.setWidth(w + this.el.getBorderWidth("lr"));
13211             Roo.fly(main).setWidth(w);
13212             this.internalRender = true;
13213             // opera does not respect the auto grow header center column
13214             // then, after it gets a width opera refuses to recalculate
13215             // without a second pass
13216             if(Roo.isOpera && !this.secondPass){
13217                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13218                 this.secondPass = true;
13219                 this.update.defer(10, this, [date]);
13220             }
13221         }
13222         */
13223         
13224     },
13225     
13226     findCell : function(dt) {
13227         dt = dt.clearTime().getTime();
13228         var ret = false;
13229         this.cells.each(function(c){
13230             //Roo.log("check " +c.dateValue + '?=' + dt);
13231             if(c.dateValue == dt){
13232                 ret = c;
13233                 return false;
13234             }
13235             return true;
13236         });
13237         
13238         return ret;
13239     },
13240     
13241     findCells : function(ev) {
13242         var s = ev.start.clone().clearTime().getTime();
13243        // Roo.log(s);
13244         var e= ev.end.clone().clearTime().getTime();
13245        // Roo.log(e);
13246         var ret = [];
13247         this.cells.each(function(c){
13248              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13249             
13250             if(c.dateValue > e){
13251                 return ;
13252             }
13253             if(c.dateValue < s){
13254                 return ;
13255             }
13256             ret.push(c);
13257         });
13258         
13259         return ret;    
13260     },
13261     
13262 //    findBestRow: function(cells)
13263 //    {
13264 //        var ret = 0;
13265 //        
13266 //        for (var i =0 ; i < cells.length;i++) {
13267 //            ret  = Math.max(cells[i].rows || 0,ret);
13268 //        }
13269 //        return ret;
13270 //        
13271 //    },
13272     
13273     
13274     addItem : function(ev)
13275     {
13276         // look for vertical location slot in
13277         var cells = this.findCells(ev);
13278         
13279 //        ev.row = this.findBestRow(cells);
13280         
13281         // work out the location.
13282         
13283         var crow = false;
13284         var rows = [];
13285         for(var i =0; i < cells.length; i++) {
13286             
13287             cells[i].row = cells[0].row;
13288             
13289             if(i == 0){
13290                 cells[i].row = cells[i].row + 1;
13291             }
13292             
13293             if (!crow) {
13294                 crow = {
13295                     start : cells[i],
13296                     end :  cells[i]
13297                 };
13298                 continue;
13299             }
13300             if (crow.start.getY() == cells[i].getY()) {
13301                 // on same row.
13302                 crow.end = cells[i];
13303                 continue;
13304             }
13305             // different row.
13306             rows.push(crow);
13307             crow = {
13308                 start: cells[i],
13309                 end : cells[i]
13310             };
13311             
13312         }
13313         
13314         rows.push(crow);
13315         ev.els = [];
13316         ev.rows = rows;
13317         ev.cells = cells;
13318         
13319         cells[0].events.push(ev);
13320         
13321         this.calevents.push(ev);
13322     },
13323     
13324     clearEvents: function() {
13325         
13326         if(!this.calevents){
13327             return;
13328         }
13329         
13330         Roo.each(this.cells.elements, function(c){
13331             c.row = 0;
13332             c.events = [];
13333             c.more = [];
13334         });
13335         
13336         Roo.each(this.calevents, function(e) {
13337             Roo.each(e.els, function(el) {
13338                 el.un('mouseenter' ,this.onEventEnter, this);
13339                 el.un('mouseleave' ,this.onEventLeave, this);
13340                 el.remove();
13341             },this);
13342         },this);
13343         
13344         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13345             e.remove();
13346         });
13347         
13348     },
13349     
13350     renderEvents: function()
13351     {   
13352         var _this = this;
13353         
13354         this.cells.each(function(c) {
13355             
13356             if(c.row < 5){
13357                 return;
13358             }
13359             
13360             var ev = c.events;
13361             
13362             var r = 4;
13363             if(c.row != c.events.length){
13364                 r = 4 - (4 - (c.row - c.events.length));
13365             }
13366             
13367             c.events = ev.slice(0, r);
13368             c.more = ev.slice(r);
13369             
13370             if(c.more.length && c.more.length == 1){
13371                 c.events.push(c.more.pop());
13372             }
13373             
13374             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13375             
13376         });
13377             
13378         this.cells.each(function(c) {
13379             
13380             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13381             
13382             
13383             for (var e = 0; e < c.events.length; e++){
13384                 var ev = c.events[e];
13385                 var rows = ev.rows;
13386                 
13387                 for(var i = 0; i < rows.length; i++) {
13388                 
13389                     // how many rows should it span..
13390
13391                     var  cfg = {
13392                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13393                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13394
13395                         unselectable : "on",
13396                         cn : [
13397                             {
13398                                 cls: 'fc-event-inner',
13399                                 cn : [
13400     //                                {
13401     //                                  tag:'span',
13402     //                                  cls: 'fc-event-time',
13403     //                                  html : cells.length > 1 ? '' : ev.time
13404     //                                },
13405                                     {
13406                                       tag:'span',
13407                                       cls: 'fc-event-title',
13408                                       html : String.format('{0}', ev.title)
13409                                     }
13410
13411
13412                                 ]
13413                             },
13414                             {
13415                                 cls: 'ui-resizable-handle ui-resizable-e',
13416                                 html : '&nbsp;&nbsp;&nbsp'
13417                             }
13418
13419                         ]
13420                     };
13421
13422                     if (i == 0) {
13423                         cfg.cls += ' fc-event-start';
13424                     }
13425                     if ((i+1) == rows.length) {
13426                         cfg.cls += ' fc-event-end';
13427                     }
13428
13429                     var ctr = _this.el.select('.fc-event-container',true).first();
13430                     var cg = ctr.createChild(cfg);
13431
13432                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13433                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13434
13435                     var r = (c.more.length) ? 1 : 0;
13436                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
13437                     cg.setWidth(ebox.right - sbox.x -2);
13438
13439                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13440                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13441                     cg.on('click', _this.onEventClick, _this, ev);
13442
13443                     ev.els.push(cg);
13444                     
13445                 }
13446                 
13447             }
13448             
13449             
13450             if(c.more.length){
13451                 var  cfg = {
13452                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13453                     style : 'position: absolute',
13454                     unselectable : "on",
13455                     cn : [
13456                         {
13457                             cls: 'fc-event-inner',
13458                             cn : [
13459                                 {
13460                                   tag:'span',
13461                                   cls: 'fc-event-title',
13462                                   html : 'More'
13463                                 }
13464
13465
13466                             ]
13467                         },
13468                         {
13469                             cls: 'ui-resizable-handle ui-resizable-e',
13470                             html : '&nbsp;&nbsp;&nbsp'
13471                         }
13472
13473                     ]
13474                 };
13475
13476                 var ctr = _this.el.select('.fc-event-container',true).first();
13477                 var cg = ctr.createChild(cfg);
13478
13479                 var sbox = c.select('.fc-day-content',true).first().getBox();
13480                 var ebox = c.select('.fc-day-content',true).first().getBox();
13481                 //Roo.log(cg);
13482                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
13483                 cg.setWidth(ebox.right - sbox.x -2);
13484
13485                 cg.on('click', _this.onMoreEventClick, _this, c.more);
13486                 
13487             }
13488             
13489         });
13490         
13491         
13492         
13493     },
13494     
13495     onEventEnter: function (e, el,event,d) {
13496         this.fireEvent('evententer', this, el, event);
13497     },
13498     
13499     onEventLeave: function (e, el,event,d) {
13500         this.fireEvent('eventleave', this, el, event);
13501     },
13502     
13503     onEventClick: function (e, el,event,d) {
13504         this.fireEvent('eventclick', this, el, event);
13505     },
13506     
13507     onMonthChange: function () {
13508         this.store.load();
13509     },
13510     
13511     onMoreEventClick: function(e, el, more)
13512     {
13513         var _this = this;
13514         
13515         this.calpopover.placement = 'right';
13516         this.calpopover.setTitle('More');
13517         
13518         this.calpopover.setContent('');
13519         
13520         var ctr = this.calpopover.el.select('.popover-content', true).first();
13521         
13522         Roo.each(more, function(m){
13523             var cfg = {
13524                 cls : 'fc-event-hori fc-event-draggable',
13525                 html : m.title
13526             }
13527             var cg = ctr.createChild(cfg);
13528             
13529             cg.on('click', _this.onEventClick, _this, m);
13530         });
13531         
13532         this.calpopover.show(el);
13533         
13534         
13535     },
13536     
13537     onLoad: function () 
13538     {   
13539         this.calevents = [];
13540         var cal = this;
13541         
13542         if(this.store.getCount() > 0){
13543             this.store.data.each(function(d){
13544                cal.addItem({
13545                     id : d.data.id,
13546                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13547                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13548                     time : d.data.start_time,
13549                     title : d.data.title,
13550                     description : d.data.description,
13551                     venue : d.data.venue
13552                 });
13553             });
13554         }
13555         
13556         this.renderEvents();
13557         
13558         if(this.calevents.length && this.loadMask){
13559             this.maskEl.hide();
13560         }
13561     },
13562     
13563     onBeforeLoad: function()
13564     {
13565         this.clearEvents();
13566         if(this.loadMask){
13567             this.maskEl.show();
13568         }
13569     }
13570 });
13571
13572  
13573  /*
13574  * - LGPL
13575  *
13576  * element
13577  * 
13578  */
13579
13580 /**
13581  * @class Roo.bootstrap.Popover
13582  * @extends Roo.bootstrap.Component
13583  * Bootstrap Popover class
13584  * @cfg {String} html contents of the popover   (or false to use children..)
13585  * @cfg {String} title of popover (or false to hide)
13586  * @cfg {String} placement how it is placed
13587  * @cfg {String} trigger click || hover (or false to trigger manually)
13588  * @cfg {String} over what (parent or false to trigger manually.)
13589  * 
13590  * @constructor
13591  * Create a new Popover
13592  * @param {Object} config The config object
13593  */
13594
13595 Roo.bootstrap.Popover = function(config){
13596     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
13597 };
13598
13599 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
13600     
13601     title: 'Fill in a title',
13602     html: false,
13603     
13604     placement : 'right',
13605     trigger : 'hover', // hover
13606     
13607     over: 'parent',
13608     
13609     can_build_overlaid : false,
13610     
13611     getChildContainer : function()
13612     {
13613         return this.el.select('.popover-content',true).first();
13614     },
13615     
13616     getAutoCreate : function(){
13617          Roo.log('make popover?');
13618         var cfg = {
13619            cls : 'popover roo-dynamic',
13620            style: 'display:block',
13621            cn : [
13622                 {
13623                     cls : 'arrow'
13624                 },
13625                 {
13626                     cls : 'popover-inner',
13627                     cn : [
13628                         {
13629                             tag: 'h3',
13630                             cls: 'popover-title',
13631                             html : this.title
13632                         },
13633                         {
13634                             cls : 'popover-content',
13635                             html : this.html
13636                         }
13637                     ]
13638                     
13639                 }
13640            ]
13641         };
13642         
13643         return cfg;
13644     },
13645     setTitle: function(str)
13646     {
13647         this.el.select('.popover-title',true).first().dom.innerHTML = str;
13648     },
13649     setContent: function(str)
13650     {
13651         this.el.select('.popover-content',true).first().dom.innerHTML = str;
13652     },
13653     // as it get's added to the bottom of the page.
13654     onRender : function(ct, position)
13655     {
13656         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
13657         if(!this.el){
13658             var cfg = Roo.apply({},  this.getAutoCreate());
13659             cfg.id = Roo.id();
13660             
13661             if (this.cls) {
13662                 cfg.cls += ' ' + this.cls;
13663             }
13664             if (this.style) {
13665                 cfg.style = this.style;
13666             }
13667             Roo.log("adding to ")
13668             this.el = Roo.get(document.body).createChild(cfg, position);
13669             Roo.log(this.el);
13670         }
13671         this.initEvents();
13672     },
13673     
13674     initEvents : function()
13675     {
13676         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
13677         this.el.enableDisplayMode('block');
13678         this.el.hide();
13679         if (this.over === false) {
13680             return; 
13681         }
13682         if (this.triggers === false) {
13683             return;
13684         }
13685         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13686         var triggers = this.trigger ? this.trigger.split(' ') : [];
13687         Roo.each(triggers, function(trigger) {
13688         
13689             if (trigger == 'click') {
13690                 on_el.on('click', this.toggle, this);
13691             } else if (trigger != 'manual') {
13692                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
13693                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
13694       
13695                 on_el.on(eventIn  ,this.enter, this);
13696                 on_el.on(eventOut, this.leave, this);
13697             }
13698         }, this);
13699         
13700     },
13701     
13702     
13703     // private
13704     timeout : null,
13705     hoverState : null,
13706     
13707     toggle : function () {
13708         this.hoverState == 'in' ? this.leave() : this.enter();
13709     },
13710     
13711     enter : function () {
13712        
13713     
13714         clearTimeout(this.timeout);
13715     
13716         this.hoverState = 'in'
13717     
13718         if (!this.delay || !this.delay.show) {
13719             this.show();
13720             return 
13721         }
13722         var _t = this;
13723         this.timeout = setTimeout(function () {
13724             if (_t.hoverState == 'in') {
13725                 _t.show();
13726             }
13727         }, this.delay.show)
13728     },
13729     leave : function() {
13730         clearTimeout(this.timeout);
13731     
13732         this.hoverState = 'out'
13733     
13734         if (!this.delay || !this.delay.hide) {
13735             this.hide();
13736             return 
13737         }
13738         var _t = this;
13739         this.timeout = setTimeout(function () {
13740             if (_t.hoverState == 'out') {
13741                 _t.hide();
13742             }
13743         }, this.delay.hide)
13744     },
13745     
13746     show : function (on_el)
13747     {
13748         if (!on_el) {
13749             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13750         }
13751         // set content.
13752         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
13753         if (this.html !== false) {
13754             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
13755         }
13756         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
13757         if (!this.title.length) {
13758             this.el.select('.popover-title',true).hide();
13759         }
13760         
13761         var placement = typeof this.placement == 'function' ?
13762             this.placement.call(this, this.el, on_el) :
13763             this.placement;
13764             
13765         var autoToken = /\s?auto?\s?/i;
13766         var autoPlace = autoToken.test(placement);
13767         if (autoPlace) {
13768             placement = placement.replace(autoToken, '') || 'top';
13769         }
13770         
13771         //this.el.detach()
13772         //this.el.setXY([0,0]);
13773         this.el.show();
13774         this.el.dom.style.display='block';
13775         this.el.addClass(placement);
13776         
13777         //this.el.appendTo(on_el);
13778         
13779         var p = this.getPosition();
13780         var box = this.el.getBox();
13781         
13782         if (autoPlace) {
13783             // fixme..
13784         }
13785         var align = Roo.bootstrap.Popover.alignment[placement]
13786         this.el.alignTo(on_el, align[0],align[1]);
13787         //var arrow = this.el.select('.arrow',true).first();
13788         //arrow.set(align[2], 
13789         
13790         this.el.addClass('in');
13791         this.hoverState = null;
13792         
13793         if (this.el.hasClass('fade')) {
13794             // fade it?
13795         }
13796         
13797     },
13798     hide : function()
13799     {
13800         this.el.setXY([0,0]);
13801         this.el.removeClass('in');
13802         this.el.hide();
13803         
13804     }
13805     
13806 });
13807
13808 Roo.bootstrap.Popover.alignment = {
13809     'left' : ['r-l', [-10,0], 'right'],
13810     'right' : ['l-r', [10,0], 'left'],
13811     'bottom' : ['t-b', [0,10], 'top'],
13812     'top' : [ 'b-t', [0,-10], 'bottom']
13813 };
13814
13815  /*
13816  * - LGPL
13817  *
13818  * Progress
13819  * 
13820  */
13821
13822 /**
13823  * @class Roo.bootstrap.Progress
13824  * @extends Roo.bootstrap.Component
13825  * Bootstrap Progress class
13826  * @cfg {Boolean} striped striped of the progress bar
13827  * @cfg {Boolean} active animated of the progress bar
13828  * 
13829  * 
13830  * @constructor
13831  * Create a new Progress
13832  * @param {Object} config The config object
13833  */
13834
13835 Roo.bootstrap.Progress = function(config){
13836     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
13837 };
13838
13839 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
13840     
13841     striped : false,
13842     active: false,
13843     
13844     getAutoCreate : function(){
13845         var cfg = {
13846             tag: 'div',
13847             cls: 'progress'
13848         };
13849         
13850         
13851         if(this.striped){
13852             cfg.cls += ' progress-striped';
13853         }
13854       
13855         if(this.active){
13856             cfg.cls += ' active';
13857         }
13858         
13859         
13860         return cfg;
13861     }
13862    
13863 });
13864
13865  
13866
13867  /*
13868  * - LGPL
13869  *
13870  * ProgressBar
13871  * 
13872  */
13873
13874 /**
13875  * @class Roo.bootstrap.ProgressBar
13876  * @extends Roo.bootstrap.Component
13877  * Bootstrap ProgressBar class
13878  * @cfg {Number} aria_valuenow aria-value now
13879  * @cfg {Number} aria_valuemin aria-value min
13880  * @cfg {Number} aria_valuemax aria-value max
13881  * @cfg {String} label label for the progress bar
13882  * @cfg {String} panel (success | info | warning | danger )
13883  * @cfg {String} role role of the progress bar
13884  * @cfg {String} sr_only text
13885  * 
13886  * 
13887  * @constructor
13888  * Create a new ProgressBar
13889  * @param {Object} config The config object
13890  */
13891
13892 Roo.bootstrap.ProgressBar = function(config){
13893     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
13894 };
13895
13896 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
13897     
13898     aria_valuenow : 0,
13899     aria_valuemin : 0,
13900     aria_valuemax : 100,
13901     label : false,
13902     panel : false,
13903     role : false,
13904     sr_only: false,
13905     
13906     getAutoCreate : function()
13907     {
13908         
13909         var cfg = {
13910             tag: 'div',
13911             cls: 'progress-bar',
13912             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
13913         };
13914         
13915         if(this.sr_only){
13916             cfg.cn = {
13917                 tag: 'span',
13918                 cls: 'sr-only',
13919                 html: this.sr_only
13920             }
13921         }
13922         
13923         if(this.role){
13924             cfg.role = this.role;
13925         }
13926         
13927         if(this.aria_valuenow){
13928             cfg['aria-valuenow'] = this.aria_valuenow;
13929         }
13930         
13931         if(this.aria_valuemin){
13932             cfg['aria-valuemin'] = this.aria_valuemin;
13933         }
13934         
13935         if(this.aria_valuemax){
13936             cfg['aria-valuemax'] = this.aria_valuemax;
13937         }
13938         
13939         if(this.label && !this.sr_only){
13940             cfg.html = this.label;
13941         }
13942         
13943         if(this.panel){
13944             cfg.cls += ' progress-bar-' + this.panel;
13945         }
13946         
13947         return cfg;
13948     },
13949     
13950     update : function(aria_valuenow)
13951     {
13952         this.aria_valuenow = aria_valuenow;
13953         
13954         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
13955     }
13956    
13957 });
13958
13959  
13960
13961  /*
13962  * - LGPL
13963  *
13964  * column
13965  * 
13966  */
13967
13968 /**
13969  * @class Roo.bootstrap.TabGroup
13970  * @extends Roo.bootstrap.Column
13971  * Bootstrap Column class
13972  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
13973  * @cfg {Boolean} carousel true to make the group behave like a carousel
13974  * 
13975  * @constructor
13976  * Create a new TabGroup
13977  * @param {Object} config The config object
13978  */
13979
13980 Roo.bootstrap.TabGroup = function(config){
13981     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
13982     if (!this.navId) {
13983         this.navId = Roo.id();
13984     }
13985     this.tabs = [];
13986     Roo.bootstrap.TabGroup.register(this);
13987     
13988 };
13989
13990 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
13991     
13992     carousel : false,
13993     transition : false,
13994      
13995     getAutoCreate : function()
13996     {
13997         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
13998         
13999         cfg.cls += ' tab-content';
14000         
14001         if (this.carousel) {
14002             cfg.cls += ' carousel slide';
14003             cfg.cn = [{
14004                cls : 'carousel-inner'
14005             }]
14006         }
14007         
14008         
14009         return cfg;
14010     },
14011     getChildContainer : function()
14012     {
14013         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
14014     },
14015     
14016     /**
14017     * register a Navigation item
14018     * @param {Roo.bootstrap.NavItem} the navitem to add
14019     */
14020     register : function(item)
14021     {
14022         this.tabs.push( item);
14023         item.navId = this.navId; // not really needed..
14024     
14025     },
14026     
14027     getActivePanel : function()
14028     {
14029         var r = false;
14030         Roo.each(this.tabs, function(t) {
14031             if (t.active) {
14032                 r = t;
14033                 return false;
14034             }
14035             return null;
14036         });
14037         return r;
14038         
14039     },
14040     getPanelByName : function(n)
14041     {
14042         var r = false;
14043         Roo.each(this.tabs, function(t) {
14044             if (t.tabId == n) {
14045                 r = t;
14046                 return false;
14047             }
14048             return null;
14049         });
14050         return r;
14051     },
14052     indexOfPanel : function(p)
14053     {
14054         var r = false;
14055         Roo.each(this.tabs, function(t,i) {
14056             if (t.tabId == p.tabId) {
14057                 r = i;
14058                 return false;
14059             }
14060             return null;
14061         });
14062         return r;
14063     },
14064     /**
14065      * show a specific panel
14066      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
14067      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
14068      */
14069     showPanel : function (pan)
14070     {
14071         
14072         if (typeof(pan) == 'number') {
14073             pan = this.tabs[pan];
14074         }
14075         if (typeof(pan) == 'string') {
14076             pan = this.getPanelByName(pan);
14077         }
14078         if (pan.tabId == this.getActivePanel().tabId) {
14079             return true;
14080         }
14081         var cur = this.getActivePanel();
14082         
14083         if (false === cur.fireEvent('beforedeactivate')) {
14084             return false;
14085         }
14086         
14087         if (this.carousel) {
14088             this.transition = true;
14089             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
14090             var lr = dir == 'next' ? 'left' : 'right';
14091             pan.el.addClass(dir); // or prev
14092             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14093             cur.el.addClass(lr); // or right
14094             pan.el.addClass(lr);
14095             
14096             var _this = this;
14097             cur.el.on('transitionend', function() {
14098                 Roo.log("trans end?");
14099                 
14100                 pan.el.removeClass([lr,dir]);
14101                 pan.setActive(true);
14102                 
14103                 cur.el.removeClass([lr]);
14104                 cur.setActive(false);
14105                 
14106                 _this.transition = false;
14107                 
14108             }, this, { single:  true } );
14109             return true;
14110         }
14111         
14112         cur.setActive(false);
14113         pan.setActive(true);
14114         return true;
14115         
14116     },
14117     showPanelNext : function()
14118     {
14119         var i = this.indexOfPanel(this.getActivePanel());
14120         if (i > this.tabs.length) {
14121             return;
14122         }
14123         this.showPanel(this.tabs[i+1]);
14124     },
14125     showPanelPrev : function()
14126     {
14127         var i = this.indexOfPanel(this.getActivePanel());
14128         if (i  < 1) {
14129             return;
14130         }
14131         this.showPanel(this.tabs[i-1]);
14132     }
14133     
14134     
14135   
14136 });
14137
14138  
14139
14140  
14141  
14142 Roo.apply(Roo.bootstrap.TabGroup, {
14143     
14144     groups: {},
14145      /**
14146     * register a Navigation Group
14147     * @param {Roo.bootstrap.NavGroup} the navgroup to add
14148     */
14149     register : function(navgrp)
14150     {
14151         this.groups[navgrp.navId] = navgrp;
14152         
14153     },
14154     /**
14155     * fetch a Navigation Group based on the navigation ID
14156     * if one does not exist , it will get created.
14157     * @param {string} the navgroup to add
14158     * @returns {Roo.bootstrap.NavGroup} the navgroup 
14159     */
14160     get: function(navId) {
14161         if (typeof(this.groups[navId]) == 'undefined') {
14162             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
14163         }
14164         return this.groups[navId] ;
14165     }
14166     
14167     
14168     
14169 });
14170
14171  /*
14172  * - LGPL
14173  *
14174  * TabPanel
14175  * 
14176  */
14177
14178 /**
14179  * @class Roo.bootstrap.TabPanel
14180  * @extends Roo.bootstrap.Component
14181  * Bootstrap TabPanel class
14182  * @cfg {Boolean} active panel active
14183  * @cfg {String} html panel content
14184  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14185  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14186  * 
14187  * 
14188  * @constructor
14189  * Create a new TabPanel
14190  * @param {Object} config The config object
14191  */
14192
14193 Roo.bootstrap.TabPanel = function(config){
14194     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14195     this.addEvents({
14196         /**
14197              * @event changed
14198              * Fires when the active status changes
14199              * @param {Roo.bootstrap.TabPanel} this
14200              * @param {Boolean} state the new state
14201             
14202          */
14203         'changed': true,
14204         /**
14205              * @event beforedeactivate
14206              * Fires before a tab is de-activated - can be used to do validation on a form.
14207              * @param {Roo.bootstrap.TabPanel} this
14208              * @return {Boolean} false if there is an error
14209             
14210          */
14211         'beforedeactivate': true
14212      });
14213     
14214     this.tabId = this.tabId || Roo.id();
14215   
14216 };
14217
14218 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
14219     
14220     active: false,
14221     html: false,
14222     tabId: false,
14223     navId : false,
14224     
14225     getAutoCreate : function(){
14226         var cfg = {
14227             tag: 'div',
14228             // item is needed for carousel - not sure if it has any effect otherwise
14229             cls: 'tab-pane item',
14230             html: this.html || ''
14231         };
14232         
14233         if(this.active){
14234             cfg.cls += ' active';
14235         }
14236         
14237         if(this.tabId){
14238             cfg.tabId = this.tabId;
14239         }
14240         
14241         
14242         return cfg;
14243     },
14244     
14245     initEvents:  function()
14246     {
14247         Roo.log('-------- init events on tab panel ---------');
14248         
14249         var p = this.parent();
14250         this.navId = this.navId || p.navId;
14251         
14252         if (typeof(this.navId) != 'undefined') {
14253             // not really needed.. but just in case.. parent should be a NavGroup.
14254             var tg = Roo.bootstrap.TabGroup.get(this.navId);
14255             Roo.log(['register', tg, this]);
14256             tg.register(this);
14257         }
14258     },
14259     
14260     
14261     onRender : function(ct, position)
14262     {
14263        // Roo.log("Call onRender: " + this.xtype);
14264         
14265         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
14266         
14267         
14268         
14269         
14270         
14271     },
14272     
14273     setActive: function(state)
14274     {
14275         Roo.log("panel - set active " + this.tabId + "=" + state);
14276         
14277         this.active = state;
14278         if (!state) {
14279             this.el.removeClass('active');
14280             
14281         } else  if (!this.el.hasClass('active')) {
14282             this.el.addClass('active');
14283         }
14284         this.fireEvent('changed', this, state);
14285     }
14286     
14287     
14288 });
14289  
14290
14291  
14292
14293  /*
14294  * - LGPL
14295  *
14296  * DateField
14297  * 
14298  */
14299
14300 /**
14301  * @class Roo.bootstrap.DateField
14302  * @extends Roo.bootstrap.Input
14303  * Bootstrap DateField class
14304  * @cfg {Number} weekStart default 0
14305  * @cfg {Number} weekStart default 0
14306  * @cfg {Number} viewMode default empty, (months|years)
14307  * @cfg {Number} minViewMode default empty, (months|years)
14308  * @cfg {Number} startDate default -Infinity
14309  * @cfg {Number} endDate default Infinity
14310  * @cfg {Boolean} todayHighlight default false
14311  * @cfg {Boolean} todayBtn default false
14312  * @cfg {Boolean} calendarWeeks default false
14313  * @cfg {Object} daysOfWeekDisabled default empty
14314  * 
14315  * @cfg {Boolean} keyboardNavigation default true
14316  * @cfg {String} language default en
14317  * 
14318  * @constructor
14319  * Create a new DateField
14320  * @param {Object} config The config object
14321  */
14322
14323 Roo.bootstrap.DateField = function(config){
14324     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
14325      this.addEvents({
14326             /**
14327              * @event show
14328              * Fires when this field show.
14329              * @param {Roo.bootstrap.DateField} this
14330              * @param {Mixed} date The date value
14331              */
14332             show : true,
14333             /**
14334              * @event show
14335              * Fires when this field hide.
14336              * @param {Roo.bootstrap.DateField} this
14337              * @param {Mixed} date The date value
14338              */
14339             hide : true,
14340             /**
14341              * @event select
14342              * Fires when select a date.
14343              * @param {Roo.bootstrap.DateField} this
14344              * @param {Mixed} date The date value
14345              */
14346             select : true
14347         });
14348 };
14349
14350 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
14351     
14352     /**
14353      * @cfg {String} format
14354      * The default date format string which can be overriden for localization support.  The format must be
14355      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
14356      */
14357     format : "m/d/y",
14358     /**
14359      * @cfg {String} altFormats
14360      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
14361      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
14362      */
14363     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
14364     
14365     weekStart : 0,
14366     
14367     viewMode : '',
14368     
14369     minViewMode : '',
14370     
14371     todayHighlight : false,
14372     
14373     todayBtn: false,
14374     
14375     language: 'en',
14376     
14377     keyboardNavigation: true,
14378     
14379     calendarWeeks: false,
14380     
14381     startDate: -Infinity,
14382     
14383     endDate: Infinity,
14384     
14385     daysOfWeekDisabled: [],
14386     
14387     _events: [],
14388     
14389     UTCDate: function()
14390     {
14391         return new Date(Date.UTC.apply(Date, arguments));
14392     },
14393     
14394     UTCToday: function()
14395     {
14396         var today = new Date();
14397         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
14398     },
14399     
14400     getDate: function() {
14401             var d = this.getUTCDate();
14402             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
14403     },
14404     
14405     getUTCDate: function() {
14406             return this.date;
14407     },
14408     
14409     setDate: function(d) {
14410             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
14411     },
14412     
14413     setUTCDate: function(d) {
14414             this.date = d;
14415             this.setValue(this.formatDate(this.date));
14416     },
14417         
14418     onRender: function(ct, position)
14419     {
14420         
14421         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
14422         
14423         this.language = this.language || 'en';
14424         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
14425         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
14426         
14427         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
14428         this.format = this.format || 'm/d/y';
14429         this.isInline = false;
14430         this.isInput = true;
14431         this.component = this.el.select('.add-on', true).first() || false;
14432         this.component = (this.component && this.component.length === 0) ? false : this.component;
14433         this.hasInput = this.component && this.inputEL().length;
14434         
14435         if (typeof(this.minViewMode === 'string')) {
14436             switch (this.minViewMode) {
14437                 case 'months':
14438                     this.minViewMode = 1;
14439                     break;
14440                 case 'years':
14441                     this.minViewMode = 2;
14442                     break;
14443                 default:
14444                     this.minViewMode = 0;
14445                     break;
14446             }
14447         }
14448         
14449         if (typeof(this.viewMode === 'string')) {
14450             switch (this.viewMode) {
14451                 case 'months':
14452                     this.viewMode = 1;
14453                     break;
14454                 case 'years':
14455                     this.viewMode = 2;
14456                     break;
14457                 default:
14458                     this.viewMode = 0;
14459                     break;
14460             }
14461         }
14462                 
14463         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
14464         
14465 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
14466         
14467         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14468         
14469         this.picker().on('mousedown', this.onMousedown, this);
14470         this.picker().on('click', this.onClick, this);
14471         
14472         this.picker().addClass('datepicker-dropdown');
14473         
14474         this.startViewMode = this.viewMode;
14475         
14476         
14477         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
14478             if(!this.calendarWeeks){
14479                 v.remove();
14480                 return;
14481             };
14482             
14483             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
14484             v.attr('colspan', function(i, val){
14485                 return parseInt(val) + 1;
14486             });
14487         })
14488                         
14489         
14490         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
14491         
14492         this.setStartDate(this.startDate);
14493         this.setEndDate(this.endDate);
14494         
14495         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
14496         
14497         this.fillDow();
14498         this.fillMonths();
14499         this.update();
14500         this.showMode();
14501         
14502         if(this.isInline) {
14503             this.show();
14504         }
14505     },
14506     
14507     picker : function()
14508     {
14509         return this.pickerEl;
14510 //        return this.el.select('.datepicker', true).first();
14511     },
14512     
14513     fillDow: function()
14514     {
14515         var dowCnt = this.weekStart;
14516         
14517         var dow = {
14518             tag: 'tr',
14519             cn: [
14520                 
14521             ]
14522         };
14523         
14524         if(this.calendarWeeks){
14525             dow.cn.push({
14526                 tag: 'th',
14527                 cls: 'cw',
14528                 html: '&nbsp;'
14529             })
14530         }
14531         
14532         while (dowCnt < this.weekStart + 7) {
14533             dow.cn.push({
14534                 tag: 'th',
14535                 cls: 'dow',
14536                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
14537             });
14538         }
14539         
14540         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
14541     },
14542     
14543     fillMonths: function()
14544     {    
14545         var i = 0
14546         var months = this.picker().select('>.datepicker-months td', true).first();
14547         
14548         months.dom.innerHTML = '';
14549         
14550         while (i < 12) {
14551             var month = {
14552                 tag: 'span',
14553                 cls: 'month',
14554                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
14555             }
14556             
14557             months.createChild(month);
14558         }
14559         
14560     },
14561     
14562     update: function()
14563     {
14564         this.date = (typeof(this.date) === 'undefined' || ((typeof(this.date) === 'string') && !this.date.length)) ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
14565         
14566         if (this.date < this.startDate) {
14567             this.viewDate = new Date(this.startDate);
14568         } else if (this.date > this.endDate) {
14569             this.viewDate = new Date(this.endDate);
14570         } else {
14571             this.viewDate = new Date(this.date);
14572         }
14573         
14574         this.fill();
14575     },
14576     
14577     fill: function() 
14578     {
14579         var d = new Date(this.viewDate),
14580                 year = d.getUTCFullYear(),
14581                 month = d.getUTCMonth(),
14582                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
14583                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
14584                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
14585                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
14586                 currentDate = this.date && this.date.valueOf(),
14587                 today = this.UTCToday();
14588         
14589         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
14590         
14591 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14592         
14593 //        this.picker.select('>tfoot th.today').
14594 //                                              .text(dates[this.language].today)
14595 //                                              .toggle(this.todayBtn !== false);
14596     
14597         this.updateNavArrows();
14598         this.fillMonths();
14599                                                 
14600         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
14601         
14602         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
14603          
14604         prevMonth.setUTCDate(day);
14605         
14606         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
14607         
14608         var nextMonth = new Date(prevMonth);
14609         
14610         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
14611         
14612         nextMonth = nextMonth.valueOf();
14613         
14614         var fillMonths = false;
14615         
14616         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
14617         
14618         while(prevMonth.valueOf() < nextMonth) {
14619             var clsName = '';
14620             
14621             if (prevMonth.getUTCDay() === this.weekStart) {
14622                 if(fillMonths){
14623                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
14624                 }
14625                     
14626                 fillMonths = {
14627                     tag: 'tr',
14628                     cn: []
14629                 };
14630                 
14631                 if(this.calendarWeeks){
14632                     // ISO 8601: First week contains first thursday.
14633                     // ISO also states week starts on Monday, but we can be more abstract here.
14634                     var
14635                     // Start of current week: based on weekstart/current date
14636                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
14637                     // Thursday of this week
14638                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
14639                     // First Thursday of year, year from thursday
14640                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
14641                     // Calendar week: ms between thursdays, div ms per day, div 7 days
14642                     calWeek =  (th - yth) / 864e5 / 7 + 1;
14643                     
14644                     fillMonths.cn.push({
14645                         tag: 'td',
14646                         cls: 'cw',
14647                         html: calWeek
14648                     });
14649                 }
14650             }
14651             
14652             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
14653                 clsName += ' old';
14654             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
14655                 clsName += ' new';
14656             }
14657             if (this.todayHighlight &&
14658                 prevMonth.getUTCFullYear() == today.getFullYear() &&
14659                 prevMonth.getUTCMonth() == today.getMonth() &&
14660                 prevMonth.getUTCDate() == today.getDate()) {
14661                 clsName += ' today';
14662             }
14663             
14664             if (currentDate && prevMonth.valueOf() === currentDate) {
14665                 clsName += ' active';
14666             }
14667             
14668             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
14669                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
14670                     clsName += ' disabled';
14671             }
14672             
14673             fillMonths.cn.push({
14674                 tag: 'td',
14675                 cls: 'day ' + clsName,
14676                 html: prevMonth.getDate()
14677             })
14678             
14679             prevMonth.setDate(prevMonth.getDate()+1);
14680         }
14681           
14682         var currentYear = this.date && this.date.getUTCFullYear();
14683         var currentMonth = this.date && this.date.getUTCMonth();
14684         
14685         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
14686         
14687         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
14688             v.removeClass('active');
14689             
14690             if(currentYear === year && k === currentMonth){
14691                 v.addClass('active');
14692             }
14693             
14694             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
14695                 v.addClass('disabled');
14696             }
14697             
14698         });
14699         
14700         
14701         year = parseInt(year/10, 10) * 10;
14702         
14703         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
14704         
14705         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
14706         
14707         year -= 1;
14708         for (var i = -1; i < 11; i++) {
14709             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
14710                 tag: 'span',
14711                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
14712                 html: year
14713             })
14714             
14715             year += 1;
14716         }
14717     },
14718     
14719     showMode: function(dir) 
14720     {
14721         if (dir) {
14722             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
14723         }
14724         Roo.each(this.picker().select('>div',true).elements, function(v){
14725             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14726             v.hide();
14727         });
14728         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
14729     },
14730     
14731     place: function()
14732     {
14733         if(this.isInline) return;
14734         
14735         this.picker().removeClass(['bottom', 'top']);
14736         
14737         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
14738             /*
14739              * place to the top of element!
14740              *
14741              */
14742             
14743             this.picker().addClass('top');
14744             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
14745             
14746             return;
14747         }
14748         
14749         this.picker().addClass('bottom');
14750         
14751         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
14752     },
14753     
14754     parseDate : function(value)
14755     {
14756         if(!value || value instanceof Date){
14757             return value;
14758         }
14759         var v = Date.parseDate(value, this.format);
14760         if (!v && this.useIso) {
14761             v = Date.parseDate(value, 'Y-m-d');
14762         }
14763         if(!v && this.altFormats){
14764             if(!this.altFormatsArray){
14765                 this.altFormatsArray = this.altFormats.split("|");
14766             }
14767             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
14768                 v = Date.parseDate(value, this.altFormatsArray[i]);
14769             }
14770         }
14771         return v;
14772     },
14773     
14774     formatDate : function(date, fmt)
14775     {
14776         return (!date || !(date instanceof Date)) ?
14777         date : date.dateFormat(fmt || this.format);
14778     },
14779     
14780     onFocus : function()
14781     {
14782         Roo.bootstrap.DateField.superclass.onFocus.call(this);
14783         this.show();
14784     },
14785     
14786     onBlur : function()
14787     {
14788         Roo.bootstrap.DateField.superclass.onBlur.call(this);
14789         
14790         var d = this.inputEl().getValue();
14791         
14792         this.setValue(d);
14793                 
14794         this.hide();
14795     },
14796     
14797     show : function()
14798     {
14799         this.picker().show();
14800         this.update();
14801         this.place();
14802         
14803         this.fireEvent('show', this, this.date);
14804     },
14805     
14806     hide : function()
14807     {
14808         if(this.isInline) return;
14809         this.picker().hide();
14810         this.viewMode = this.startViewMode;
14811         this.showMode();
14812         
14813         this.fireEvent('hide', this, this.date);
14814         
14815     },
14816     
14817     onMousedown: function(e)
14818     {
14819         e.stopPropagation();
14820         e.preventDefault();
14821     },
14822     
14823     keyup: function(e)
14824     {
14825         Roo.bootstrap.DateField.superclass.keyup.call(this);
14826         this.update();
14827     },
14828
14829     setValue: function(v)
14830     {
14831         var d = new Date(v).clearTime();
14832         
14833         if(isNaN(d.getTime())){
14834             this.date = this.viewDate = '';
14835             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
14836             return;
14837         }
14838         
14839         v = this.formatDate(d);
14840         
14841         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
14842         
14843         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
14844      
14845         this.update();
14846
14847         this.fireEvent('select', this, this.date);
14848         
14849     },
14850     
14851     getValue: function()
14852     {
14853         return this.formatDate(this.date);
14854     },
14855     
14856     fireKey: function(e)
14857     {
14858         if (!this.picker().isVisible()){
14859             if (e.keyCode == 27) // allow escape to hide and re-show picker
14860                 this.show();
14861             return;
14862         }
14863         
14864         var dateChanged = false,
14865         dir, day, month,
14866         newDate, newViewDate;
14867         
14868         switch(e.keyCode){
14869             case 27: // escape
14870                 this.hide();
14871                 e.preventDefault();
14872                 break;
14873             case 37: // left
14874             case 39: // right
14875                 if (!this.keyboardNavigation) break;
14876                 dir = e.keyCode == 37 ? -1 : 1;
14877                 
14878                 if (e.ctrlKey){
14879                     newDate = this.moveYear(this.date, dir);
14880                     newViewDate = this.moveYear(this.viewDate, dir);
14881                 } else if (e.shiftKey){
14882                     newDate = this.moveMonth(this.date, dir);
14883                     newViewDate = this.moveMonth(this.viewDate, dir);
14884                 } else {
14885                     newDate = new Date(this.date);
14886                     newDate.setUTCDate(this.date.getUTCDate() + dir);
14887                     newViewDate = new Date(this.viewDate);
14888                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
14889                 }
14890                 if (this.dateWithinRange(newDate)){
14891                     this.date = newDate;
14892                     this.viewDate = newViewDate;
14893                     this.setValue(this.formatDate(this.date));
14894 //                    this.update();
14895                     e.preventDefault();
14896                     dateChanged = true;
14897                 }
14898                 break;
14899             case 38: // up
14900             case 40: // down
14901                 if (!this.keyboardNavigation) break;
14902                 dir = e.keyCode == 38 ? -1 : 1;
14903                 if (e.ctrlKey){
14904                     newDate = this.moveYear(this.date, dir);
14905                     newViewDate = this.moveYear(this.viewDate, dir);
14906                 } else if (e.shiftKey){
14907                     newDate = this.moveMonth(this.date, dir);
14908                     newViewDate = this.moveMonth(this.viewDate, dir);
14909                 } else {
14910                     newDate = new Date(this.date);
14911                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
14912                     newViewDate = new Date(this.viewDate);
14913                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
14914                 }
14915                 if (this.dateWithinRange(newDate)){
14916                     this.date = newDate;
14917                     this.viewDate = newViewDate;
14918                     this.setValue(this.formatDate(this.date));
14919 //                    this.update();
14920                     e.preventDefault();
14921                     dateChanged = true;
14922                 }
14923                 break;
14924             case 13: // enter
14925                 this.setValue(this.formatDate(this.date));
14926                 this.hide();
14927                 e.preventDefault();
14928                 break;
14929             case 9: // tab
14930                 this.setValue(this.formatDate(this.date));
14931                 this.hide();
14932                 break;
14933             case 16: // shift
14934             case 17: // ctrl
14935             case 18: // alt
14936                 break;
14937             default :
14938                 this.hide();
14939                 
14940         }
14941     },
14942     
14943     
14944     onClick: function(e) 
14945     {
14946         e.stopPropagation();
14947         e.preventDefault();
14948         
14949         var target = e.getTarget();
14950         
14951         if(target.nodeName.toLowerCase() === 'i'){
14952             target = Roo.get(target).dom.parentNode;
14953         }
14954         
14955         var nodeName = target.nodeName;
14956         var className = target.className;
14957         var html = target.innerHTML;
14958         
14959         switch(nodeName.toLowerCase()) {
14960             case 'th':
14961                 switch(className) {
14962                     case 'switch':
14963                         this.showMode(1);
14964                         break;
14965                     case 'prev':
14966                     case 'next':
14967                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
14968                         switch(this.viewMode){
14969                                 case 0:
14970                                         this.viewDate = this.moveMonth(this.viewDate, dir);
14971                                         break;
14972                                 case 1:
14973                                 case 2:
14974                                         this.viewDate = this.moveYear(this.viewDate, dir);
14975                                         break;
14976                         }
14977                         this.fill();
14978                         break;
14979                     case 'today':
14980                         var date = new Date();
14981                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
14982 //                        this.fill()
14983                         this.setValue(this.formatDate(this.date));
14984                         
14985                         this.hide();
14986                         break;
14987                 }
14988                 break;
14989             case 'span':
14990                 if (className.indexOf('disabled') === -1) {
14991                     this.viewDate.setUTCDate(1);
14992                     if (className.indexOf('month') !== -1) {
14993                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
14994                     } else {
14995                         var year = parseInt(html, 10) || 0;
14996                         this.viewDate.setUTCFullYear(year);
14997                         
14998                     }
14999                     this.showMode(-1);
15000                     this.fill();
15001                 }
15002                 break;
15003                 
15004             case 'td':
15005                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
15006                     var day = parseInt(html, 10) || 1;
15007                     var year = this.viewDate.getUTCFullYear(),
15008                         month = this.viewDate.getUTCMonth();
15009
15010                     if (className.indexOf('old') !== -1) {
15011                         if(month === 0 ){
15012                             month = 11;
15013                             year -= 1;
15014                         }else{
15015                             month -= 1;
15016                         }
15017                     } else if (className.indexOf('new') !== -1) {
15018                         if (month == 11) {
15019                             month = 0;
15020                             year += 1;
15021                         } else {
15022                             month += 1;
15023                         }
15024                     }
15025                     this.date = this.UTCDate(year, month, day,0,0,0,0);
15026                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
15027 //                    this.fill();
15028                     this.setValue(this.formatDate(this.date));
15029                     this.hide();
15030                 }
15031                 break;
15032         }
15033     },
15034     
15035     setStartDate: function(startDate)
15036     {
15037         this.startDate = startDate || -Infinity;
15038         if (this.startDate !== -Infinity) {
15039             this.startDate = this.parseDate(this.startDate);
15040         }
15041         this.update();
15042         this.updateNavArrows();
15043     },
15044
15045     setEndDate: function(endDate)
15046     {
15047         this.endDate = endDate || Infinity;
15048         if (this.endDate !== Infinity) {
15049             this.endDate = this.parseDate(this.endDate);
15050         }
15051         this.update();
15052         this.updateNavArrows();
15053     },
15054     
15055     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
15056     {
15057         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
15058         if (typeof(this.daysOfWeekDisabled) !== 'object') {
15059             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
15060         }
15061         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
15062             return parseInt(d, 10);
15063         });
15064         this.update();
15065         this.updateNavArrows();
15066     },
15067     
15068     updateNavArrows: function() 
15069     {
15070         var d = new Date(this.viewDate),
15071         year = d.getUTCFullYear(),
15072         month = d.getUTCMonth();
15073         
15074         Roo.each(this.picker().select('.prev', true).elements, function(v){
15075             v.show();
15076             switch (this.viewMode) {
15077                 case 0:
15078
15079                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
15080                         v.hide();
15081                     }
15082                     break;
15083                 case 1:
15084                 case 2:
15085                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
15086                         v.hide();
15087                     }
15088                     break;
15089             }
15090         });
15091         
15092         Roo.each(this.picker().select('.next', true).elements, function(v){
15093             v.show();
15094             switch (this.viewMode) {
15095                 case 0:
15096
15097                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
15098                         v.hide();
15099                     }
15100                     break;
15101                 case 1:
15102                 case 2:
15103                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
15104                         v.hide();
15105                     }
15106                     break;
15107             }
15108         })
15109     },
15110     
15111     moveMonth: function(date, dir)
15112     {
15113         if (!dir) return date;
15114         var new_date = new Date(date.valueOf()),
15115         day = new_date.getUTCDate(),
15116         month = new_date.getUTCMonth(),
15117         mag = Math.abs(dir),
15118         new_month, test;
15119         dir = dir > 0 ? 1 : -1;
15120         if (mag == 1){
15121             test = dir == -1
15122             // If going back one month, make sure month is not current month
15123             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
15124             ? function(){
15125                 return new_date.getUTCMonth() == month;
15126             }
15127             // If going forward one month, make sure month is as expected
15128             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
15129             : function(){
15130                 return new_date.getUTCMonth() != new_month;
15131             };
15132             new_month = month + dir;
15133             new_date.setUTCMonth(new_month);
15134             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
15135             if (new_month < 0 || new_month > 11)
15136                 new_month = (new_month + 12) % 12;
15137         } else {
15138             // For magnitudes >1, move one month at a time...
15139             for (var i=0; i<mag; i++)
15140                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
15141                 new_date = this.moveMonth(new_date, dir);
15142             // ...then reset the day, keeping it in the new month
15143             new_month = new_date.getUTCMonth();
15144             new_date.setUTCDate(day);
15145             test = function(){
15146                 return new_month != new_date.getUTCMonth();
15147             };
15148         }
15149         // Common date-resetting loop -- if date is beyond end of month, make it
15150         // end of month
15151         while (test()){
15152             new_date.setUTCDate(--day);
15153             new_date.setUTCMonth(new_month);
15154         }
15155         return new_date;
15156     },
15157
15158     moveYear: function(date, dir)
15159     {
15160         return this.moveMonth(date, dir*12);
15161     },
15162
15163     dateWithinRange: function(date)
15164     {
15165         return date >= this.startDate && date <= this.endDate;
15166     },
15167
15168     
15169     remove: function() 
15170     {
15171         this.picker().remove();
15172     }
15173    
15174 });
15175
15176 Roo.apply(Roo.bootstrap.DateField,  {
15177     
15178     head : {
15179         tag: 'thead',
15180         cn: [
15181         {
15182             tag: 'tr',
15183             cn: [
15184             {
15185                 tag: 'th',
15186                 cls: 'prev',
15187                 html: '<i class="fa fa-arrow-left"/>'
15188             },
15189             {
15190                 tag: 'th',
15191                 cls: 'switch',
15192                 colspan: '5'
15193             },
15194             {
15195                 tag: 'th',
15196                 cls: 'next',
15197                 html: '<i class="fa fa-arrow-right"/>'
15198             }
15199
15200             ]
15201         }
15202         ]
15203     },
15204     
15205     content : {
15206         tag: 'tbody',
15207         cn: [
15208         {
15209             tag: 'tr',
15210             cn: [
15211             {
15212                 tag: 'td',
15213                 colspan: '7'
15214             }
15215             ]
15216         }
15217         ]
15218     },
15219     
15220     footer : {
15221         tag: 'tfoot',
15222         cn: [
15223         {
15224             tag: 'tr',
15225             cn: [
15226             {
15227                 tag: 'th',
15228                 colspan: '7',
15229                 cls: 'today'
15230             }
15231                     
15232             ]
15233         }
15234         ]
15235     },
15236     
15237     dates:{
15238         en: {
15239             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
15240             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
15241             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
15242             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
15243             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
15244             today: "Today"
15245         }
15246     },
15247     
15248     modes: [
15249     {
15250         clsName: 'days',
15251         navFnc: 'Month',
15252         navStep: 1
15253     },
15254     {
15255         clsName: 'months',
15256         navFnc: 'FullYear',
15257         navStep: 1
15258     },
15259     {
15260         clsName: 'years',
15261         navFnc: 'FullYear',
15262         navStep: 10
15263     }]
15264 });
15265
15266 Roo.apply(Roo.bootstrap.DateField,  {
15267   
15268     template : {
15269         tag: 'div',
15270         cls: 'datepicker dropdown-menu',
15271         cn: [
15272         {
15273             tag: 'div',
15274             cls: 'datepicker-days',
15275             cn: [
15276             {
15277                 tag: 'table',
15278                 cls: 'table-condensed',
15279                 cn:[
15280                 Roo.bootstrap.DateField.head,
15281                 {
15282                     tag: 'tbody'
15283                 },
15284                 Roo.bootstrap.DateField.footer
15285                 ]
15286             }
15287             ]
15288         },
15289         {
15290             tag: 'div',
15291             cls: 'datepicker-months',
15292             cn: [
15293             {
15294                 tag: 'table',
15295                 cls: 'table-condensed',
15296                 cn:[
15297                 Roo.bootstrap.DateField.head,
15298                 Roo.bootstrap.DateField.content,
15299                 Roo.bootstrap.DateField.footer
15300                 ]
15301             }
15302             ]
15303         },
15304         {
15305             tag: 'div',
15306             cls: 'datepicker-years',
15307             cn: [
15308             {
15309                 tag: 'table',
15310                 cls: 'table-condensed',
15311                 cn:[
15312                 Roo.bootstrap.DateField.head,
15313                 Roo.bootstrap.DateField.content,
15314                 Roo.bootstrap.DateField.footer
15315                 ]
15316             }
15317             ]
15318         }
15319         ]
15320     }
15321 });
15322
15323  
15324
15325  /*
15326  * - LGPL
15327  *
15328  * TimeField
15329  * 
15330  */
15331
15332 /**
15333  * @class Roo.bootstrap.TimeField
15334  * @extends Roo.bootstrap.Input
15335  * Bootstrap DateField class
15336  * 
15337  * 
15338  * @constructor
15339  * Create a new TimeField
15340  * @param {Object} config The config object
15341  */
15342
15343 Roo.bootstrap.TimeField = function(config){
15344     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
15345     this.addEvents({
15346             /**
15347              * @event show
15348              * Fires when this field show.
15349              * @param {Roo.bootstrap.DateField} this
15350              * @param {Mixed} date The date value
15351              */
15352             show : true,
15353             /**
15354              * @event show
15355              * Fires when this field hide.
15356              * @param {Roo.bootstrap.DateField} this
15357              * @param {Mixed} date The date value
15358              */
15359             hide : true,
15360             /**
15361              * @event select
15362              * Fires when select a date.
15363              * @param {Roo.bootstrap.DateField} this
15364              * @param {Mixed} date The date value
15365              */
15366             select : true
15367         });
15368 };
15369
15370 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
15371     
15372     /**
15373      * @cfg {String} format
15374      * The default time format string which can be overriden for localization support.  The format must be
15375      * valid according to {@link Date#parseDate} (defaults to 'H:i').
15376      */
15377     format : "H:i",
15378        
15379     onRender: function(ct, position)
15380     {
15381         
15382         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
15383                 
15384         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
15385         
15386         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15387         
15388         this.pop = this.picker().select('>.datepicker-time',true).first();
15389         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
15390         
15391         this.picker().on('mousedown', this.onMousedown, this);
15392         this.picker().on('click', this.onClick, this);
15393         
15394         this.picker().addClass('datepicker-dropdown');
15395     
15396         this.fillTime();
15397         this.update();
15398             
15399         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
15400         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
15401         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
15402         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
15403         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
15404         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
15405
15406     },
15407     
15408     fireKey: function(e){
15409         if (!this.picker().isVisible()){
15410             if (e.keyCode == 27) // allow escape to hide and re-show picker
15411                 this.show();
15412             return;
15413         }
15414
15415         e.preventDefault();
15416         
15417         switch(e.keyCode){
15418             case 27: // escape
15419                 this.hide();
15420                 break;
15421             case 37: // left
15422             case 39: // right
15423                 this.onTogglePeriod();
15424                 break;
15425             case 38: // up
15426                 this.onIncrementMinutes();
15427                 break;
15428             case 40: // down
15429                 this.onDecrementMinutes();
15430                 break;
15431             case 13: // enter
15432             case 9: // tab
15433                 this.setTime();
15434                 break;
15435         }
15436     },
15437     
15438     onClick: function(e) {
15439         e.stopPropagation();
15440         e.preventDefault();
15441     },
15442     
15443     picker : function()
15444     {
15445         return this.el.select('.datepicker', true).first();
15446     },
15447     
15448     fillTime: function()
15449     {    
15450         var time = this.pop.select('tbody', true).first();
15451         
15452         time.dom.innerHTML = '';
15453         
15454         time.createChild({
15455             tag: 'tr',
15456             cn: [
15457                 {
15458                     tag: 'td',
15459                     cn: [
15460                         {
15461                             tag: 'a',
15462                             href: '#',
15463                             cls: 'btn',
15464                             cn: [
15465                                 {
15466                                     tag: 'span',
15467                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
15468                                 }
15469                             ]
15470                         } 
15471                     ]
15472                 },
15473                 {
15474                     tag: 'td',
15475                     cls: 'separator'
15476                 },
15477                 {
15478                     tag: 'td',
15479                     cn: [
15480                         {
15481                             tag: 'a',
15482                             href: '#',
15483                             cls: 'btn',
15484                             cn: [
15485                                 {
15486                                     tag: 'span',
15487                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
15488                                 }
15489                             ]
15490                         }
15491                     ]
15492                 },
15493                 {
15494                     tag: 'td',
15495                     cls: 'separator'
15496                 }
15497             ]
15498         });
15499         
15500         time.createChild({
15501             tag: 'tr',
15502             cn: [
15503                 {
15504                     tag: 'td',
15505                     cn: [
15506                         {
15507                             tag: 'span',
15508                             cls: 'timepicker-hour',
15509                             html: '00'
15510                         }  
15511                     ]
15512                 },
15513                 {
15514                     tag: 'td',
15515                     cls: 'separator',
15516                     html: ':'
15517                 },
15518                 {
15519                     tag: 'td',
15520                     cn: [
15521                         {
15522                             tag: 'span',
15523                             cls: 'timepicker-minute',
15524                             html: '00'
15525                         }  
15526                     ]
15527                 },
15528                 {
15529                     tag: 'td',
15530                     cls: 'separator'
15531                 },
15532                 {
15533                     tag: 'td',
15534                     cn: [
15535                         {
15536                             tag: 'button',
15537                             type: 'button',
15538                             cls: 'btn btn-primary period',
15539                             html: 'AM'
15540                             
15541                         }
15542                     ]
15543                 }
15544             ]
15545         });
15546         
15547         time.createChild({
15548             tag: 'tr',
15549             cn: [
15550                 {
15551                     tag: 'td',
15552                     cn: [
15553                         {
15554                             tag: 'a',
15555                             href: '#',
15556                             cls: 'btn',
15557                             cn: [
15558                                 {
15559                                     tag: 'span',
15560                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
15561                                 }
15562                             ]
15563                         }
15564                     ]
15565                 },
15566                 {
15567                     tag: 'td',
15568                     cls: 'separator'
15569                 },
15570                 {
15571                     tag: 'td',
15572                     cn: [
15573                         {
15574                             tag: 'a',
15575                             href: '#',
15576                             cls: 'btn',
15577                             cn: [
15578                                 {
15579                                     tag: 'span',
15580                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
15581                                 }
15582                             ]
15583                         }
15584                     ]
15585                 },
15586                 {
15587                     tag: 'td',
15588                     cls: 'separator'
15589                 }
15590             ]
15591         });
15592         
15593     },
15594     
15595     update: function()
15596     {
15597         
15598         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
15599         
15600         this.fill();
15601     },
15602     
15603     fill: function() 
15604     {
15605         var hours = this.time.getHours();
15606         var minutes = this.time.getMinutes();
15607         var period = 'AM';
15608         
15609         if(hours > 11){
15610             period = 'PM';
15611         }
15612         
15613         if(hours == 0){
15614             hours = 12;
15615         }
15616         
15617         
15618         if(hours > 12){
15619             hours = hours - 12;
15620         }
15621         
15622         if(hours < 10){
15623             hours = '0' + hours;
15624         }
15625         
15626         if(minutes < 10){
15627             minutes = '0' + minutes;
15628         }
15629         
15630         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
15631         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
15632         this.pop.select('button', true).first().dom.innerHTML = period;
15633         
15634     },
15635     
15636     place: function()
15637     {   
15638         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
15639         
15640         var cls = ['bottom'];
15641         
15642         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
15643             cls.pop();
15644             cls.push('top');
15645         }
15646         
15647         cls.push('right');
15648         
15649         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
15650             cls.pop();
15651             cls.push('left');
15652         }
15653         
15654         this.picker().addClass(cls.join('-'));
15655         
15656         var _this = this;
15657         
15658         Roo.each(cls, function(c){
15659             if(c == 'bottom'){
15660                 _this.picker().setTop(_this.inputEl().getHeight());
15661                 return;
15662             }
15663             if(c == 'top'){
15664                 _this.picker().setTop(0 - _this.picker().getHeight());
15665                 return;
15666             }
15667             
15668             if(c == 'left'){
15669                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
15670                 return;
15671             }
15672             if(c == 'right'){
15673                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
15674                 return;
15675             }
15676         });
15677         
15678     },
15679   
15680     onFocus : function()
15681     {
15682         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
15683         this.show();
15684     },
15685     
15686     onBlur : function()
15687     {
15688         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
15689         this.hide();
15690     },
15691     
15692     show : function()
15693     {
15694         this.picker().show();
15695         this.pop.show();
15696         this.update();
15697         this.place();
15698         
15699         this.fireEvent('show', this, this.date);
15700     },
15701     
15702     hide : function()
15703     {
15704         this.picker().hide();
15705         this.pop.hide();
15706         
15707         this.fireEvent('hide', this, this.date);
15708     },
15709     
15710     setTime : function()
15711     {
15712         this.hide();
15713         this.setValue(this.time.format(this.format));
15714         
15715         this.fireEvent('select', this, this.date);
15716         
15717         
15718     },
15719     
15720     onMousedown: function(e){
15721         e.stopPropagation();
15722         e.preventDefault();
15723     },
15724     
15725     onIncrementHours: function()
15726     {
15727         Roo.log('onIncrementHours');
15728         this.time = this.time.add(Date.HOUR, 1);
15729         this.update();
15730         
15731     },
15732     
15733     onDecrementHours: function()
15734     {
15735         Roo.log('onDecrementHours');
15736         this.time = this.time.add(Date.HOUR, -1);
15737         this.update();
15738     },
15739     
15740     onIncrementMinutes: function()
15741     {
15742         Roo.log('onIncrementMinutes');
15743         this.time = this.time.add(Date.MINUTE, 1);
15744         this.update();
15745     },
15746     
15747     onDecrementMinutes: function()
15748     {
15749         Roo.log('onDecrementMinutes');
15750         this.time = this.time.add(Date.MINUTE, -1);
15751         this.update();
15752     },
15753     
15754     onTogglePeriod: function()
15755     {
15756         Roo.log('onTogglePeriod');
15757         this.time = this.time.add(Date.HOUR, 12);
15758         this.update();
15759     }
15760     
15761    
15762 });
15763
15764 Roo.apply(Roo.bootstrap.TimeField,  {
15765     
15766     content : {
15767         tag: 'tbody',
15768         cn: [
15769             {
15770                 tag: 'tr',
15771                 cn: [
15772                 {
15773                     tag: 'td',
15774                     colspan: '7'
15775                 }
15776                 ]
15777             }
15778         ]
15779     },
15780     
15781     footer : {
15782         tag: 'tfoot',
15783         cn: [
15784             {
15785                 tag: 'tr',
15786                 cn: [
15787                 {
15788                     tag: 'th',
15789                     colspan: '7',
15790                     cls: '',
15791                     cn: [
15792                         {
15793                             tag: 'button',
15794                             cls: 'btn btn-info ok',
15795                             html: 'OK'
15796                         }
15797                     ]
15798                 }
15799
15800                 ]
15801             }
15802         ]
15803     }
15804 });
15805
15806 Roo.apply(Roo.bootstrap.TimeField,  {
15807   
15808     template : {
15809         tag: 'div',
15810         cls: 'datepicker dropdown-menu',
15811         cn: [
15812             {
15813                 tag: 'div',
15814                 cls: 'datepicker-time',
15815                 cn: [
15816                 {
15817                     tag: 'table',
15818                     cls: 'table-condensed',
15819                     cn:[
15820                     Roo.bootstrap.TimeField.content,
15821                     Roo.bootstrap.TimeField.footer
15822                     ]
15823                 }
15824                 ]
15825             }
15826         ]
15827     }
15828 });
15829
15830  
15831
15832  /*
15833  * - LGPL
15834  *
15835  * CheckBox
15836  * 
15837  */
15838
15839 /**
15840  * @class Roo.bootstrap.CheckBox
15841  * @extends Roo.bootstrap.Input
15842  * Bootstrap CheckBox class
15843  * 
15844  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
15845  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
15846  * @cfg {String} boxLabel The text that appears beside the checkbox
15847  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
15848  * @cfg {Boolean} checked initnal the element
15849  * 
15850  * 
15851  * @constructor
15852  * Create a new CheckBox
15853  * @param {Object} config The config object
15854  */
15855
15856 Roo.bootstrap.CheckBox = function(config){
15857     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
15858    
15859         this.addEvents({
15860             /**
15861             * @event check
15862             * Fires when the element is checked or unchecked.
15863             * @param {Roo.bootstrap.CheckBox} this This input
15864             * @param {Boolean} checked The new checked value
15865             */
15866            check : true
15867         });
15868 };
15869
15870 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
15871     
15872     inputType: 'checkbox',
15873     inputValue: 1,
15874     valueOff: 0,
15875     boxLabel: false,
15876     checked: false,
15877     weight : false,
15878     
15879     getAutoCreate : function()
15880     {
15881         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15882         
15883         var id = Roo.id();
15884         
15885         var cfg = {};
15886         
15887         cfg.cls = 'form-group checkbox' //input-group
15888         
15889         
15890         
15891         
15892         var input =  {
15893             tag: 'input',
15894             id : id,
15895             type : this.inputType,
15896             value : (!this.checked) ? this.valueOff : this.inputValue,
15897             cls : 'roo-checkbox', //'form-box',
15898             placeholder : this.placeholder || ''
15899             
15900         };
15901         
15902         if (this.weight) { // Validity check?
15903             cfg.cls += " checkbox-" + this.weight;
15904         }
15905         
15906         if (this.disabled) {
15907             input.disabled=true;
15908         }
15909         
15910         if(this.checked){
15911             input.checked = this.checked;
15912         }
15913         
15914         if (this.name) {
15915             input.name = this.name;
15916         }
15917         
15918         if (this.size) {
15919             input.cls += ' input-' + this.size;
15920         }
15921         
15922         var settings=this;
15923         ['xs','sm','md','lg'].map(function(size){
15924             if (settings[size]) {
15925                 cfg.cls += ' col-' + size + '-' + settings[size];
15926             }
15927         });
15928         
15929        
15930         
15931         var inputblock = input;
15932         
15933         
15934         
15935         
15936         if (this.before || this.after) {
15937             
15938             inputblock = {
15939                 cls : 'input-group',
15940                 cn :  [] 
15941             };
15942             if (this.before) {
15943                 inputblock.cn.push({
15944                     tag :'span',
15945                     cls : 'input-group-addon',
15946                     html : this.before
15947                 });
15948             }
15949             inputblock.cn.push(input);
15950             if (this.after) {
15951                 inputblock.cn.push({
15952                     tag :'span',
15953                     cls : 'input-group-addon',
15954                     html : this.after
15955                 });
15956             }
15957             
15958         };
15959         
15960         if (align ==='left' && this.fieldLabel.length) {
15961                 Roo.log("left and has label");
15962                 cfg.cn = [
15963                     
15964                     {
15965                         tag: 'label',
15966                         'for' :  id,
15967                         cls : 'control-label col-md-' + this.labelWidth,
15968                         html : this.fieldLabel
15969                         
15970                     },
15971                     {
15972                         cls : "col-md-" + (12 - this.labelWidth), 
15973                         cn: [
15974                             inputblock
15975                         ]
15976                     }
15977                     
15978                 ];
15979         } else if ( this.fieldLabel.length) {
15980                 Roo.log(" label");
15981                 cfg.cn = [
15982                    
15983                     {
15984                         tag: this.boxLabel ? 'span' : 'label',
15985                         'for': id,
15986                         cls: 'control-label box-input-label',
15987                         //cls : 'input-group-addon',
15988                         html : this.fieldLabel
15989                         
15990                     },
15991                     
15992                     inputblock
15993                     
15994                 ];
15995
15996         } else {
15997             
15998                 Roo.log(" no label && no align");
15999                 cfg.cn = [  inputblock ] ;
16000                 
16001                 
16002         };
16003          if(this.boxLabel){
16004             cfg.cn.push( {
16005                 tag: 'label',
16006                 'for': id,
16007                 cls: 'box-label',
16008                 html: this.boxLabel
16009                 
16010             });
16011         }
16012         
16013         
16014        
16015         return cfg;
16016         
16017     },
16018     
16019     /**
16020      * return the real input element.
16021      */
16022     inputEl: function ()
16023     {
16024         return this.el.select('input.roo-checkbox',true).first();
16025     },
16026     
16027     label: function()
16028     {
16029         return this.el.select('label.control-label',true).first();
16030     },
16031     
16032     initEvents : function()
16033     {
16034 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
16035         
16036         this.inputEl().on('click', this.onClick,  this);
16037         
16038     },
16039     
16040     onClick : function()
16041     {   
16042         this.setChecked(!this.checked);
16043     },
16044     
16045     setChecked : function(state,suppressEvent)
16046     {
16047         this.checked = state;
16048         
16049         this.inputEl().dom.checked = state;
16050         
16051         if(suppressEvent !== true){
16052             this.fireEvent('check', this, state);
16053         }
16054         
16055         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16056         
16057     },
16058     
16059     setValue : function(v,suppressEvent)
16060     {
16061         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
16062     }
16063     
16064 });
16065
16066  
16067 /*
16068  * - LGPL
16069  *
16070  * Radio
16071  * 
16072  */
16073
16074 /**
16075  * @class Roo.bootstrap.Radio
16076  * @extends Roo.bootstrap.CheckBox
16077  * Bootstrap Radio class
16078
16079  * @constructor
16080  * Create a new Radio
16081  * @param {Object} config The config object
16082  */
16083
16084 Roo.bootstrap.Radio = function(config){
16085     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
16086    
16087 };
16088
16089 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
16090     
16091     inputType: 'radio',
16092     inputValue: '',
16093     valueOff: '',
16094     
16095     getAutoCreate : function()
16096     {
16097         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
16098         
16099         var id = Roo.id();
16100         
16101         var cfg = {};
16102         
16103         cfg.cls = 'form-group radio' //input-group
16104         
16105         var input =  {
16106             tag: 'input',
16107             id : id,
16108             type : this.inputType,
16109             value : (!this.checked) ? this.valueOff : this.inputValue,
16110             cls : 'roo-radio',
16111             placeholder : this.placeholder || ''
16112             
16113         };
16114           if (this.weight) { // Validity check?
16115             cfg.cls += " radio-" + this.weight;
16116         }
16117         if (this.disabled) {
16118             input.disabled=true;
16119         }
16120         
16121         if(this.checked){
16122             input.checked = this.checked;
16123         }
16124         
16125         if (this.name) {
16126             input.name = this.name;
16127         }
16128         
16129         if (this.size) {
16130             input.cls += ' input-' + this.size;
16131         }
16132         
16133         var settings=this;
16134         ['xs','sm','md','lg'].map(function(size){
16135             if (settings[size]) {
16136                 cfg.cls += ' col-' + size + '-' + settings[size];
16137             }
16138         });
16139         
16140         var inputblock = input;
16141         
16142         if (this.before || this.after) {
16143             
16144             inputblock = {
16145                 cls : 'input-group',
16146                 cn :  [] 
16147             };
16148             if (this.before) {
16149                 inputblock.cn.push({
16150                     tag :'span',
16151                     cls : 'input-group-addon',
16152                     html : this.before
16153                 });
16154             }
16155             inputblock.cn.push(input);
16156             if (this.after) {
16157                 inputblock.cn.push({
16158                     tag :'span',
16159                     cls : 'input-group-addon',
16160                     html : this.after
16161                 });
16162             }
16163             
16164         };
16165         
16166         if (align ==='left' && this.fieldLabel.length) {
16167                 Roo.log("left and has label");
16168                 cfg.cn = [
16169                     
16170                     {
16171                         tag: 'label',
16172                         'for' :  id,
16173                         cls : 'control-label col-md-' + this.labelWidth,
16174                         html : this.fieldLabel
16175                         
16176                     },
16177                     {
16178                         cls : "col-md-" + (12 - this.labelWidth), 
16179                         cn: [
16180                             inputblock
16181                         ]
16182                     }
16183                     
16184                 ];
16185         } else if ( this.fieldLabel.length) {
16186                 Roo.log(" label");
16187                  cfg.cn = [
16188                    
16189                     {
16190                         tag: 'label',
16191                         'for': id,
16192                         cls: 'control-label box-input-label',
16193                         //cls : 'input-group-addon',
16194                         html : this.fieldLabel
16195                         
16196                     },
16197                     
16198                     inputblock
16199                     
16200                 ];
16201
16202         } else {
16203             
16204                    Roo.log(" no label && no align");
16205                 cfg.cn = [
16206                     
16207                         inputblock
16208                     
16209                 ];
16210                 
16211                 
16212         };
16213         
16214         if(this.boxLabel){
16215             cfg.cn.push({
16216                 tag: 'label',
16217                 'for': id,
16218                 cls: 'box-label',
16219                 html: this.boxLabel
16220             })
16221         }
16222         
16223         return cfg;
16224         
16225     },
16226     inputEl: function ()
16227     {
16228         return this.el.select('input.roo-radio',true).first();
16229     },
16230     onClick : function()
16231     {   
16232         this.setChecked(true);
16233     },
16234     
16235     setChecked : function(state,suppressEvent)
16236     {
16237         if(state){
16238             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16239                 v.dom.checked = false;
16240             });
16241         }
16242         
16243         this.checked = state;
16244         this.inputEl().dom.checked = state;
16245         
16246         if(suppressEvent !== true){
16247             this.fireEvent('check', this, state);
16248         }
16249         
16250         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16251         
16252     },
16253     
16254     getGroupValue : function()
16255     {
16256         var value = ''
16257         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16258             if(v.dom.checked == true){
16259                 value = v.dom.value;
16260             }
16261         });
16262         
16263         return value;
16264     },
16265     
16266     /**
16267      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16268      * @return {Mixed} value The field value
16269      */
16270     getValue : function(){
16271         return this.getGroupValue();
16272     }
16273     
16274 });
16275
16276  
16277 //<script type="text/javascript">
16278
16279 /*
16280  * Based  Ext JS Library 1.1.1
16281  * Copyright(c) 2006-2007, Ext JS, LLC.
16282  * LGPL
16283  *
16284  */
16285  
16286 /**
16287  * @class Roo.HtmlEditorCore
16288  * @extends Roo.Component
16289  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
16290  *
16291  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
16292  */
16293
16294 Roo.HtmlEditorCore = function(config){
16295     
16296     
16297     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
16298     this.addEvents({
16299         /**
16300          * @event initialize
16301          * Fires when the editor is fully initialized (including the iframe)
16302          * @param {Roo.HtmlEditorCore} this
16303          */
16304         initialize: true,
16305         /**
16306          * @event activate
16307          * Fires when the editor is first receives the focus. Any insertion must wait
16308          * until after this event.
16309          * @param {Roo.HtmlEditorCore} this
16310          */
16311         activate: true,
16312          /**
16313          * @event beforesync
16314          * Fires before the textarea is updated with content from the editor iframe. Return false
16315          * to cancel the sync.
16316          * @param {Roo.HtmlEditorCore} this
16317          * @param {String} html
16318          */
16319         beforesync: true,
16320          /**
16321          * @event beforepush
16322          * Fires before the iframe editor is updated with content from the textarea. Return false
16323          * to cancel the push.
16324          * @param {Roo.HtmlEditorCore} this
16325          * @param {String} html
16326          */
16327         beforepush: true,
16328          /**
16329          * @event sync
16330          * Fires when the textarea is updated with content from the editor iframe.
16331          * @param {Roo.HtmlEditorCore} this
16332          * @param {String} html
16333          */
16334         sync: true,
16335          /**
16336          * @event push
16337          * Fires when the iframe editor is updated with content from the textarea.
16338          * @param {Roo.HtmlEditorCore} this
16339          * @param {String} html
16340          */
16341         push: true,
16342         
16343         /**
16344          * @event editorevent
16345          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
16346          * @param {Roo.HtmlEditorCore} this
16347          */
16348         editorevent: true
16349     });
16350      
16351 };
16352
16353
16354 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
16355
16356
16357      /**
16358      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
16359      */
16360     
16361     owner : false,
16362     
16363      /**
16364      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
16365      *                        Roo.resizable.
16366      */
16367     resizable : false,
16368      /**
16369      * @cfg {Number} height (in pixels)
16370      */   
16371     height: 300,
16372    /**
16373      * @cfg {Number} width (in pixels)
16374      */   
16375     width: 500,
16376     
16377     /**
16378      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
16379      * 
16380      */
16381     stylesheets: false,
16382     
16383     // id of frame..
16384     frameId: false,
16385     
16386     // private properties
16387     validationEvent : false,
16388     deferHeight: true,
16389     initialized : false,
16390     activated : false,
16391     sourceEditMode : false,
16392     onFocus : Roo.emptyFn,
16393     iframePad:3,
16394     hideMode:'offsets',
16395     
16396     clearUp: true,
16397     
16398      
16399     
16400
16401     /**
16402      * Protected method that will not generally be called directly. It
16403      * is called when the editor initializes the iframe with HTML contents. Override this method if you
16404      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
16405      */
16406     getDocMarkup : function(){
16407         // body styles..
16408         var st = '';
16409         Roo.log(this.stylesheets);
16410         
16411         // inherit styels from page...?? 
16412         if (this.stylesheets === false) {
16413             
16414             Roo.get(document.head).select('style').each(function(node) {
16415                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16416             });
16417             
16418             Roo.get(document.head).select('link').each(function(node) { 
16419                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16420             });
16421             
16422         } else if (!this.stylesheets.length) {
16423                 // simple..
16424                 st = '<style type="text/css">' +
16425                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16426                    '</style>';
16427         } else {
16428             Roo.each(this.stylesheets, function(s) {
16429                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
16430             });
16431             
16432         }
16433         
16434         st +=  '<style type="text/css">' +
16435             'IMG { cursor: pointer } ' +
16436         '</style>';
16437
16438         
16439         return '<html><head>' + st  +
16440             //<style type="text/css">' +
16441             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16442             //'</style>' +
16443             ' </head><body class="roo-htmleditor-body"></body></html>';
16444     },
16445
16446     // private
16447     onRender : function(ct, position)
16448     {
16449         var _t = this;
16450         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
16451         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
16452         
16453         
16454         this.el.dom.style.border = '0 none';
16455         this.el.dom.setAttribute('tabIndex', -1);
16456         this.el.addClass('x-hidden hide');
16457         
16458         
16459         
16460         if(Roo.isIE){ // fix IE 1px bogus margin
16461             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
16462         }
16463        
16464         
16465         this.frameId = Roo.id();
16466         
16467          
16468         
16469         var iframe = this.owner.wrap.createChild({
16470             tag: 'iframe',
16471             cls: 'form-control', // bootstrap..
16472             id: this.frameId,
16473             name: this.frameId,
16474             frameBorder : 'no',
16475             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
16476         }, this.el
16477         );
16478         
16479         
16480         this.iframe = iframe.dom;
16481
16482          this.assignDocWin();
16483         
16484         this.doc.designMode = 'on';
16485        
16486         this.doc.open();
16487         this.doc.write(this.getDocMarkup());
16488         this.doc.close();
16489
16490         
16491         var task = { // must defer to wait for browser to be ready
16492             run : function(){
16493                 //console.log("run task?" + this.doc.readyState);
16494                 this.assignDocWin();
16495                 if(this.doc.body || this.doc.readyState == 'complete'){
16496                     try {
16497                         this.doc.designMode="on";
16498                     } catch (e) {
16499                         return;
16500                     }
16501                     Roo.TaskMgr.stop(task);
16502                     this.initEditor.defer(10, this);
16503                 }
16504             },
16505             interval : 10,
16506             duration: 10000,
16507             scope: this
16508         };
16509         Roo.TaskMgr.start(task);
16510
16511         
16512          
16513     },
16514
16515     // private
16516     onResize : function(w, h)
16517     {
16518          Roo.log('resize: ' +w + ',' + h );
16519         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
16520         if(!this.iframe){
16521             return;
16522         }
16523         if(typeof w == 'number'){
16524             
16525             this.iframe.style.width = w + 'px';
16526         }
16527         if(typeof h == 'number'){
16528             
16529             this.iframe.style.height = h + 'px';
16530             if(this.doc){
16531                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
16532             }
16533         }
16534         
16535     },
16536
16537     /**
16538      * Toggles the editor between standard and source edit mode.
16539      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16540      */
16541     toggleSourceEdit : function(sourceEditMode){
16542         
16543         this.sourceEditMode = sourceEditMode === true;
16544         
16545         if(this.sourceEditMode){
16546  
16547             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
16548             
16549         }else{
16550             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
16551             //this.iframe.className = '';
16552             this.deferFocus();
16553         }
16554         //this.setSize(this.owner.wrap.getSize());
16555         //this.fireEvent('editmodechange', this, this.sourceEditMode);
16556     },
16557
16558     
16559   
16560
16561     /**
16562      * Protected method that will not generally be called directly. If you need/want
16563      * custom HTML cleanup, this is the method you should override.
16564      * @param {String} html The HTML to be cleaned
16565      * return {String} The cleaned HTML
16566      */
16567     cleanHtml : function(html){
16568         html = String(html);
16569         if(html.length > 5){
16570             if(Roo.isSafari){ // strip safari nonsense
16571                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
16572             }
16573         }
16574         if(html == '&nbsp;'){
16575             html = '';
16576         }
16577         return html;
16578     },
16579
16580     /**
16581      * HTML Editor -> Textarea
16582      * Protected method that will not generally be called directly. Syncs the contents
16583      * of the editor iframe with the textarea.
16584      */
16585     syncValue : function(){
16586         if(this.initialized){
16587             var bd = (this.doc.body || this.doc.documentElement);
16588             //this.cleanUpPaste(); -- this is done else where and causes havoc..
16589             var html = bd.innerHTML;
16590             if(Roo.isSafari){
16591                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
16592                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
16593                 if(m && m[1]){
16594                     html = '<div style="'+m[0]+'">' + html + '</div>';
16595                 }
16596             }
16597             html = this.cleanHtml(html);
16598             // fix up the special chars.. normaly like back quotes in word...
16599             // however we do not want to do this with chinese..
16600             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
16601                 var cc = b.charCodeAt();
16602                 if (
16603                     (cc >= 0x4E00 && cc < 0xA000 ) ||
16604                     (cc >= 0x3400 && cc < 0x4E00 ) ||
16605                     (cc >= 0xf900 && cc < 0xfb00 )
16606                 ) {
16607                         return b;
16608                 }
16609                 return "&#"+cc+";" 
16610             });
16611             if(this.owner.fireEvent('beforesync', this, html) !== false){
16612                 this.el.dom.value = html;
16613                 this.owner.fireEvent('sync', this, html);
16614             }
16615         }
16616     },
16617
16618     /**
16619      * Protected method that will not generally be called directly. Pushes the value of the textarea
16620      * into the iframe editor.
16621      */
16622     pushValue : function(){
16623         if(this.initialized){
16624             var v = this.el.dom.value.trim();
16625             
16626 //            if(v.length < 1){
16627 //                v = '&#160;';
16628 //            }
16629             
16630             if(this.owner.fireEvent('beforepush', this, v) !== false){
16631                 var d = (this.doc.body || this.doc.documentElement);
16632                 d.innerHTML = v;
16633                 this.cleanUpPaste();
16634                 this.el.dom.value = d.innerHTML;
16635                 this.owner.fireEvent('push', this, v);
16636             }
16637         }
16638     },
16639
16640     // private
16641     deferFocus : function(){
16642         this.focus.defer(10, this);
16643     },
16644
16645     // doc'ed in Field
16646     focus : function(){
16647         if(this.win && !this.sourceEditMode){
16648             this.win.focus();
16649         }else{
16650             this.el.focus();
16651         }
16652     },
16653     
16654     assignDocWin: function()
16655     {
16656         var iframe = this.iframe;
16657         
16658          if(Roo.isIE){
16659             this.doc = iframe.contentWindow.document;
16660             this.win = iframe.contentWindow;
16661         } else {
16662 //            if (!Roo.get(this.frameId)) {
16663 //                return;
16664 //            }
16665 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16666 //            this.win = Roo.get(this.frameId).dom.contentWindow;
16667             
16668             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
16669                 return;
16670             }
16671             
16672             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16673             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
16674         }
16675     },
16676     
16677     // private
16678     initEditor : function(){
16679         //console.log("INIT EDITOR");
16680         this.assignDocWin();
16681         
16682         
16683         
16684         this.doc.designMode="on";
16685         this.doc.open();
16686         this.doc.write(this.getDocMarkup());
16687         this.doc.close();
16688         
16689         var dbody = (this.doc.body || this.doc.documentElement);
16690         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
16691         // this copies styles from the containing element into thsi one..
16692         // not sure why we need all of this..
16693         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
16694         
16695         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
16696         //ss['background-attachment'] = 'fixed'; // w3c
16697         dbody.bgProperties = 'fixed'; // ie
16698         //Roo.DomHelper.applyStyles(dbody, ss);
16699         Roo.EventManager.on(this.doc, {
16700             //'mousedown': this.onEditorEvent,
16701             'mouseup': this.onEditorEvent,
16702             'dblclick': this.onEditorEvent,
16703             'click': this.onEditorEvent,
16704             'keyup': this.onEditorEvent,
16705             buffer:100,
16706             scope: this
16707         });
16708         if(Roo.isGecko){
16709             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
16710         }
16711         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
16712             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
16713         }
16714         this.initialized = true;
16715
16716         this.owner.fireEvent('initialize', this);
16717         this.pushValue();
16718     },
16719
16720     // private
16721     onDestroy : function(){
16722         
16723         
16724         
16725         if(this.rendered){
16726             
16727             //for (var i =0; i < this.toolbars.length;i++) {
16728             //    // fixme - ask toolbars for heights?
16729             //    this.toolbars[i].onDestroy();
16730            // }
16731             
16732             //this.wrap.dom.innerHTML = '';
16733             //this.wrap.remove();
16734         }
16735     },
16736
16737     // private
16738     onFirstFocus : function(){
16739         
16740         this.assignDocWin();
16741         
16742         
16743         this.activated = true;
16744          
16745     
16746         if(Roo.isGecko){ // prevent silly gecko errors
16747             this.win.focus();
16748             var s = this.win.getSelection();
16749             if(!s.focusNode || s.focusNode.nodeType != 3){
16750                 var r = s.getRangeAt(0);
16751                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
16752                 r.collapse(true);
16753                 this.deferFocus();
16754             }
16755             try{
16756                 this.execCmd('useCSS', true);
16757                 this.execCmd('styleWithCSS', false);
16758             }catch(e){}
16759         }
16760         this.owner.fireEvent('activate', this);
16761     },
16762
16763     // private
16764     adjustFont: function(btn){
16765         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
16766         //if(Roo.isSafari){ // safari
16767         //    adjust *= 2;
16768        // }
16769         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
16770         if(Roo.isSafari){ // safari
16771             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
16772             v =  (v < 10) ? 10 : v;
16773             v =  (v > 48) ? 48 : v;
16774             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
16775             
16776         }
16777         
16778         
16779         v = Math.max(1, v+adjust);
16780         
16781         this.execCmd('FontSize', v  );
16782     },
16783
16784     onEditorEvent : function(e){
16785         this.owner.fireEvent('editorevent', this, e);
16786       //  this.updateToolbar();
16787         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
16788     },
16789
16790     insertTag : function(tg)
16791     {
16792         // could be a bit smarter... -> wrap the current selected tRoo..
16793         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
16794             
16795             range = this.createRange(this.getSelection());
16796             var wrappingNode = this.doc.createElement(tg.toLowerCase());
16797             wrappingNode.appendChild(range.extractContents());
16798             range.insertNode(wrappingNode);
16799
16800             return;
16801             
16802             
16803             
16804         }
16805         this.execCmd("formatblock",   tg);
16806         
16807     },
16808     
16809     insertText : function(txt)
16810     {
16811         
16812         
16813         var range = this.createRange();
16814         range.deleteContents();
16815                //alert(Sender.getAttribute('label'));
16816                
16817         range.insertNode(this.doc.createTextNode(txt));
16818     } ,
16819     
16820      
16821
16822     /**
16823      * Executes a Midas editor command on the editor document and performs necessary focus and
16824      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
16825      * @param {String} cmd The Midas command
16826      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16827      */
16828     relayCmd : function(cmd, value){
16829         this.win.focus();
16830         this.execCmd(cmd, value);
16831         this.owner.fireEvent('editorevent', this);
16832         //this.updateToolbar();
16833         this.owner.deferFocus();
16834     },
16835
16836     /**
16837      * Executes a Midas editor command directly on the editor document.
16838      * For visual commands, you should use {@link #relayCmd} instead.
16839      * <b>This should only be called after the editor is initialized.</b>
16840      * @param {String} cmd The Midas command
16841      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16842      */
16843     execCmd : function(cmd, value){
16844         this.doc.execCommand(cmd, false, value === undefined ? null : value);
16845         this.syncValue();
16846     },
16847  
16848  
16849    
16850     /**
16851      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
16852      * to insert tRoo.
16853      * @param {String} text | dom node.. 
16854      */
16855     insertAtCursor : function(text)
16856     {
16857         
16858         
16859         
16860         if(!this.activated){
16861             return;
16862         }
16863         /*
16864         if(Roo.isIE){
16865             this.win.focus();
16866             var r = this.doc.selection.createRange();
16867             if(r){
16868                 r.collapse(true);
16869                 r.pasteHTML(text);
16870                 this.syncValue();
16871                 this.deferFocus();
16872             
16873             }
16874             return;
16875         }
16876         */
16877         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
16878             this.win.focus();
16879             
16880             
16881             // from jquery ui (MIT licenced)
16882             var range, node;
16883             var win = this.win;
16884             
16885             if (win.getSelection && win.getSelection().getRangeAt) {
16886                 range = win.getSelection().getRangeAt(0);
16887                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
16888                 range.insertNode(node);
16889             } else if (win.document.selection && win.document.selection.createRange) {
16890                 // no firefox support
16891                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16892                 win.document.selection.createRange().pasteHTML(txt);
16893             } else {
16894                 // no firefox support
16895                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16896                 this.execCmd('InsertHTML', txt);
16897             } 
16898             
16899             this.syncValue();
16900             
16901             this.deferFocus();
16902         }
16903     },
16904  // private
16905     mozKeyPress : function(e){
16906         if(e.ctrlKey){
16907             var c = e.getCharCode(), cmd;
16908           
16909             if(c > 0){
16910                 c = String.fromCharCode(c).toLowerCase();
16911                 switch(c){
16912                     case 'b':
16913                         cmd = 'bold';
16914                         break;
16915                     case 'i':
16916                         cmd = 'italic';
16917                         break;
16918                     
16919                     case 'u':
16920                         cmd = 'underline';
16921                         break;
16922                     
16923                     case 'v':
16924                         this.cleanUpPaste.defer(100, this);
16925                         return;
16926                         
16927                 }
16928                 if(cmd){
16929                     this.win.focus();
16930                     this.execCmd(cmd);
16931                     this.deferFocus();
16932                     e.preventDefault();
16933                 }
16934                 
16935             }
16936         }
16937     },
16938
16939     // private
16940     fixKeys : function(){ // load time branching for fastest keydown performance
16941         if(Roo.isIE){
16942             return function(e){
16943                 var k = e.getKey(), r;
16944                 if(k == e.TAB){
16945                     e.stopEvent();
16946                     r = this.doc.selection.createRange();
16947                     if(r){
16948                         r.collapse(true);
16949                         r.pasteHTML('&#160;&#160;&#160;&#160;');
16950                         this.deferFocus();
16951                     }
16952                     return;
16953                 }
16954                 
16955                 if(k == e.ENTER){
16956                     r = this.doc.selection.createRange();
16957                     if(r){
16958                         var target = r.parentElement();
16959                         if(!target || target.tagName.toLowerCase() != 'li'){
16960                             e.stopEvent();
16961                             r.pasteHTML('<br />');
16962                             r.collapse(false);
16963                             r.select();
16964                         }
16965                     }
16966                 }
16967                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16968                     this.cleanUpPaste.defer(100, this);
16969                     return;
16970                 }
16971                 
16972                 
16973             };
16974         }else if(Roo.isOpera){
16975             return function(e){
16976                 var k = e.getKey();
16977                 if(k == e.TAB){
16978                     e.stopEvent();
16979                     this.win.focus();
16980                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
16981                     this.deferFocus();
16982                 }
16983                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16984                     this.cleanUpPaste.defer(100, this);
16985                     return;
16986                 }
16987                 
16988             };
16989         }else if(Roo.isSafari){
16990             return function(e){
16991                 var k = e.getKey();
16992                 
16993                 if(k == e.TAB){
16994                     e.stopEvent();
16995                     this.execCmd('InsertText','\t');
16996                     this.deferFocus();
16997                     return;
16998                 }
16999                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17000                     this.cleanUpPaste.defer(100, this);
17001                     return;
17002                 }
17003                 
17004              };
17005         }
17006     }(),
17007     
17008     getAllAncestors: function()
17009     {
17010         var p = this.getSelectedNode();
17011         var a = [];
17012         if (!p) {
17013             a.push(p); // push blank onto stack..
17014             p = this.getParentElement();
17015         }
17016         
17017         
17018         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
17019             a.push(p);
17020             p = p.parentNode;
17021         }
17022         a.push(this.doc.body);
17023         return a;
17024     },
17025     lastSel : false,
17026     lastSelNode : false,
17027     
17028     
17029     getSelection : function() 
17030     {
17031         this.assignDocWin();
17032         return Roo.isIE ? this.doc.selection : this.win.getSelection();
17033     },
17034     
17035     getSelectedNode: function() 
17036     {
17037         // this may only work on Gecko!!!
17038         
17039         // should we cache this!!!!
17040         
17041         
17042         
17043          
17044         var range = this.createRange(this.getSelection()).cloneRange();
17045         
17046         if (Roo.isIE) {
17047             var parent = range.parentElement();
17048             while (true) {
17049                 var testRange = range.duplicate();
17050                 testRange.moveToElementText(parent);
17051                 if (testRange.inRange(range)) {
17052                     break;
17053                 }
17054                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
17055                     break;
17056                 }
17057                 parent = parent.parentElement;
17058             }
17059             return parent;
17060         }
17061         
17062         // is ancestor a text element.
17063         var ac =  range.commonAncestorContainer;
17064         if (ac.nodeType == 3) {
17065             ac = ac.parentNode;
17066         }
17067         
17068         var ar = ac.childNodes;
17069          
17070         var nodes = [];
17071         var other_nodes = [];
17072         var has_other_nodes = false;
17073         for (var i=0;i<ar.length;i++) {
17074             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
17075                 continue;
17076             }
17077             // fullly contained node.
17078             
17079             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
17080                 nodes.push(ar[i]);
17081                 continue;
17082             }
17083             
17084             // probably selected..
17085             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
17086                 other_nodes.push(ar[i]);
17087                 continue;
17088             }
17089             // outer..
17090             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
17091                 continue;
17092             }
17093             
17094             
17095             has_other_nodes = true;
17096         }
17097         if (!nodes.length && other_nodes.length) {
17098             nodes= other_nodes;
17099         }
17100         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
17101             return false;
17102         }
17103         
17104         return nodes[0];
17105     },
17106     createRange: function(sel)
17107     {
17108         // this has strange effects when using with 
17109         // top toolbar - not sure if it's a great idea.
17110         //this.editor.contentWindow.focus();
17111         if (typeof sel != "undefined") {
17112             try {
17113                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
17114             } catch(e) {
17115                 return this.doc.createRange();
17116             }
17117         } else {
17118             return this.doc.createRange();
17119         }
17120     },
17121     getParentElement: function()
17122     {
17123         
17124         this.assignDocWin();
17125         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
17126         
17127         var range = this.createRange(sel);
17128          
17129         try {
17130             var p = range.commonAncestorContainer;
17131             while (p.nodeType == 3) { // text node
17132                 p = p.parentNode;
17133             }
17134             return p;
17135         } catch (e) {
17136             return null;
17137         }
17138     
17139     },
17140     /***
17141      *
17142      * Range intersection.. the hard stuff...
17143      *  '-1' = before
17144      *  '0' = hits..
17145      *  '1' = after.
17146      *         [ -- selected range --- ]
17147      *   [fail]                        [fail]
17148      *
17149      *    basically..
17150      *      if end is before start or  hits it. fail.
17151      *      if start is after end or hits it fail.
17152      *
17153      *   if either hits (but other is outside. - then it's not 
17154      *   
17155      *    
17156      **/
17157     
17158     
17159     // @see http://www.thismuchiknow.co.uk/?p=64.
17160     rangeIntersectsNode : function(range, node)
17161     {
17162         var nodeRange = node.ownerDocument.createRange();
17163         try {
17164             nodeRange.selectNode(node);
17165         } catch (e) {
17166             nodeRange.selectNodeContents(node);
17167         }
17168     
17169         var rangeStartRange = range.cloneRange();
17170         rangeStartRange.collapse(true);
17171     
17172         var rangeEndRange = range.cloneRange();
17173         rangeEndRange.collapse(false);
17174     
17175         var nodeStartRange = nodeRange.cloneRange();
17176         nodeStartRange.collapse(true);
17177     
17178         var nodeEndRange = nodeRange.cloneRange();
17179         nodeEndRange.collapse(false);
17180     
17181         return rangeStartRange.compareBoundaryPoints(
17182                  Range.START_TO_START, nodeEndRange) == -1 &&
17183                rangeEndRange.compareBoundaryPoints(
17184                  Range.START_TO_START, nodeStartRange) == 1;
17185         
17186          
17187     },
17188     rangeCompareNode : function(range, node)
17189     {
17190         var nodeRange = node.ownerDocument.createRange();
17191         try {
17192             nodeRange.selectNode(node);
17193         } catch (e) {
17194             nodeRange.selectNodeContents(node);
17195         }
17196         
17197         
17198         range.collapse(true);
17199     
17200         nodeRange.collapse(true);
17201      
17202         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
17203         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
17204          
17205         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
17206         
17207         var nodeIsBefore   =  ss == 1;
17208         var nodeIsAfter    = ee == -1;
17209         
17210         if (nodeIsBefore && nodeIsAfter)
17211             return 0; // outer
17212         if (!nodeIsBefore && nodeIsAfter)
17213             return 1; //right trailed.
17214         
17215         if (nodeIsBefore && !nodeIsAfter)
17216             return 2;  // left trailed.
17217         // fully contined.
17218         return 3;
17219     },
17220
17221     // private? - in a new class?
17222     cleanUpPaste :  function()
17223     {
17224         // cleans up the whole document..
17225         Roo.log('cleanuppaste');
17226         
17227         this.cleanUpChildren(this.doc.body);
17228         var clean = this.cleanWordChars(this.doc.body.innerHTML);
17229         if (clean != this.doc.body.innerHTML) {
17230             this.doc.body.innerHTML = clean;
17231         }
17232         
17233     },
17234     
17235     cleanWordChars : function(input) {// change the chars to hex code
17236         var he = Roo.HtmlEditorCore;
17237         
17238         var output = input;
17239         Roo.each(he.swapCodes, function(sw) { 
17240             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
17241             
17242             output = output.replace(swapper, sw[1]);
17243         });
17244         
17245         return output;
17246     },
17247     
17248     
17249     cleanUpChildren : function (n)
17250     {
17251         if (!n.childNodes.length) {
17252             return;
17253         }
17254         for (var i = n.childNodes.length-1; i > -1 ; i--) {
17255            this.cleanUpChild(n.childNodes[i]);
17256         }
17257     },
17258     
17259     
17260         
17261     
17262     cleanUpChild : function (node)
17263     {
17264         var ed = this;
17265         //console.log(node);
17266         if (node.nodeName == "#text") {
17267             // clean up silly Windows -- stuff?
17268             return; 
17269         }
17270         if (node.nodeName == "#comment") {
17271             node.parentNode.removeChild(node);
17272             // clean up silly Windows -- stuff?
17273             return; 
17274         }
17275         
17276         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
17277             // remove node.
17278             node.parentNode.removeChild(node);
17279             return;
17280             
17281         }
17282         
17283         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
17284         
17285         // remove <a name=....> as rendering on yahoo mailer is borked with this.
17286         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
17287         
17288         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
17289         //    remove_keep_children = true;
17290         //}
17291         
17292         if (remove_keep_children) {
17293             this.cleanUpChildren(node);
17294             // inserts everything just before this node...
17295             while (node.childNodes.length) {
17296                 var cn = node.childNodes[0];
17297                 node.removeChild(cn);
17298                 node.parentNode.insertBefore(cn, node);
17299             }
17300             node.parentNode.removeChild(node);
17301             return;
17302         }
17303         
17304         if (!node.attributes || !node.attributes.length) {
17305             this.cleanUpChildren(node);
17306             return;
17307         }
17308         
17309         function cleanAttr(n,v)
17310         {
17311             
17312             if (v.match(/^\./) || v.match(/^\//)) {
17313                 return;
17314             }
17315             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
17316                 return;
17317             }
17318             if (v.match(/^#/)) {
17319                 return;
17320             }
17321 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
17322             node.removeAttribute(n);
17323             
17324         }
17325         
17326         function cleanStyle(n,v)
17327         {
17328             if (v.match(/expression/)) { //XSS?? should we even bother..
17329                 node.removeAttribute(n);
17330                 return;
17331             }
17332             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
17333             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
17334             
17335             
17336             var parts = v.split(/;/);
17337             var clean = [];
17338             
17339             Roo.each(parts, function(p) {
17340                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
17341                 if (!p.length) {
17342                     return true;
17343                 }
17344                 var l = p.split(':').shift().replace(/\s+/g,'');
17345                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
17346                 
17347                 if ( cblack.indexOf(l) > -1) {
17348 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17349                     //node.removeAttribute(n);
17350                     return true;
17351                 }
17352                 //Roo.log()
17353                 // only allow 'c whitelisted system attributes'
17354                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
17355 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17356                     //node.removeAttribute(n);
17357                     return true;
17358                 }
17359                 
17360                 
17361                  
17362                 
17363                 clean.push(p);
17364                 return true;
17365             });
17366             if (clean.length) { 
17367                 node.setAttribute(n, clean.join(';'));
17368             } else {
17369                 node.removeAttribute(n);
17370             }
17371             
17372         }
17373         
17374         
17375         for (var i = node.attributes.length-1; i > -1 ; i--) {
17376             var a = node.attributes[i];
17377             //console.log(a);
17378             
17379             if (a.name.toLowerCase().substr(0,2)=='on')  {
17380                 node.removeAttribute(a.name);
17381                 continue;
17382             }
17383             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
17384                 node.removeAttribute(a.name);
17385                 continue;
17386             }
17387             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
17388                 cleanAttr(a.name,a.value); // fixme..
17389                 continue;
17390             }
17391             if (a.name == 'style') {
17392                 cleanStyle(a.name,a.value);
17393                 continue;
17394             }
17395             /// clean up MS crap..
17396             // tecnically this should be a list of valid class'es..
17397             
17398             
17399             if (a.name == 'class') {
17400                 if (a.value.match(/^Mso/)) {
17401                     node.className = '';
17402                 }
17403                 
17404                 if (a.value.match(/body/)) {
17405                     node.className = '';
17406                 }
17407                 continue;
17408             }
17409             
17410             // style cleanup!?
17411             // class cleanup?
17412             
17413         }
17414         
17415         
17416         this.cleanUpChildren(node);
17417         
17418         
17419     },
17420     /**
17421      * Clean up MS wordisms...
17422      */
17423     cleanWord : function(node)
17424     {
17425         var _t = this;
17426         var cleanWordChildren = function()
17427         {
17428             if (!node.childNodes.length) {
17429                 return;
17430             }
17431             for (var i = node.childNodes.length-1; i > -1 ; i--) {
17432                _t.cleanWord(node.childNodes[i]);
17433             }
17434         }
17435         
17436         
17437         if (!node) {
17438             this.cleanWord(this.doc.body);
17439             return;
17440         }
17441         if (node.nodeName == "#text") {
17442             // clean up silly Windows -- stuff?
17443             return; 
17444         }
17445         if (node.nodeName == "#comment") {
17446             node.parentNode.removeChild(node);
17447             // clean up silly Windows -- stuff?
17448             return; 
17449         }
17450         
17451         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
17452             node.parentNode.removeChild(node);
17453             return;
17454         }
17455         
17456         // remove - but keep children..
17457         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
17458             while (node.childNodes.length) {
17459                 var cn = node.childNodes[0];
17460                 node.removeChild(cn);
17461                 node.parentNode.insertBefore(cn, node);
17462             }
17463             node.parentNode.removeChild(node);
17464             cleanWordChildren();
17465             return;
17466         }
17467         // clean styles
17468         if (node.className.length) {
17469             
17470             var cn = node.className.split(/\W+/);
17471             var cna = [];
17472             Roo.each(cn, function(cls) {
17473                 if (cls.match(/Mso[a-zA-Z]+/)) {
17474                     return;
17475                 }
17476                 cna.push(cls);
17477             });
17478             node.className = cna.length ? cna.join(' ') : '';
17479             if (!cna.length) {
17480                 node.removeAttribute("class");
17481             }
17482         }
17483         
17484         if (node.hasAttribute("lang")) {
17485             node.removeAttribute("lang");
17486         }
17487         
17488         if (node.hasAttribute("style")) {
17489             
17490             var styles = node.getAttribute("style").split(";");
17491             var nstyle = [];
17492             Roo.each(styles, function(s) {
17493                 if (!s.match(/:/)) {
17494                     return;
17495                 }
17496                 var kv = s.split(":");
17497                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
17498                     return;
17499                 }
17500                 // what ever is left... we allow.
17501                 nstyle.push(s);
17502             });
17503             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
17504             if (!nstyle.length) {
17505                 node.removeAttribute('style');
17506             }
17507         }
17508         
17509         cleanWordChildren();
17510         
17511         
17512     },
17513     domToHTML : function(currentElement, depth, nopadtext) {
17514         
17515             depth = depth || 0;
17516             nopadtext = nopadtext || false;
17517         
17518             if (!currentElement) {
17519                 return this.domToHTML(this.doc.body);
17520             }
17521             
17522             //Roo.log(currentElement);
17523             var j;
17524             var allText = false;
17525             var nodeName = currentElement.nodeName;
17526             var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
17527             
17528             if  (nodeName == '#text') {
17529                 return currentElement.nodeValue;
17530             }
17531             
17532             
17533             var ret = '';
17534             if (nodeName != 'BODY') {
17535                  
17536                 var i = 0;
17537                 // Prints the node tagName, such as <A>, <IMG>, etc
17538                 if (tagName) {
17539                     var attr = [];
17540                     for(i = 0; i < currentElement.attributes.length;i++) {
17541                         // quoting?
17542                         var aname = currentElement.attributes.item(i).name;
17543                         if (!currentElement.attributes.item(i).value.length) {
17544                             continue;
17545                         }
17546                         attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
17547                     }
17548                     
17549                     ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
17550                 } 
17551                 else {
17552                     
17553                     // eack
17554                 }
17555             } else {
17556                 tagName = false;
17557             }
17558             if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
17559                 return ret;
17560             }
17561             if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
17562                 nopadtext = true;
17563             }
17564             
17565             
17566             // Traverse the tree
17567             i = 0;
17568             var currentElementChild = currentElement.childNodes.item(i);
17569             var allText = true;
17570             var innerHTML  = '';
17571             lastnode = '';
17572             while (currentElementChild) {
17573                 // Formatting code (indent the tree so it looks nice on the screen)
17574                 var nopad = nopadtext;
17575                 if (lastnode == 'SPAN') {
17576                     nopad  = true;
17577                 }
17578                 // text
17579                 if  (currentElementChild.nodeName == '#text') {
17580                     var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
17581                     if (!nopad && toadd.length > 80) {
17582                         innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
17583                     }
17584                     innerHTML  += toadd;
17585                     
17586                     i++;
17587                     currentElementChild = currentElement.childNodes.item(i);
17588                     lastNode = '';
17589                     continue;
17590                 }
17591                 allText = false;
17592                 
17593                 innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
17594                     
17595                 // Recursively traverse the tree structure of the child node
17596                 innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
17597                 lastnode = currentElementChild.nodeName;
17598                 i++;
17599                 currentElementChild=currentElement.childNodes.item(i);
17600             }
17601             
17602             ret += innerHTML;
17603             
17604             if (!allText) {
17605                     // The remaining code is mostly for formatting the tree
17606                 ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
17607             }
17608             
17609             
17610             if (tagName) {
17611                 ret+= "</"+tagName+">";
17612             }
17613             return ret;
17614             
17615         }
17616     
17617     // hide stuff that is not compatible
17618     /**
17619      * @event blur
17620      * @hide
17621      */
17622     /**
17623      * @event change
17624      * @hide
17625      */
17626     /**
17627      * @event focus
17628      * @hide
17629      */
17630     /**
17631      * @event specialkey
17632      * @hide
17633      */
17634     /**
17635      * @cfg {String} fieldClass @hide
17636      */
17637     /**
17638      * @cfg {String} focusClass @hide
17639      */
17640     /**
17641      * @cfg {String} autoCreate @hide
17642      */
17643     /**
17644      * @cfg {String} inputType @hide
17645      */
17646     /**
17647      * @cfg {String} invalidClass @hide
17648      */
17649     /**
17650      * @cfg {String} invalidText @hide
17651      */
17652     /**
17653      * @cfg {String} msgFx @hide
17654      */
17655     /**
17656      * @cfg {String} validateOnBlur @hide
17657      */
17658 });
17659
17660 Roo.HtmlEditorCore.white = [
17661         'area', 'br', 'img', 'input', 'hr', 'wbr',
17662         
17663        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
17664        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
17665        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
17666        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
17667        'table',   'ul',         'xmp', 
17668        
17669        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
17670       'thead',   'tr', 
17671      
17672       'dir', 'menu', 'ol', 'ul', 'dl',
17673        
17674       'embed',  'object'
17675 ];
17676
17677
17678 Roo.HtmlEditorCore.black = [
17679     //    'embed',  'object', // enable - backend responsiblity to clean thiese
17680         'applet', // 
17681         'base',   'basefont', 'bgsound', 'blink',  'body', 
17682         'frame',  'frameset', 'head',    'html',   'ilayer', 
17683         'iframe', 'layer',  'link',     'meta',    'object',   
17684         'script', 'style' ,'title',  'xml' // clean later..
17685 ];
17686 Roo.HtmlEditorCore.clean = [
17687     'script', 'style', 'title', 'xml'
17688 ];
17689 Roo.HtmlEditorCore.remove = [
17690     'font'
17691 ];
17692 // attributes..
17693
17694 Roo.HtmlEditorCore.ablack = [
17695     'on'
17696 ];
17697     
17698 Roo.HtmlEditorCore.aclean = [ 
17699     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
17700 ];
17701
17702 // protocols..
17703 Roo.HtmlEditorCore.pwhite= [
17704         'http',  'https',  'mailto'
17705 ];
17706
17707 // white listed style attributes.
17708 Roo.HtmlEditorCore.cwhite= [
17709       //  'text-align', /// default is to allow most things..
17710       
17711          
17712 //        'font-size'//??
17713 ];
17714
17715 // black listed style attributes.
17716 Roo.HtmlEditorCore.cblack= [
17717       //  'font-size' -- this can be set by the project 
17718 ];
17719
17720
17721 Roo.HtmlEditorCore.swapCodes   =[ 
17722     [    8211, "--" ], 
17723     [    8212, "--" ], 
17724     [    8216,  "'" ],  
17725     [    8217, "'" ],  
17726     [    8220, '"' ],  
17727     [    8221, '"' ],  
17728     [    8226, "*" ],  
17729     [    8230, "..." ]
17730 ]; 
17731
17732     /*
17733  * - LGPL
17734  *
17735  * HtmlEditor
17736  * 
17737  */
17738
17739 /**
17740  * @class Roo.bootstrap.HtmlEditor
17741  * @extends Roo.bootstrap.TextArea
17742  * Bootstrap HtmlEditor class
17743
17744  * @constructor
17745  * Create a new HtmlEditor
17746  * @param {Object} config The config object
17747  */
17748
17749 Roo.bootstrap.HtmlEditor = function(config){
17750     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
17751     if (!this.toolbars) {
17752         this.toolbars = [];
17753     }
17754     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
17755     this.addEvents({
17756             /**
17757              * @event initialize
17758              * Fires when the editor is fully initialized (including the iframe)
17759              * @param {HtmlEditor} this
17760              */
17761             initialize: true,
17762             /**
17763              * @event activate
17764              * Fires when the editor is first receives the focus. Any insertion must wait
17765              * until after this event.
17766              * @param {HtmlEditor} this
17767              */
17768             activate: true,
17769              /**
17770              * @event beforesync
17771              * Fires before the textarea is updated with content from the editor iframe. Return false
17772              * to cancel the sync.
17773              * @param {HtmlEditor} this
17774              * @param {String} html
17775              */
17776             beforesync: true,
17777              /**
17778              * @event beforepush
17779              * Fires before the iframe editor is updated with content from the textarea. Return false
17780              * to cancel the push.
17781              * @param {HtmlEditor} this
17782              * @param {String} html
17783              */
17784             beforepush: true,
17785              /**
17786              * @event sync
17787              * Fires when the textarea is updated with content from the editor iframe.
17788              * @param {HtmlEditor} this
17789              * @param {String} html
17790              */
17791             sync: true,
17792              /**
17793              * @event push
17794              * Fires when the iframe editor is updated with content from the textarea.
17795              * @param {HtmlEditor} this
17796              * @param {String} html
17797              */
17798             push: true,
17799              /**
17800              * @event editmodechange
17801              * Fires when the editor switches edit modes
17802              * @param {HtmlEditor} this
17803              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
17804              */
17805             editmodechange: true,
17806             /**
17807              * @event editorevent
17808              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
17809              * @param {HtmlEditor} this
17810              */
17811             editorevent: true,
17812             /**
17813              * @event firstfocus
17814              * Fires when on first focus - needed by toolbars..
17815              * @param {HtmlEditor} this
17816              */
17817             firstfocus: true,
17818             /**
17819              * @event autosave
17820              * Auto save the htmlEditor value as a file into Events
17821              * @param {HtmlEditor} this
17822              */
17823             autosave: true,
17824             /**
17825              * @event savedpreview
17826              * preview the saved version of htmlEditor
17827              * @param {HtmlEditor} this
17828              */
17829             savedpreview: true
17830         });
17831 };
17832
17833
17834 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
17835     
17836     
17837       /**
17838      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
17839      */
17840     toolbars : false,
17841    
17842      /**
17843      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
17844      *                        Roo.resizable.
17845      */
17846     resizable : false,
17847      /**
17848      * @cfg {Number} height (in pixels)
17849      */   
17850     height: 300,
17851    /**
17852      * @cfg {Number} width (in pixels)
17853      */   
17854     width: false,
17855     
17856     /**
17857      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
17858      * 
17859      */
17860     stylesheets: false,
17861     
17862     // id of frame..
17863     frameId: false,
17864     
17865     // private properties
17866     validationEvent : false,
17867     deferHeight: true,
17868     initialized : false,
17869     activated : false,
17870     
17871     onFocus : Roo.emptyFn,
17872     iframePad:3,
17873     hideMode:'offsets',
17874     
17875     
17876     tbContainer : false,
17877     
17878     toolbarContainer :function() {
17879         return this.wrap.select('.x-html-editor-tb',true).first();
17880     },
17881
17882     /**
17883      * Protected method that will not generally be called directly. It
17884      * is called when the editor creates its toolbar. Override this method if you need to
17885      * add custom toolbar buttons.
17886      * @param {HtmlEditor} editor
17887      */
17888     createToolbar : function(){
17889         
17890         Roo.log("create toolbars");
17891         
17892         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
17893         this.toolbars[0].render(this.toolbarContainer());
17894         
17895         return;
17896         
17897 //        if (!editor.toolbars || !editor.toolbars.length) {
17898 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
17899 //        }
17900 //        
17901 //        for (var i =0 ; i < editor.toolbars.length;i++) {
17902 //            editor.toolbars[i] = Roo.factory(
17903 //                    typeof(editor.toolbars[i]) == 'string' ?
17904 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
17905 //                Roo.bootstrap.HtmlEditor);
17906 //            editor.toolbars[i].init(editor);
17907 //        }
17908     },
17909
17910      
17911     // private
17912     onRender : function(ct, position)
17913     {
17914        // Roo.log("Call onRender: " + this.xtype);
17915         var _t = this;
17916         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
17917       
17918         this.wrap = this.inputEl().wrap({
17919             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
17920         });
17921         
17922         this.editorcore.onRender(ct, position);
17923          
17924         if (this.resizable) {
17925             this.resizeEl = new Roo.Resizable(this.wrap, {
17926                 pinned : true,
17927                 wrap: true,
17928                 dynamic : true,
17929                 minHeight : this.height,
17930                 height: this.height,
17931                 handles : this.resizable,
17932                 width: this.width,
17933                 listeners : {
17934                     resize : function(r, w, h) {
17935                         _t.onResize(w,h); // -something
17936                     }
17937                 }
17938             });
17939             
17940         }
17941         this.createToolbar(this);
17942        
17943         
17944         if(!this.width && this.resizable){
17945             this.setSize(this.wrap.getSize());
17946         }
17947         if (this.resizeEl) {
17948             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
17949             // should trigger onReize..
17950         }
17951         
17952     },
17953
17954     // private
17955     onResize : function(w, h)
17956     {
17957         Roo.log('resize: ' +w + ',' + h );
17958         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
17959         var ew = false;
17960         var eh = false;
17961         
17962         if(this.inputEl() ){
17963             if(typeof w == 'number'){
17964                 var aw = w - this.wrap.getFrameWidth('lr');
17965                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
17966                 ew = aw;
17967             }
17968             if(typeof h == 'number'){
17969                  var tbh = -11;  // fixme it needs to tool bar size!
17970                 for (var i =0; i < this.toolbars.length;i++) {
17971                     // fixme - ask toolbars for heights?
17972                     tbh += this.toolbars[i].el.getHeight();
17973                     //if (this.toolbars[i].footer) {
17974                     //    tbh += this.toolbars[i].footer.el.getHeight();
17975                     //}
17976                 }
17977               
17978                 
17979                 
17980                 
17981                 
17982                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
17983                 ah -= 5; // knock a few pixes off for look..
17984                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
17985                 var eh = ah;
17986             }
17987         }
17988         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
17989         this.editorcore.onResize(ew,eh);
17990         
17991     },
17992
17993     /**
17994      * Toggles the editor between standard and source edit mode.
17995      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
17996      */
17997     toggleSourceEdit : function(sourceEditMode)
17998     {
17999         this.editorcore.toggleSourceEdit(sourceEditMode);
18000         
18001         if(this.editorcore.sourceEditMode){
18002             Roo.log('editor - showing textarea');
18003             
18004 //            Roo.log('in');
18005 //            Roo.log(this.syncValue());
18006             this.syncValue();
18007             this.inputEl().removeClass(['hide', 'x-hidden']);
18008             this.inputEl().dom.removeAttribute('tabIndex');
18009             this.inputEl().focus();
18010         }else{
18011             Roo.log('editor - hiding textarea');
18012 //            Roo.log('out')
18013 //            Roo.log(this.pushValue()); 
18014             this.pushValue();
18015             
18016             this.inputEl().addClass(['hide', 'x-hidden']);
18017             this.inputEl().dom.setAttribute('tabIndex', -1);
18018             //this.deferFocus();
18019         }
18020          
18021         if(this.resizable){
18022             this.setSize(this.wrap.getSize());
18023         }
18024         
18025         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
18026     },
18027  
18028     // private (for BoxComponent)
18029     adjustSize : Roo.BoxComponent.prototype.adjustSize,
18030
18031     // private (for BoxComponent)
18032     getResizeEl : function(){
18033         return this.wrap;
18034     },
18035
18036     // private (for BoxComponent)
18037     getPositionEl : function(){
18038         return this.wrap;
18039     },
18040
18041     // private
18042     initEvents : function(){
18043         this.originalValue = this.getValue();
18044     },
18045
18046 //    /**
18047 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18048 //     * @method
18049 //     */
18050 //    markInvalid : Roo.emptyFn,
18051 //    /**
18052 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18053 //     * @method
18054 //     */
18055 //    clearInvalid : Roo.emptyFn,
18056
18057     setValue : function(v){
18058         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
18059         this.editorcore.pushValue();
18060     },
18061
18062      
18063     // private
18064     deferFocus : function(){
18065         this.focus.defer(10, this);
18066     },
18067
18068     // doc'ed in Field
18069     focus : function(){
18070         this.editorcore.focus();
18071         
18072     },
18073       
18074
18075     // private
18076     onDestroy : function(){
18077         
18078         
18079         
18080         if(this.rendered){
18081             
18082             for (var i =0; i < this.toolbars.length;i++) {
18083                 // fixme - ask toolbars for heights?
18084                 this.toolbars[i].onDestroy();
18085             }
18086             
18087             this.wrap.dom.innerHTML = '';
18088             this.wrap.remove();
18089         }
18090     },
18091
18092     // private
18093     onFirstFocus : function(){
18094         //Roo.log("onFirstFocus");
18095         this.editorcore.onFirstFocus();
18096          for (var i =0; i < this.toolbars.length;i++) {
18097             this.toolbars[i].onFirstFocus();
18098         }
18099         
18100     },
18101     
18102     // private
18103     syncValue : function()
18104     {   
18105         this.editorcore.syncValue();
18106     },
18107     
18108     pushValue : function()
18109     {   
18110         this.editorcore.pushValue();
18111     }
18112      
18113     
18114     // hide stuff that is not compatible
18115     /**
18116      * @event blur
18117      * @hide
18118      */
18119     /**
18120      * @event change
18121      * @hide
18122      */
18123     /**
18124      * @event focus
18125      * @hide
18126      */
18127     /**
18128      * @event specialkey
18129      * @hide
18130      */
18131     /**
18132      * @cfg {String} fieldClass @hide
18133      */
18134     /**
18135      * @cfg {String} focusClass @hide
18136      */
18137     /**
18138      * @cfg {String} autoCreate @hide
18139      */
18140     /**
18141      * @cfg {String} inputType @hide
18142      */
18143     /**
18144      * @cfg {String} invalidClass @hide
18145      */
18146     /**
18147      * @cfg {String} invalidText @hide
18148      */
18149     /**
18150      * @cfg {String} msgFx @hide
18151      */
18152     /**
18153      * @cfg {String} validateOnBlur @hide
18154      */
18155 });
18156  
18157     
18158    
18159    
18160    
18161       
18162 Roo.namespace('Roo.bootstrap.htmleditor');
18163 /**
18164  * @class Roo.bootstrap.HtmlEditorToolbar1
18165  * Basic Toolbar
18166  * 
18167  * Usage:
18168  *
18169  new Roo.bootstrap.HtmlEditor({
18170     ....
18171     toolbars : [
18172         new Roo.bootstrap.HtmlEditorToolbar1({
18173             disable : { fonts: 1 , format: 1, ..., ... , ...],
18174             btns : [ .... ]
18175         })
18176     }
18177      
18178  * 
18179  * @cfg {Object} disable List of elements to disable..
18180  * @cfg {Array} btns List of additional buttons.
18181  * 
18182  * 
18183  * NEEDS Extra CSS? 
18184  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
18185  */
18186  
18187 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
18188 {
18189     
18190     Roo.apply(this, config);
18191     
18192     // default disabled, based on 'good practice'..
18193     this.disable = this.disable || {};
18194     Roo.applyIf(this.disable, {
18195         fontSize : true,
18196         colors : true,
18197         specialElements : true
18198     });
18199     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
18200     
18201     this.editor = config.editor;
18202     this.editorcore = config.editor.editorcore;
18203     
18204     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
18205     
18206     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
18207     // dont call parent... till later.
18208 }
18209 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
18210      
18211     bar : true,
18212     
18213     editor : false,
18214     editorcore : false,
18215     
18216     
18217     formats : [
18218         "p" ,  
18219         "h1","h2","h3","h4","h5","h6", 
18220         "pre", "code", 
18221         "abbr", "acronym", "address", "cite", "samp", "var",
18222         'div','span'
18223     ],
18224     
18225     onRender : function(ct, position)
18226     {
18227        // Roo.log("Call onRender: " + this.xtype);
18228         
18229        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
18230        Roo.log(this.el);
18231        this.el.dom.style.marginBottom = '0';
18232        var _this = this;
18233        var editorcore = this.editorcore;
18234        var editor= this.editor;
18235        
18236        var children = [];
18237        var btn = function(id,cmd , toggle, handler){
18238        
18239             var  event = toggle ? 'toggle' : 'click';
18240        
18241             var a = {
18242                 size : 'sm',
18243                 xtype: 'Button',
18244                 xns: Roo.bootstrap,
18245                 glyphicon : id,
18246                 cmd : id || cmd,
18247                 enableToggle:toggle !== false,
18248                 //html : 'submit'
18249                 pressed : toggle ? false : null,
18250                 listeners : {}
18251             }
18252             a.listeners[toggle ? 'toggle' : 'click'] = function() {
18253                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
18254             }
18255             children.push(a);
18256             return a;
18257        }
18258         
18259         var style = {
18260                 xtype: 'Button',
18261                 size : 'sm',
18262                 xns: Roo.bootstrap,
18263                 glyphicon : 'font',
18264                 //html : 'submit'
18265                 menu : {
18266                     xtype: 'Menu',
18267                     xns: Roo.bootstrap,
18268                     items:  []
18269                 }
18270         };
18271         Roo.each(this.formats, function(f) {
18272             style.menu.items.push({
18273                 xtype :'MenuItem',
18274                 xns: Roo.bootstrap,
18275                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
18276                 tagname : f,
18277                 listeners : {
18278                     click : function()
18279                     {
18280                         editorcore.insertTag(this.tagname);
18281                         editor.focus();
18282                     }
18283                 }
18284                 
18285             });
18286         });
18287          children.push(style);   
18288             
18289             
18290         btn('bold',false,true);
18291         btn('italic',false,true);
18292         btn('align-left', 'justifyleft',true);
18293         btn('align-center', 'justifycenter',true);
18294         btn('align-right' , 'justifyright',true);
18295         btn('link', false, false, function(btn) {
18296             //Roo.log("create link?");
18297             var url = prompt(this.createLinkText, this.defaultLinkValue);
18298             if(url && url != 'http:/'+'/'){
18299                 this.editorcore.relayCmd('createlink', url);
18300             }
18301         }),
18302         btn('list','insertunorderedlist',true);
18303         btn('pencil', false,true, function(btn){
18304                 Roo.log(this);
18305                 
18306                 this.toggleSourceEdit(btn.pressed);
18307         });
18308         /*
18309         var cog = {
18310                 xtype: 'Button',
18311                 size : 'sm',
18312                 xns: Roo.bootstrap,
18313                 glyphicon : 'cog',
18314                 //html : 'submit'
18315                 menu : {
18316                     xtype: 'Menu',
18317                     xns: Roo.bootstrap,
18318                     items:  []
18319                 }
18320         };
18321         
18322         cog.menu.items.push({
18323             xtype :'MenuItem',
18324             xns: Roo.bootstrap,
18325             html : Clean styles,
18326             tagname : f,
18327             listeners : {
18328                 click : function()
18329                 {
18330                     editorcore.insertTag(this.tagname);
18331                     editor.focus();
18332                 }
18333             }
18334             
18335         });
18336        */
18337         
18338          
18339        this.xtype = 'NavSimplebar';
18340         
18341         for(var i=0;i< children.length;i++) {
18342             
18343             this.buttons.add(this.addxtypeChild(children[i]));
18344             
18345         }
18346         
18347         editor.on('editorevent', this.updateToolbar, this);
18348     },
18349     onBtnClick : function(id)
18350     {
18351        this.editorcore.relayCmd(id);
18352        this.editorcore.focus();
18353     },
18354     
18355     /**
18356      * Protected method that will not generally be called directly. It triggers
18357      * a toolbar update by reading the markup state of the current selection in the editor.
18358      */
18359     updateToolbar: function(){
18360
18361         if(!this.editorcore.activated){
18362             this.editor.onFirstFocus(); // is this neeed?
18363             return;
18364         }
18365
18366         var btns = this.buttons; 
18367         var doc = this.editorcore.doc;
18368         btns.get('bold').setActive(doc.queryCommandState('bold'));
18369         btns.get('italic').setActive(doc.queryCommandState('italic'));
18370         //btns.get('underline').setActive(doc.queryCommandState('underline'));
18371         
18372         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
18373         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
18374         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
18375         
18376         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
18377         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
18378          /*
18379         
18380         var ans = this.editorcore.getAllAncestors();
18381         if (this.formatCombo) {
18382             
18383             
18384             var store = this.formatCombo.store;
18385             this.formatCombo.setValue("");
18386             for (var i =0; i < ans.length;i++) {
18387                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
18388                     // select it..
18389                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
18390                     break;
18391                 }
18392             }
18393         }
18394         
18395         
18396         
18397         // hides menus... - so this cant be on a menu...
18398         Roo.bootstrap.MenuMgr.hideAll();
18399         */
18400         Roo.bootstrap.MenuMgr.hideAll();
18401         //this.editorsyncValue();
18402     },
18403     onFirstFocus: function() {
18404         this.buttons.each(function(item){
18405            item.enable();
18406         });
18407     },
18408     toggleSourceEdit : function(sourceEditMode){
18409         
18410           
18411         if(sourceEditMode){
18412             Roo.log("disabling buttons");
18413            this.buttons.each( function(item){
18414                 if(item.cmd != 'pencil'){
18415                     item.disable();
18416                 }
18417             });
18418           
18419         }else{
18420             Roo.log("enabling buttons");
18421             if(this.editorcore.initialized){
18422                 this.buttons.each( function(item){
18423                     item.enable();
18424                 });
18425             }
18426             
18427         }
18428         Roo.log("calling toggole on editor");
18429         // tell the editor that it's been pressed..
18430         this.editor.toggleSourceEdit(sourceEditMode);
18431        
18432     }
18433 });
18434
18435
18436
18437
18438
18439 /**
18440  * @class Roo.bootstrap.Table.AbstractSelectionModel
18441  * @extends Roo.util.Observable
18442  * Abstract base class for grid SelectionModels.  It provides the interface that should be
18443  * implemented by descendant classes.  This class should not be directly instantiated.
18444  * @constructor
18445  */
18446 Roo.bootstrap.Table.AbstractSelectionModel = function(){
18447     this.locked = false;
18448     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
18449 };
18450
18451
18452 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
18453     /** @ignore Called by the grid automatically. Do not call directly. */
18454     init : function(grid){
18455         this.grid = grid;
18456         this.initEvents();
18457     },
18458
18459     /**
18460      * Locks the selections.
18461      */
18462     lock : function(){
18463         this.locked = true;
18464     },
18465
18466     /**
18467      * Unlocks the selections.
18468      */
18469     unlock : function(){
18470         this.locked = false;
18471     },
18472
18473     /**
18474      * Returns true if the selections are locked.
18475      * @return {Boolean}
18476      */
18477     isLocked : function(){
18478         return this.locked;
18479     }
18480 });
18481 /**
18482  * @extends Roo.bootstrap.Table.AbstractSelectionModel
18483  * @class Roo.bootstrap.Table.RowSelectionModel
18484  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
18485  * It supports multiple selections and keyboard selection/navigation. 
18486  * @constructor
18487  * @param {Object} config
18488  */
18489
18490 Roo.bootstrap.Table.RowSelectionModel = function(config){
18491     Roo.apply(this, config);
18492     this.selections = new Roo.util.MixedCollection(false, function(o){
18493         return o.id;
18494     });
18495
18496     this.last = false;
18497     this.lastActive = false;
18498
18499     this.addEvents({
18500         /**
18501              * @event selectionchange
18502              * Fires when the selection changes
18503              * @param {SelectionModel} this
18504              */
18505             "selectionchange" : true,
18506         /**
18507              * @event afterselectionchange
18508              * Fires after the selection changes (eg. by key press or clicking)
18509              * @param {SelectionModel} this
18510              */
18511             "afterselectionchange" : true,
18512         /**
18513              * @event beforerowselect
18514              * Fires when a row is selected being selected, return false to cancel.
18515              * @param {SelectionModel} this
18516              * @param {Number} rowIndex The selected index
18517              * @param {Boolean} keepExisting False if other selections will be cleared
18518              */
18519             "beforerowselect" : true,
18520         /**
18521              * @event rowselect
18522              * Fires when a row is selected.
18523              * @param {SelectionModel} this
18524              * @param {Number} rowIndex The selected index
18525              * @param {Roo.data.Record} r The record
18526              */
18527             "rowselect" : true,
18528         /**
18529              * @event rowdeselect
18530              * Fires when a row is deselected.
18531              * @param {SelectionModel} this
18532              * @param {Number} rowIndex The selected index
18533              */
18534         "rowdeselect" : true
18535     });
18536     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
18537     this.locked = false;
18538 };
18539
18540 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
18541     /**
18542      * @cfg {Boolean} singleSelect
18543      * True to allow selection of only one row at a time (defaults to false)
18544      */
18545     singleSelect : false,
18546
18547     // private
18548     initEvents : function(){
18549
18550         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
18551             this.grid.on("mousedown", this.handleMouseDown, this);
18552         }else{ // allow click to work like normal
18553             this.grid.on("rowclick", this.handleDragableRowClick, this);
18554         }
18555
18556         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
18557             "up" : function(e){
18558                 if(!e.shiftKey){
18559                     this.selectPrevious(e.shiftKey);
18560                 }else if(this.last !== false && this.lastActive !== false){
18561                     var last = this.last;
18562                     this.selectRange(this.last,  this.lastActive-1);
18563                     this.grid.getView().focusRow(this.lastActive);
18564                     if(last !== false){
18565                         this.last = last;
18566                     }
18567                 }else{
18568                     this.selectFirstRow();
18569                 }
18570                 this.fireEvent("afterselectionchange", this);
18571             },
18572             "down" : function(e){
18573                 if(!e.shiftKey){
18574                     this.selectNext(e.shiftKey);
18575                 }else if(this.last !== false && this.lastActive !== false){
18576                     var last = this.last;
18577                     this.selectRange(this.last,  this.lastActive+1);
18578                     this.grid.getView().focusRow(this.lastActive);
18579                     if(last !== false){
18580                         this.last = last;
18581                     }
18582                 }else{
18583                     this.selectFirstRow();
18584                 }
18585                 this.fireEvent("afterselectionchange", this);
18586             },
18587             scope: this
18588         });
18589
18590         var view = this.grid.view;
18591         view.on("refresh", this.onRefresh, this);
18592         view.on("rowupdated", this.onRowUpdated, this);
18593         view.on("rowremoved", this.onRemove, this);
18594     },
18595
18596     // private
18597     onRefresh : function(){
18598         var ds = this.grid.dataSource, i, v = this.grid.view;
18599         var s = this.selections;
18600         s.each(function(r){
18601             if((i = ds.indexOfId(r.id)) != -1){
18602                 v.onRowSelect(i);
18603             }else{
18604                 s.remove(r);
18605             }
18606         });
18607     },
18608
18609     // private
18610     onRemove : function(v, index, r){
18611         this.selections.remove(r);
18612     },
18613
18614     // private
18615     onRowUpdated : function(v, index, r){
18616         if(this.isSelected(r)){
18617             v.onRowSelect(index);
18618         }
18619     },
18620
18621     /**
18622      * Select records.
18623      * @param {Array} records The records to select
18624      * @param {Boolean} keepExisting (optional) True to keep existing selections
18625      */
18626     selectRecords : function(records, keepExisting){
18627         if(!keepExisting){
18628             this.clearSelections();
18629         }
18630         var ds = this.grid.dataSource;
18631         for(var i = 0, len = records.length; i < len; i++){
18632             this.selectRow(ds.indexOf(records[i]), true);
18633         }
18634     },
18635
18636     /**
18637      * Gets the number of selected rows.
18638      * @return {Number}
18639      */
18640     getCount : function(){
18641         return this.selections.length;
18642     },
18643
18644     /**
18645      * Selects the first row in the grid.
18646      */
18647     selectFirstRow : function(){
18648         this.selectRow(0);
18649     },
18650
18651     /**
18652      * Select the last row.
18653      * @param {Boolean} keepExisting (optional) True to keep existing selections
18654      */
18655     selectLastRow : function(keepExisting){
18656         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
18657     },
18658
18659     /**
18660      * Selects the row immediately following the last selected row.
18661      * @param {Boolean} keepExisting (optional) True to keep existing selections
18662      */
18663     selectNext : function(keepExisting){
18664         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
18665             this.selectRow(this.last+1, keepExisting);
18666             this.grid.getView().focusRow(this.last);
18667         }
18668     },
18669
18670     /**
18671      * Selects the row that precedes the last selected row.
18672      * @param {Boolean} keepExisting (optional) True to keep existing selections
18673      */
18674     selectPrevious : function(keepExisting){
18675         if(this.last){
18676             this.selectRow(this.last-1, keepExisting);
18677             this.grid.getView().focusRow(this.last);
18678         }
18679     },
18680
18681     /**
18682      * Returns the selected records
18683      * @return {Array} Array of selected records
18684      */
18685     getSelections : function(){
18686         return [].concat(this.selections.items);
18687     },
18688
18689     /**
18690      * Returns the first selected record.
18691      * @return {Record}
18692      */
18693     getSelected : function(){
18694         return this.selections.itemAt(0);
18695     },
18696
18697
18698     /**
18699      * Clears all selections.
18700      */
18701     clearSelections : function(fast){
18702         if(this.locked) return;
18703         if(fast !== true){
18704             var ds = this.grid.dataSource;
18705             var s = this.selections;
18706             s.each(function(r){
18707                 this.deselectRow(ds.indexOfId(r.id));
18708             }, this);
18709             s.clear();
18710         }else{
18711             this.selections.clear();
18712         }
18713         this.last = false;
18714     },
18715
18716
18717     /**
18718      * Selects all rows.
18719      */
18720     selectAll : function(){
18721         if(this.locked) return;
18722         this.selections.clear();
18723         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
18724             this.selectRow(i, true);
18725         }
18726     },
18727
18728     /**
18729      * Returns True if there is a selection.
18730      * @return {Boolean}
18731      */
18732     hasSelection : function(){
18733         return this.selections.length > 0;
18734     },
18735
18736     /**
18737      * Returns True if the specified row is selected.
18738      * @param {Number/Record} record The record or index of the record to check
18739      * @return {Boolean}
18740      */
18741     isSelected : function(index){
18742         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
18743         return (r && this.selections.key(r.id) ? true : false);
18744     },
18745
18746     /**
18747      * Returns True if the specified record id is selected.
18748      * @param {String} id The id of record to check
18749      * @return {Boolean}
18750      */
18751     isIdSelected : function(id){
18752         return (this.selections.key(id) ? true : false);
18753     },
18754
18755     // private
18756     handleMouseDown : function(e, t){
18757         var view = this.grid.getView(), rowIndex;
18758         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
18759             return;
18760         };
18761         if(e.shiftKey && this.last !== false){
18762             var last = this.last;
18763             this.selectRange(last, rowIndex, e.ctrlKey);
18764             this.last = last; // reset the last
18765             view.focusRow(rowIndex);
18766         }else{
18767             var isSelected = this.isSelected(rowIndex);
18768             if(e.button !== 0 && isSelected){
18769                 view.focusRow(rowIndex);
18770             }else if(e.ctrlKey && isSelected){
18771                 this.deselectRow(rowIndex);
18772             }else if(!isSelected){
18773                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
18774                 view.focusRow(rowIndex);
18775             }
18776         }
18777         this.fireEvent("afterselectionchange", this);
18778     },
18779     // private
18780     handleDragableRowClick :  function(grid, rowIndex, e) 
18781     {
18782         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
18783             this.selectRow(rowIndex, false);
18784             grid.view.focusRow(rowIndex);
18785              this.fireEvent("afterselectionchange", this);
18786         }
18787     },
18788     
18789     /**
18790      * Selects multiple rows.
18791      * @param {Array} rows Array of the indexes of the row to select
18792      * @param {Boolean} keepExisting (optional) True to keep existing selections
18793      */
18794     selectRows : function(rows, keepExisting){
18795         if(!keepExisting){
18796             this.clearSelections();
18797         }
18798         for(var i = 0, len = rows.length; i < len; i++){
18799             this.selectRow(rows[i], true);
18800         }
18801     },
18802
18803     /**
18804      * Selects a range of rows. All rows in between startRow and endRow are also selected.
18805      * @param {Number} startRow The index of the first row in the range
18806      * @param {Number} endRow The index of the last row in the range
18807      * @param {Boolean} keepExisting (optional) True to retain existing selections
18808      */
18809     selectRange : function(startRow, endRow, keepExisting){
18810         if(this.locked) return;
18811         if(!keepExisting){
18812             this.clearSelections();
18813         }
18814         if(startRow <= endRow){
18815             for(var i = startRow; i <= endRow; i++){
18816                 this.selectRow(i, true);
18817             }
18818         }else{
18819             for(var i = startRow; i >= endRow; i--){
18820                 this.selectRow(i, true);
18821             }
18822         }
18823     },
18824
18825     /**
18826      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
18827      * @param {Number} startRow The index of the first row in the range
18828      * @param {Number} endRow The index of the last row in the range
18829      */
18830     deselectRange : function(startRow, endRow, preventViewNotify){
18831         if(this.locked) return;
18832         for(var i = startRow; i <= endRow; i++){
18833             this.deselectRow(i, preventViewNotify);
18834         }
18835     },
18836
18837     /**
18838      * Selects a row.
18839      * @param {Number} row The index of the row to select
18840      * @param {Boolean} keepExisting (optional) True to keep existing selections
18841      */
18842     selectRow : function(index, keepExisting, preventViewNotify){
18843         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
18844         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
18845             if(!keepExisting || this.singleSelect){
18846                 this.clearSelections();
18847             }
18848             var r = this.grid.dataSource.getAt(index);
18849             this.selections.add(r);
18850             this.last = this.lastActive = index;
18851             if(!preventViewNotify){
18852                 this.grid.getView().onRowSelect(index);
18853             }
18854             this.fireEvent("rowselect", this, index, r);
18855             this.fireEvent("selectionchange", this);
18856         }
18857     },
18858
18859     /**
18860      * Deselects a row.
18861      * @param {Number} row The index of the row to deselect
18862      */
18863     deselectRow : function(index, preventViewNotify){
18864         if(this.locked) return;
18865         if(this.last == index){
18866             this.last = false;
18867         }
18868         if(this.lastActive == index){
18869             this.lastActive = false;
18870         }
18871         var r = this.grid.dataSource.getAt(index);
18872         this.selections.remove(r);
18873         if(!preventViewNotify){
18874             this.grid.getView().onRowDeselect(index);
18875         }
18876         this.fireEvent("rowdeselect", this, index);
18877         this.fireEvent("selectionchange", this);
18878     },
18879
18880     // private
18881     restoreLast : function(){
18882         if(this._last){
18883             this.last = this._last;
18884         }
18885     },
18886
18887     // private
18888     acceptsNav : function(row, col, cm){
18889         return !cm.isHidden(col) && cm.isCellEditable(col, row);
18890     },
18891
18892     // private
18893     onEditorKey : function(field, e){
18894         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
18895         if(k == e.TAB){
18896             e.stopEvent();
18897             ed.completeEdit();
18898             if(e.shiftKey){
18899                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
18900             }else{
18901                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
18902             }
18903         }else if(k == e.ENTER && !e.ctrlKey){
18904             e.stopEvent();
18905             ed.completeEdit();
18906             if(e.shiftKey){
18907                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
18908             }else{
18909                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
18910             }
18911         }else if(k == e.ESC){
18912             ed.cancelEdit();
18913         }
18914         if(newCell){
18915             g.startEditing(newCell[0], newCell[1]);
18916         }
18917     }
18918 });/*
18919  * Based on:
18920  * Ext JS Library 1.1.1
18921  * Copyright(c) 2006-2007, Ext JS, LLC.
18922  *
18923  * Originally Released Under LGPL - original licence link has changed is not relivant.
18924  *
18925  * Fork - LGPL
18926  * <script type="text/javascript">
18927  */
18928  
18929 /**
18930  * @class Roo.bootstrap.PagingToolbar
18931  * @extends Roo.Row
18932  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
18933  * @constructor
18934  * Create a new PagingToolbar
18935  * @param {Object} config The config object
18936  */
18937 Roo.bootstrap.PagingToolbar = function(config)
18938 {
18939     // old args format still supported... - xtype is prefered..
18940         // created from xtype...
18941     var ds = config.dataSource;
18942     this.toolbarItems = [];
18943     if (config.items) {
18944         this.toolbarItems = config.items;
18945 //        config.items = [];
18946     }
18947     
18948     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
18949     this.ds = ds;
18950     this.cursor = 0;
18951     if (ds) { 
18952         this.bind(ds);
18953     }
18954     
18955     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
18956     
18957 };
18958
18959 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
18960     /**
18961      * @cfg {Roo.data.Store} dataSource
18962      * The underlying data store providing the paged data
18963      */
18964     /**
18965      * @cfg {String/HTMLElement/Element} container
18966      * container The id or element that will contain the toolbar
18967      */
18968     /**
18969      * @cfg {Boolean} displayInfo
18970      * True to display the displayMsg (defaults to false)
18971      */
18972     /**
18973      * @cfg {Number} pageSize
18974      * The number of records to display per page (defaults to 20)
18975      */
18976     pageSize: 20,
18977     /**
18978      * @cfg {String} displayMsg
18979      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
18980      */
18981     displayMsg : 'Displaying {0} - {1} of {2}',
18982     /**
18983      * @cfg {String} emptyMsg
18984      * The message to display when no records are found (defaults to "No data to display")
18985      */
18986     emptyMsg : 'No data to display',
18987     /**
18988      * Customizable piece of the default paging text (defaults to "Page")
18989      * @type String
18990      */
18991     beforePageText : "Page",
18992     /**
18993      * Customizable piece of the default paging text (defaults to "of %0")
18994      * @type String
18995      */
18996     afterPageText : "of {0}",
18997     /**
18998      * Customizable piece of the default paging text (defaults to "First Page")
18999      * @type String
19000      */
19001     firstText : "First Page",
19002     /**
19003      * Customizable piece of the default paging text (defaults to "Previous Page")
19004      * @type String
19005      */
19006     prevText : "Previous Page",
19007     /**
19008      * Customizable piece of the default paging text (defaults to "Next Page")
19009      * @type String
19010      */
19011     nextText : "Next Page",
19012     /**
19013      * Customizable piece of the default paging text (defaults to "Last Page")
19014      * @type String
19015      */
19016     lastText : "Last Page",
19017     /**
19018      * Customizable piece of the default paging text (defaults to "Refresh")
19019      * @type String
19020      */
19021     refreshText : "Refresh",
19022
19023     buttons : false,
19024     // private
19025     onRender : function(ct, position) 
19026     {
19027         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
19028         this.navgroup.parentId = this.id;
19029         this.navgroup.onRender(this.el, null);
19030         // add the buttons to the navgroup
19031         
19032         if(this.displayInfo){
19033             Roo.log(this.el.select('ul.navbar-nav',true).first());
19034             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
19035             this.displayEl = this.el.select('.x-paging-info', true).first();
19036 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
19037 //            this.displayEl = navel.el.select('span',true).first();
19038         }
19039         
19040         var _this = this;
19041         
19042         if(this.buttons){
19043             Roo.each(_this.buttons, function(e){
19044                Roo.factory(e).onRender(_this.el, null);
19045             });
19046         }
19047             
19048         Roo.each(_this.toolbarItems, function(e) {
19049             _this.navgroup.addItem(e);
19050         });
19051         
19052         this.first = this.navgroup.addItem({
19053             tooltip: this.firstText,
19054             cls: "prev",
19055             icon : 'fa fa-backward',
19056             disabled: true,
19057             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
19058         });
19059         
19060         this.prev =  this.navgroup.addItem({
19061             tooltip: this.prevText,
19062             cls: "prev",
19063             icon : 'fa fa-step-backward',
19064             disabled: true,
19065             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
19066         });
19067     //this.addSeparator();
19068         
19069         
19070         var field = this.navgroup.addItem( {
19071             tagtype : 'span',
19072             cls : 'x-paging-position',
19073             
19074             html : this.beforePageText  +
19075                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
19076                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
19077          } ); //?? escaped?
19078         
19079         this.field = field.el.select('input', true).first();
19080         this.field.on("keydown", this.onPagingKeydown, this);
19081         this.field.on("focus", function(){this.dom.select();});
19082     
19083     
19084         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
19085         //this.field.setHeight(18);
19086         //this.addSeparator();
19087         this.next = this.navgroup.addItem({
19088             tooltip: this.nextText,
19089             cls: "next",
19090             html : ' <i class="fa fa-step-forward">',
19091             disabled: true,
19092             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
19093         });
19094         this.last = this.navgroup.addItem({
19095             tooltip: this.lastText,
19096             icon : 'fa fa-forward',
19097             cls: "next",
19098             disabled: true,
19099             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
19100         });
19101     //this.addSeparator();
19102         this.loading = this.navgroup.addItem({
19103             tooltip: this.refreshText,
19104             icon: 'fa fa-refresh',
19105             
19106             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
19107         });
19108
19109     },
19110
19111     // private
19112     updateInfo : function(){
19113         if(this.displayEl){
19114             var count = this.ds.getCount();
19115             var msg = count == 0 ?
19116                 this.emptyMsg :
19117                 String.format(
19118                     this.displayMsg,
19119                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
19120                 );
19121             this.displayEl.update(msg);
19122         }
19123     },
19124
19125     // private
19126     onLoad : function(ds, r, o){
19127        this.cursor = o.params ? o.params.start : 0;
19128        var d = this.getPageData(),
19129             ap = d.activePage,
19130             ps = d.pages;
19131         
19132        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
19133        this.field.dom.value = ap;
19134        this.first.setDisabled(ap == 1);
19135        this.prev.setDisabled(ap == 1);
19136        this.next.setDisabled(ap == ps);
19137        this.last.setDisabled(ap == ps);
19138        this.loading.enable();
19139        this.updateInfo();
19140     },
19141
19142     // private
19143     getPageData : function(){
19144         var total = this.ds.getTotalCount();
19145         return {
19146             total : total,
19147             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
19148             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
19149         };
19150     },
19151
19152     // private
19153     onLoadError : function(){
19154         this.loading.enable();
19155     },
19156
19157     // private
19158     onPagingKeydown : function(e){
19159         var k = e.getKey();
19160         var d = this.getPageData();
19161         if(k == e.RETURN){
19162             var v = this.field.dom.value, pageNum;
19163             if(!v || isNaN(pageNum = parseInt(v, 10))){
19164                 this.field.dom.value = d.activePage;
19165                 return;
19166             }
19167             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
19168             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19169             e.stopEvent();
19170         }
19171         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))
19172         {
19173           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
19174           this.field.dom.value = pageNum;
19175           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
19176           e.stopEvent();
19177         }
19178         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19179         {
19180           var v = this.field.dom.value, pageNum; 
19181           var increment = (e.shiftKey) ? 10 : 1;
19182           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19183             increment *= -1;
19184           if(!v || isNaN(pageNum = parseInt(v, 10))) {
19185             this.field.dom.value = d.activePage;
19186             return;
19187           }
19188           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
19189           {
19190             this.field.dom.value = parseInt(v, 10) + increment;
19191             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
19192             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19193           }
19194           e.stopEvent();
19195         }
19196     },
19197
19198     // private
19199     beforeLoad : function(){
19200         if(this.loading){
19201             this.loading.disable();
19202         }
19203     },
19204
19205     // private
19206     onClick : function(which){
19207         var ds = this.ds;
19208         if (!ds) {
19209             return;
19210         }
19211         switch(which){
19212             case "first":
19213                 ds.load({params:{start: 0, limit: this.pageSize}});
19214             break;
19215             case "prev":
19216                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
19217             break;
19218             case "next":
19219                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
19220             break;
19221             case "last":
19222                 var total = ds.getTotalCount();
19223                 var extra = total % this.pageSize;
19224                 var lastStart = extra ? (total - extra) : total-this.pageSize;
19225                 ds.load({params:{start: lastStart, limit: this.pageSize}});
19226             break;
19227             case "refresh":
19228                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
19229             break;
19230         }
19231     },
19232
19233     /**
19234      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
19235      * @param {Roo.data.Store} store The data store to unbind
19236      */
19237     unbind : function(ds){
19238         ds.un("beforeload", this.beforeLoad, this);
19239         ds.un("load", this.onLoad, this);
19240         ds.un("loadexception", this.onLoadError, this);
19241         ds.un("remove", this.updateInfo, this);
19242         ds.un("add", this.updateInfo, this);
19243         this.ds = undefined;
19244     },
19245
19246     /**
19247      * Binds the paging toolbar to the specified {@link Roo.data.Store}
19248      * @param {Roo.data.Store} store The data store to bind
19249      */
19250     bind : function(ds){
19251         ds.on("beforeload", this.beforeLoad, this);
19252         ds.on("load", this.onLoad, this);
19253         ds.on("loadexception", this.onLoadError, this);
19254         ds.on("remove", this.updateInfo, this);
19255         ds.on("add", this.updateInfo, this);
19256         this.ds = ds;
19257     }
19258 });/*
19259  * - LGPL
19260  *
19261  * element
19262  * 
19263  */
19264
19265 /**
19266  * @class Roo.bootstrap.MessageBar
19267  * @extends Roo.bootstrap.Component
19268  * Bootstrap MessageBar class
19269  * @cfg {String} html contents of the MessageBar
19270  * @cfg {String} weight (info | success | warning | danger) default info
19271  * @cfg {String} beforeClass insert the bar before the given class
19272  * @cfg {Boolean} closable (true | false) default false
19273  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
19274  * 
19275  * @constructor
19276  * Create a new Element
19277  * @param {Object} config The config object
19278  */
19279
19280 Roo.bootstrap.MessageBar = function(config){
19281     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
19282 };
19283
19284 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
19285     
19286     html: '',
19287     weight: 'info',
19288     closable: false,
19289     fixed: false,
19290     beforeClass: 'bootstrap-sticky-wrap',
19291     
19292     getAutoCreate : function(){
19293         
19294         var cfg = {
19295             tag: 'div',
19296             cls: 'alert alert-dismissable alert-' + this.weight,
19297             cn: [
19298                 {
19299                     tag: 'span',
19300                     cls: 'message',
19301                     html: this.html || ''
19302                 }
19303             ]
19304         }
19305         
19306         if(this.fixed){
19307             cfg.cls += ' alert-messages-fixed';
19308         }
19309         
19310         if(this.closable){
19311             cfg.cn.push({
19312                 tag: 'button',
19313                 cls: 'close',
19314                 html: 'x'
19315             });
19316         }
19317         
19318         return cfg;
19319     },
19320     
19321     onRender : function(ct, position)
19322     {
19323         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19324         
19325         if(!this.el){
19326             var cfg = Roo.apply({},  this.getAutoCreate());
19327             cfg.id = Roo.id();
19328             
19329             if (this.cls) {
19330                 cfg.cls += ' ' + this.cls;
19331             }
19332             if (this.style) {
19333                 cfg.style = this.style;
19334             }
19335             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
19336             
19337             this.el.setVisibilityMode(Roo.Element.DISPLAY);
19338         }
19339         
19340         this.el.select('>button.close').on('click', this.hide, this);
19341         
19342     },
19343     
19344     show : function()
19345     {
19346         if (!this.rendered) {
19347             this.render();
19348         }
19349         
19350         this.el.show();
19351         
19352         this.fireEvent('show', this);
19353         
19354     },
19355     
19356     hide : function()
19357     {
19358         if (!this.rendered) {
19359             this.render();
19360         }
19361         
19362         this.el.hide();
19363         
19364         this.fireEvent('hide', this);
19365     },
19366     
19367     update : function()
19368     {
19369 //        var e = this.el.dom.firstChild;
19370 //        
19371 //        if(this.closable){
19372 //            e = e.nextSibling;
19373 //        }
19374 //        
19375 //        e.data = this.html || '';
19376
19377         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
19378     }
19379    
19380 });
19381
19382  
19383
19384      /*
19385  * - LGPL
19386  *
19387  * Graph
19388  * 
19389  */
19390
19391
19392 /**
19393  * @class Roo.bootstrap.Graph
19394  * @extends Roo.bootstrap.Component
19395  * Bootstrap Graph class
19396 > Prameters
19397  -sm {number} sm 4
19398  -md {number} md 5
19399  @cfg {String} graphtype  bar | vbar | pie
19400  @cfg {number} g_x coodinator | centre x (pie)
19401  @cfg {number} g_y coodinator | centre y (pie)
19402  @cfg {number} g_r radius (pie)
19403  @cfg {number} g_height height of the chart (respected by all elements in the set)
19404  @cfg {number} g_width width of the chart (respected by all elements in the set)
19405  @cfg {Object} title The title of the chart
19406     
19407  -{Array}  values
19408  -opts (object) options for the chart 
19409      o {
19410      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
19411      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
19412      o vgutter (number)
19413      o colors (array) colors be used repeatedly to plot the bars. If multicolumn bar is used each sequence of bars with use a different color.
19414      o stacked (boolean) whether or not to tread values as in a stacked bar chart
19415      o to
19416      o stretch (boolean)
19417      o }
19418  -opts (object) options for the pie
19419      o{
19420      o cut
19421      o startAngle (number)
19422      o endAngle (number)
19423      } 
19424  *
19425  * @constructor
19426  * Create a new Input
19427  * @param {Object} config The config object
19428  */
19429
19430 Roo.bootstrap.Graph = function(config){
19431     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
19432     
19433     this.addEvents({
19434         // img events
19435         /**
19436          * @event click
19437          * The img click event for the img.
19438          * @param {Roo.EventObject} e
19439          */
19440         "click" : true
19441     });
19442 };
19443
19444 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
19445     
19446     sm: 4,
19447     md: 5,
19448     graphtype: 'bar',
19449     g_height: 250,
19450     g_width: 400,
19451     g_x: 50,
19452     g_y: 50,
19453     g_r: 30,
19454     opts:{
19455         //g_colors: this.colors,
19456         g_type: 'soft',
19457         g_gutter: '20%'
19458
19459     },
19460     title : false,
19461
19462     getAutoCreate : function(){
19463         
19464         var cfg = {
19465             tag: 'div',
19466             html : null
19467         }
19468         
19469         
19470         return  cfg;
19471     },
19472
19473     onRender : function(ct,position){
19474         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
19475         this.raphael = Raphael(this.el.dom);
19476         
19477                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19478                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19479                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19480                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
19481                 /*
19482                 r.text(160, 10, "Single Series Chart").attr(txtattr);
19483                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
19484                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
19485                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
19486                 
19487                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
19488                 r.barchart(330, 10, 300, 220, data1);
19489                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
19490                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
19491                 */
19492                 
19493                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19494                 // r.barchart(30, 30, 560, 250,  xdata, {
19495                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
19496                 //     axis : "0 0 1 1",
19497                 //     axisxlabels :  xdata
19498                 //     //yvalues : cols,
19499                    
19500                 // });
19501 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19502 //        
19503 //        this.load(null,xdata,{
19504 //                axis : "0 0 1 1",
19505 //                axisxlabels :  xdata
19506 //                });
19507
19508     },
19509
19510     load : function(graphtype,xdata,opts){
19511         this.raphael.clear();
19512         if(!graphtype) {
19513             graphtype = this.graphtype;
19514         }
19515         if(!opts){
19516             opts = this.opts;
19517         }
19518         var r = this.raphael,
19519             fin = function () {
19520                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
19521             },
19522             fout = function () {
19523                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
19524             },
19525             pfin = function() {
19526                 this.sector.stop();
19527                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
19528
19529                 if (this.label) {
19530                     this.label[0].stop();
19531                     this.label[0].attr({ r: 7.5 });
19532                     this.label[1].attr({ "font-weight": 800 });
19533                 }
19534             },
19535             pfout = function() {
19536                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
19537
19538                 if (this.label) {
19539                     this.label[0].animate({ r: 5 }, 500, "bounce");
19540                     this.label[1].attr({ "font-weight": 400 });
19541                 }
19542             };
19543
19544         switch(graphtype){
19545             case 'bar':
19546                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19547                 break;
19548             case 'hbar':
19549                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19550                 break;
19551             case 'pie':
19552 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
19553 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
19554 //            
19555                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
19556                 
19557                 break;
19558
19559         }
19560         
19561         if(this.title){
19562             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
19563         }
19564         
19565     },
19566     
19567     setTitle: function(o)
19568     {
19569         this.title = o;
19570     },
19571     
19572     initEvents: function() {
19573         
19574         if(!this.href){
19575             this.el.on('click', this.onClick, this);
19576         }
19577     },
19578     
19579     onClick : function(e)
19580     {
19581         Roo.log('img onclick');
19582         this.fireEvent('click', this, e);
19583     }
19584    
19585 });
19586
19587  
19588 /*
19589  * - LGPL
19590  *
19591  * numberBox
19592  * 
19593  */
19594 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19595
19596 /**
19597  * @class Roo.bootstrap.dash.NumberBox
19598  * @extends Roo.bootstrap.Component
19599  * Bootstrap NumberBox class
19600  * @cfg {String} bgcolor Box background color, such as (aqua | green | yellow | red etc..) Default aqua
19601  * @cfg {String} headline Box headline
19602  * @cfg {String} content Box content
19603  * @cfg {String} icon Box icon
19604  * @cfg {String} footer Footer text
19605  * @cfg {String} fhref Footer href
19606  * 
19607  * @constructor
19608  * Create a new NumberBox
19609  * @param {Object} config The config object
19610  */
19611
19612
19613 Roo.bootstrap.dash.NumberBox = function(config){
19614     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
19615     
19616 };
19617
19618 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
19619     
19620     bgcolor : 'aqua',
19621     headline : '',
19622     content : '',
19623     icon : '',
19624     footer : '',
19625     fhref : '',
19626     ficon : '',
19627     
19628     getAutoCreate : function(){
19629         
19630         var cfg = {
19631             tag : 'div',
19632             cls : 'small-box bg-' + this.bgcolor,
19633             cn : [
19634                 {
19635                     tag : 'div',
19636                     cls : 'inner',
19637                     cn :[
19638                         {
19639                             tag : 'h3',
19640                             cls : 'roo-headline',
19641                             html : this.headline
19642                         },
19643                         {
19644                             tag : 'p',
19645                             cls : 'roo-content',
19646                             html : this.content
19647                         }
19648                     ]
19649                 }
19650             ]
19651         }
19652         
19653         if(this.icon){
19654             cfg.cn.push({
19655                 tag : 'div',
19656                 cls : 'icon',
19657                 cn :[
19658                     {
19659                         tag : 'i',
19660                         cls : 'ion ' + this.icon
19661                     }
19662                 ]
19663             });
19664         }
19665         
19666         if(this.footer){
19667             var footer = {
19668                 tag : 'a',
19669                 cls : 'small-box-footer',
19670                 href : this.fhref || '#',
19671                 html : this.footer
19672             };
19673             
19674             cfg.cn.push(footer);
19675             
19676         }
19677         
19678         return  cfg;
19679     },
19680
19681     onRender : function(ct,position){
19682         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
19683
19684
19685        
19686                 
19687     },
19688
19689     setHeadline: function (value)
19690     {
19691         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
19692     },
19693     
19694     setFooter: function (value, href)
19695     {
19696         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
19697         
19698         if(href){
19699             this.el.select('a.small-box-footer',true).first().attr('href', href);
19700         }
19701         
19702     },
19703
19704     setContent: function (value)
19705     {
19706         this.el.select('.roo-content',true).first().dom.innerHTML = value;
19707     },
19708
19709     initEvents: function() 
19710     {   
19711         
19712     }
19713     
19714 });
19715
19716  
19717 /*
19718  * - LGPL
19719  *
19720  * TabBox
19721  * 
19722  */
19723 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19724
19725 /**
19726  * @class Roo.bootstrap.dash.TabBox
19727  * @extends Roo.bootstrap.Component
19728  * Bootstrap TabBox class
19729  * @cfg {String} title Title of the TabBox
19730  * @cfg {String} icon Icon of the TabBox
19731  * @cfg {Boolean} showtabs (true|false) show the tabs default true
19732  * 
19733  * @constructor
19734  * Create a new TabBox
19735  * @param {Object} config The config object
19736  */
19737
19738
19739 Roo.bootstrap.dash.TabBox = function(config){
19740     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
19741     this.addEvents({
19742         // raw events
19743         /**
19744          * @event addpane
19745          * When a pane is added
19746          * @param {Roo.bootstrap.dash.TabPane} pane
19747          */
19748         "addpane" : true
19749          
19750     });
19751 };
19752
19753 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
19754
19755     title : '',
19756     icon : false,
19757     showtabs : true,
19758     
19759     getChildContainer : function()
19760     {
19761         return this.el.select('.tab-content', true).first();
19762     },
19763     
19764     getAutoCreate : function(){
19765         
19766         var header = {
19767             tag: 'li',
19768             cls: 'pull-left header',
19769             html: this.title,
19770             cn : []
19771         };
19772         
19773         if(this.icon){
19774             header.cn.push({
19775                 tag: 'i',
19776                 cls: 'fa ' + this.icon
19777             });
19778         }
19779         
19780         
19781         var cfg = {
19782             tag: 'div',
19783             cls: 'nav-tabs-custom',
19784             cn: [
19785                 {
19786                     tag: 'ul',
19787                     cls: 'nav nav-tabs pull-right',
19788                     cn: [
19789                         header
19790                     ]
19791                 },
19792                 {
19793                     tag: 'div',
19794                     cls: 'tab-content no-padding',
19795                     cn: []
19796                 }
19797             ]
19798         }
19799
19800         return  cfg;
19801     },
19802     initEvents : function()
19803     {
19804         //Roo.log('add add pane handler');
19805         this.on('addpane', this.onAddPane, this);
19806     },
19807      /**
19808      * Updates the box title
19809      * @param {String} html to set the title to.
19810      */
19811     setTitle : function(value)
19812     {
19813         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
19814     },
19815     onAddPane : function(pane)
19816     {
19817         //Roo.log('addpane');
19818         //Roo.log(pane);
19819         // tabs are rendere left to right..
19820         if(!this.showtabs){
19821             return;
19822         }
19823         
19824         var ctr = this.el.select('.nav-tabs', true).first();
19825          
19826          
19827         var existing = ctr.select('.nav-tab',true);
19828         var qty = existing.getCount();;
19829         
19830         
19831         var tab = ctr.createChild({
19832             tag : 'li',
19833             cls : 'nav-tab' + (qty ? '' : ' active'),
19834             cn : [
19835                 {
19836                     tag : 'a',
19837                     href:'#',
19838                     html : pane.title
19839                 }
19840             ]
19841         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
19842         pane.tab = tab;
19843         
19844         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
19845         if (!qty) {
19846             pane.el.addClass('active');
19847         }
19848         
19849                 
19850     },
19851     onTabClick : function(ev,un,ob,pane)
19852     {
19853         //Roo.log('tab - prev default');
19854         ev.preventDefault();
19855         
19856         
19857         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
19858         pane.tab.addClass('active');
19859         //Roo.log(pane.title);
19860         this.getChildContainer().select('.tab-pane',true).removeClass('active');
19861         // technically we should have a deactivate event.. but maybe add later.
19862         // and it should not de-activate the selected tab...
19863         
19864         pane.el.addClass('active');
19865         pane.fireEvent('activate');
19866         
19867         
19868     }
19869     
19870     
19871 });
19872
19873  
19874 /*
19875  * - LGPL
19876  *
19877  * Tab pane
19878  * 
19879  */
19880 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19881 /**
19882  * @class Roo.bootstrap.TabPane
19883  * @extends Roo.bootstrap.Component
19884  * Bootstrap TabPane class
19885  * @cfg {Boolean} active (false | true) Default false
19886  * @cfg {String} title title of panel
19887
19888  * 
19889  * @constructor
19890  * Create a new TabPane
19891  * @param {Object} config The config object
19892  */
19893
19894 Roo.bootstrap.dash.TabPane = function(config){
19895     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
19896     
19897     this.addEvents({
19898         // raw events
19899         /**
19900          * @event activate
19901          * When a pane is activated
19902          * @param {Roo.bootstrap.dash.TabPane} pane
19903          */
19904         "activate" : true
19905          
19906     });
19907 };
19908
19909 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
19910     
19911     active : false,
19912     title : '',
19913     
19914     // the tabBox that this is attached to.
19915     tab : false,
19916      
19917     getAutoCreate : function() 
19918     {
19919         var cfg = {
19920             tag: 'div',
19921             cls: 'tab-pane'
19922         }
19923         
19924         if(this.active){
19925             cfg.cls += ' active';
19926         }
19927         
19928         return cfg;
19929     },
19930     initEvents  : function()
19931     {
19932         //Roo.log('trigger add pane handler');
19933         this.parent().fireEvent('addpane', this)
19934     },
19935     
19936      /**
19937      * Updates the tab title 
19938      * @param {String} html to set the title to.
19939      */
19940     setTitle: function(str)
19941     {
19942         if (!this.tab) {
19943             return;
19944         }
19945         this.title = str;
19946         this.tab.select('a', true).first().dom.innerHTML = str;
19947         
19948     }
19949     
19950     
19951     
19952 });
19953
19954  
19955
19956
19957  /*
19958  * - LGPL
19959  *
19960  * menu
19961  * 
19962  */
19963 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
19964
19965 /**
19966  * @class Roo.bootstrap.menu.Menu
19967  * @extends Roo.bootstrap.Component
19968  * Bootstrap Menu class - container for Menu
19969  * @cfg {String} html Text of the menu
19970  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
19971  * @cfg {String} icon Font awesome icon
19972  * @cfg {String} pos Menu align to (top | bottom) default bottom
19973  * 
19974  * 
19975  * @constructor
19976  * Create a new Menu
19977  * @param {Object} config The config object
19978  */
19979
19980
19981 Roo.bootstrap.menu.Menu = function(config){
19982     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
19983     
19984     this.addEvents({
19985         /**
19986          * @event beforeshow
19987          * Fires before this menu is displayed
19988          * @param {Roo.bootstrap.menu.Menu} this
19989          */
19990         beforeshow : true,
19991         /**
19992          * @event beforehide
19993          * Fires before this menu is hidden
19994          * @param {Roo.bootstrap.menu.Menu} this
19995          */
19996         beforehide : true,
19997         /**
19998          * @event show
19999          * Fires after this menu is displayed
20000          * @param {Roo.bootstrap.menu.Menu} this
20001          */
20002         show : true,
20003         /**
20004          * @event hide
20005          * Fires after this menu is hidden
20006          * @param {Roo.bootstrap.menu.Menu} this
20007          */
20008         hide : true,
20009         /**
20010          * @event click
20011          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
20012          * @param {Roo.bootstrap.menu.Menu} this
20013          * @param {Roo.EventObject} e
20014          */
20015         click : true
20016     });
20017     
20018 };
20019
20020 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
20021     
20022     submenu : false,
20023     html : '',
20024     weight : 'default',
20025     icon : false,
20026     pos : 'bottom',
20027     
20028     
20029     getChildContainer : function() {
20030         if(this.isSubMenu){
20031             return this.el;
20032         }
20033         
20034         return this.el.select('ul.dropdown-menu', true).first();  
20035     },
20036     
20037     getAutoCreate : function()
20038     {
20039         var text = [
20040             {
20041                 tag : 'span',
20042                 cls : 'roo-menu-text',
20043                 html : this.html
20044             }
20045         ];
20046         
20047         if(this.icon){
20048             text.unshift({
20049                 tag : 'i',
20050                 cls : 'fa ' + this.icon
20051             })
20052         }
20053         
20054         
20055         var cfg = {
20056             tag : 'div',
20057             cls : 'btn-group',
20058             cn : [
20059                 {
20060                     tag : 'button',
20061                     cls : 'dropdown-button btn btn-' + this.weight,
20062                     cn : text
20063                 },
20064                 {
20065                     tag : 'button',
20066                     cls : 'dropdown-toggle btn btn-' + this.weight,
20067                     cn : [
20068                         {
20069                             tag : 'span',
20070                             cls : 'caret'
20071                         }
20072                     ]
20073                 },
20074                 {
20075                     tag : 'ul',
20076                     cls : 'dropdown-menu'
20077                 }
20078             ]
20079             
20080         };
20081         
20082         if(this.pos == 'top'){
20083             cfg.cls += ' dropup';
20084         }
20085         
20086         if(this.isSubMenu){
20087             cfg = {
20088                 tag : 'ul',
20089                 cls : 'dropdown-menu'
20090             }
20091         }
20092         
20093         return cfg;
20094     },
20095     
20096     onRender : function(ct, position)
20097     {
20098         this.isSubMenu = ct.hasClass('dropdown-submenu');
20099         
20100         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
20101     },
20102     
20103     initEvents : function() 
20104     {
20105         if(this.isSubMenu){
20106             return;
20107         }
20108         
20109         this.hidden = true;
20110         
20111         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
20112         this.triggerEl.on('click', this.onTriggerPress, this);
20113         
20114         this.buttonEl = this.el.select('button.dropdown-button', true).first();
20115         this.buttonEl.on('click', this.onClick, this);
20116         
20117     },
20118     
20119     list : function()
20120     {
20121         if(this.isSubMenu){
20122             return this.el;
20123         }
20124         
20125         return this.el.select('ul.dropdown-menu', true).first();
20126     },
20127     
20128     onClick : function(e)
20129     {
20130         this.fireEvent("click", this, e);
20131     },
20132     
20133     onTriggerPress  : function(e)
20134     {   
20135         if (this.isVisible()) {
20136             this.hide();
20137         } else {
20138             this.show();
20139         }
20140     },
20141     
20142     isVisible : function(){
20143         return !this.hidden;
20144     },
20145     
20146     show : function()
20147     {
20148         this.fireEvent("beforeshow", this);
20149         
20150         this.hidden = false;
20151         this.el.addClass('open');
20152         
20153         Roo.get(document).on("mouseup", this.onMouseUp, this);
20154         
20155         this.fireEvent("show", this);
20156         
20157         
20158     },
20159     
20160     hide : function()
20161     {
20162         this.fireEvent("beforehide", this);
20163         
20164         this.hidden = true;
20165         this.el.removeClass('open');
20166         
20167         Roo.get(document).un("mouseup", this.onMouseUp);
20168         
20169         this.fireEvent("hide", this);
20170     },
20171     
20172     onMouseUp : function()
20173     {
20174         this.hide();
20175     }
20176     
20177 });
20178
20179  
20180  /*
20181  * - LGPL
20182  *
20183  * menu item
20184  * 
20185  */
20186 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20187
20188 /**
20189  * @class Roo.bootstrap.menu.Item
20190  * @extends Roo.bootstrap.Component
20191  * Bootstrap MenuItem class
20192  * @cfg {Boolean} submenu (true | false) default false
20193  * @cfg {String} html text of the item
20194  * @cfg {String} href the link
20195  * @cfg {Boolean} disable (true | false) default false
20196  * @cfg {Boolean} preventDefault (true | false) default true
20197  * @cfg {String} icon Font awesome icon
20198  * @cfg {String} pos Submenu align to (left | right) default right 
20199  * 
20200  * 
20201  * @constructor
20202  * Create a new Item
20203  * @param {Object} config The config object
20204  */
20205
20206
20207 Roo.bootstrap.menu.Item = function(config){
20208     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
20209     this.addEvents({
20210         /**
20211          * @event mouseover
20212          * Fires when the mouse is hovering over this menu
20213          * @param {Roo.bootstrap.menu.Item} this
20214          * @param {Roo.EventObject} e
20215          */
20216         mouseover : true,
20217         /**
20218          * @event mouseout
20219          * Fires when the mouse exits this menu
20220          * @param {Roo.bootstrap.menu.Item} this
20221          * @param {Roo.EventObject} e
20222          */
20223         mouseout : true,
20224         // raw events
20225         /**
20226          * @event click
20227          * The raw click event for the entire grid.
20228          * @param {Roo.EventObject} e
20229          */
20230         click : true
20231     });
20232 };
20233
20234 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
20235     
20236     submenu : false,
20237     href : '',
20238     html : '',
20239     preventDefault: true,
20240     disable : false,
20241     icon : false,
20242     pos : 'right',
20243     
20244     getAutoCreate : function()
20245     {
20246         var text = [
20247             {
20248                 tag : 'span',
20249                 cls : 'roo-menu-item-text',
20250                 html : this.html
20251             }
20252         ];
20253         
20254         if(this.icon){
20255             text.unshift({
20256                 tag : 'i',
20257                 cls : 'fa ' + this.icon
20258             })
20259         }
20260         
20261         var cfg = {
20262             tag : 'li',
20263             cn : [
20264                 {
20265                     tag : 'a',
20266                     href : this.href || '#',
20267                     cn : text
20268                 }
20269             ]
20270         };
20271         
20272         if(this.disable){
20273             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
20274         }
20275         
20276         if(this.submenu){
20277             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
20278             
20279             if(this.pos == 'left'){
20280                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
20281             }
20282         }
20283         
20284         return cfg;
20285     },
20286     
20287     initEvents : function() 
20288     {
20289         this.el.on('mouseover', this.onMouseOver, this);
20290         this.el.on('mouseout', this.onMouseOut, this);
20291         
20292         this.el.select('a', true).first().on('click', this.onClick, this);
20293         
20294     },
20295     
20296     onClick : function(e)
20297     {
20298         if(this.preventDefault){
20299             e.preventDefault();
20300         }
20301         
20302         this.fireEvent("click", this, e);
20303     },
20304     
20305     onMouseOver : function(e)
20306     {
20307         if(this.submenu && this.pos == 'left'){
20308             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
20309         }
20310         
20311         this.fireEvent("mouseover", this, e);
20312     },
20313     
20314     onMouseOut : function(e)
20315     {
20316         this.fireEvent("mouseout", this, e);
20317     }
20318 });
20319
20320  
20321
20322  /*
20323  * - LGPL
20324  *
20325  * menu separator
20326  * 
20327  */
20328 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20329
20330 /**
20331  * @class Roo.bootstrap.menu.Separator
20332  * @extends Roo.bootstrap.Component
20333  * Bootstrap Separator class
20334  * 
20335  * @constructor
20336  * Create a new Separator
20337  * @param {Object} config The config object
20338  */
20339
20340
20341 Roo.bootstrap.menu.Separator = function(config){
20342     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
20343 };
20344
20345 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
20346     
20347     getAutoCreate : function(){
20348         var cfg = {
20349             tag : 'li',
20350             cls: 'divider'
20351         };
20352         
20353         return cfg;
20354     }
20355    
20356 });
20357
20358  
20359
20360