Roo/bootstrap/Img.js
[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  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         cfg.id = this.id || Roo.id();
105         
106         // fill in the extra attributes 
107         if (this.xattr && typeof(this.xattr) =='object') {
108             for (var i in this.xattr) {
109                 cfg[i] = this.xattr[i];
110             }
111         }
112         
113         if(this.dataId){
114             cfg.dataId = this.dataId;
115         }
116         
117         if (this.cls) {
118             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
119         }
120         
121         if (this.style) { // fixme needs to support more complex style data.
122             cfg.style = this.style;
123         }
124         
125         if(this.name){
126             cfg.name = this.name;
127         }
128         
129         this.el = ct.createChild(cfg, position);
130         
131         if (this.tooltip) {
132             this.tooltipEl().attr('tooltip', this.tooltip);
133         }
134         
135         if(this.tabIndex !== undefined){
136             this.el.dom.setAttribute('tabIndex', this.tabIndex);
137         }
138         this.initEvents();
139         
140         
141     },
142     /**
143      * Fetch the element to add children to
144      * @return {Roo.Element} defaults to this.el
145      */
146     getChildContainer : function()
147     {
148         return this.el;
149     },
150     /**
151      * Fetch the element to display the tooltip on.
152      * @return {Roo.Element} defaults to this.el
153      */
154     tooltipEl : function()
155     {
156         return this.el;
157     },
158         
159     addxtype  : function(tree,cntr)
160     {
161         var cn = this;
162         
163         cn = Roo.factory(tree);
164            
165         cn.parentType = this.xtype; //??
166         cn.parentId = this.id;
167         
168         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
169         if (typeof(cn.container_method) == 'string') {
170             cntr = cn.container_method;
171         }
172         
173         
174         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
175         
176         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
177         
178         var build_from_html =  Roo.XComponent.build_from_html;
179           
180         var is_body  = (tree.xtype == 'Body') ;
181           
182         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
183           
184         var self_cntr_el = Roo.get(this[cntr](false));
185         
186         // do not try and build conditional elements 
187         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
188             return false;
189         }
190         
191         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
192             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
193                 return this.addxtypeChild(tree,cntr);
194             }
195             
196             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
197                 
198             if(echild){
199                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
200             }
201             
202             Roo.log('skipping render');
203             return cn;
204             
205         }
206         
207         var ret = false;
208         if (!build_from_html) {
209             return false;
210         }
211         
212         // this i think handles overlaying multiple children of the same type
213         // with the sam eelement.. - which might be buggy..
214         while (true) {
215             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
216             
217             if (!echild) {
218                 break;
219             }
220             
221             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
222                 break;
223             }
224             
225             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
226         }
227         return ret;
228     },
229     
230     addxtypeChild : function (tree, cntr)
231     {
232         Roo.debug && Roo.log('addxtypeChild:' + cntr);
233         var cn = this;
234         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
235         
236         
237         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
238                     (typeof(tree['flexy:foreach']) != 'undefined');
239           
240         
241         
242          skip_children = false;
243         // render the element if it's not BODY.
244         if (tree.xtype != 'Body') {
245            
246             cn = Roo.factory(tree);
247            
248             cn.parentType = this.xtype; //??
249             cn.parentId = this.id;
250             
251             var build_from_html =  Roo.XComponent.build_from_html;
252             
253             
254             // does the container contain child eleemnts with 'xtype' attributes.
255             // that match this xtype..
256             // note - when we render we create these as well..
257             // so we should check to see if body has xtype set.
258             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
259                
260                 var self_cntr_el = Roo.get(this[cntr](false));
261                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
262                 if (echild) { 
263                     //Roo.log(Roo.XComponent.build_from_html);
264                     //Roo.log("got echild:");
265                     //Roo.log(echild);
266                 }
267                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
268                 // and are not displayed -this causes this to use up the wrong element when matching.
269                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
270                 
271                 
272                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
273                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
274                   
275                   
276                   
277                     cn.el = echild;
278                   //  Roo.log("GOT");
279                     //echild.dom.removeAttribute('xtype');
280                 } else {
281                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
282                     Roo.debug && Roo.log(self_cntr_el);
283                     Roo.debug && Roo.log(echild);
284                     Roo.debug && Roo.log(cn);
285                 }
286             }
287            
288             
289            
290             // if object has flexy:if - then it may or may not be rendered.
291             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
292                 // skip a flexy if element.
293                 Roo.debug && Roo.log('skipping render');
294                 Roo.debug && Roo.log(tree);
295                 if (!cn.el) {
296                     Roo.debug && Roo.log('skipping all children');
297                     skip_children = true;
298                 }
299                 
300              } else {
301                  
302                 // actually if flexy:foreach is found, we really want to create 
303                 // multiple copies here...
304                 //Roo.log('render');
305                 //Roo.log(this[cntr]());
306                 cn.render(this[cntr](true));
307              }
308             // then add the element..
309         }
310         
311         
312         // handle the kids..
313         
314         var nitems = [];
315         /*
316         if (typeof (tree.menu) != 'undefined') {
317             tree.menu.parentType = cn.xtype;
318             tree.menu.triggerEl = cn.el;
319             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
320             
321         }
322         */
323         if (!tree.items || !tree.items.length) {
324             cn.items = nitems;
325             return cn;
326         }
327         var items = tree.items;
328         delete tree.items;
329         
330         //Roo.log(items.length);
331             // add the items..
332         if (!skip_children) {    
333             for(var i =0;i < items.length;i++) {
334                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
335             }
336         }
337         
338         cn.items = nitems;
339         
340         this.fireEvent('childrenrendered', this);
341         
342         return cn;
343     },
344     /**
345      * Show a component - removes 'hidden' class
346      */
347     show : function()
348     {
349         if (this.el) {
350             this.el.removeClass('hidden');
351         }
352     },
353     /**
354      * Hide a component - adds 'hidden' class
355      */
356     hide: function()
357     {
358         if (this.el && !this.el.hasClass('hidden')) {
359             this.el.addClass('hidden');
360         }
361         
362     }
363 });
364
365  /*
366  * - LGPL
367  *
368  * Body
369  * 
370  */
371
372 /**
373  * @class Roo.bootstrap.Body
374  * @extends Roo.bootstrap.Component
375  * Bootstrap Body class
376  * 
377  * @constructor
378  * Create a new body
379  * @param {Object} config The config object
380  */
381
382 Roo.bootstrap.Body = function(config){
383     Roo.bootstrap.Body.superclass.constructor.call(this, config);
384     this.el = Roo.get(document.body);
385     if (this.cls && this.cls.length) {
386         Roo.get(document.body).addClass(this.cls);
387     }
388 };
389
390 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
391       
392         autoCreate : {
393         cls: 'container'
394     },
395     onRender : function(ct, position)
396     {
397        /* Roo.log("Roo.bootstrap.Body - onRender");
398         if (this.cls && this.cls.length) {
399             Roo.get(document.body).addClass(this.cls);
400         }
401         // style??? xttr???
402         */
403     }
404     
405     
406  
407    
408 });
409
410  /*
411  * - LGPL
412  *
413  * button group
414  * 
415  */
416
417
418 /**
419  * @class Roo.bootstrap.ButtonGroup
420  * @extends Roo.bootstrap.Component
421  * Bootstrap ButtonGroup class
422  * @cfg {String} size lg | sm | xs (default empty normal)
423  * @cfg {String} align vertical | justified  (default none)
424  * @cfg {String} direction up | down (default down)
425  * @cfg {Boolean} toolbar false | true
426  * @cfg {Boolean} btn true | false
427  * 
428  * 
429  * @constructor
430  * Create a new Input
431  * @param {Object} config The config object
432  */
433
434 Roo.bootstrap.ButtonGroup = function(config){
435     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
436 };
437
438 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
439     
440     size: '',
441     align: '',
442     direction: '',
443     toolbar: false,
444     btn: true,
445
446     getAutoCreate : function(){
447         var cfg = {
448             cls: 'btn-group',
449             html : null
450         }
451         
452         cfg.html = this.html || cfg.html;
453         
454         if (this.toolbar) {
455             cfg = {
456                 cls: 'btn-toolbar',
457                 html: null
458             }
459             
460             return cfg;
461         }
462         
463         if (['vertical','justified'].indexOf(this.align)!==-1) {
464             cfg.cls = 'btn-group-' + this.align;
465             
466             if (this.align == 'justified') {
467                 console.log(this.items);
468             }
469         }
470         
471         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
472             cfg.cls += ' btn-group-' + this.size;
473         }
474         
475         if (this.direction == 'up') {
476             cfg.cls += ' dropup' ;
477         }
478         
479         return cfg;
480     }
481    
482 });
483
484  /*
485  * - LGPL
486  *
487  * button
488  * 
489  */
490
491 /**
492  * @class Roo.bootstrap.Button
493  * @extends Roo.bootstrap.Component
494  * Bootstrap Button class
495  * @cfg {String} html The button content
496  * @cfg {String} weight (  primary | success | info | warning | danger | link ) default 
497  * @cfg {String} size ( lg | sm | xs)
498  * @cfg {String} tag ( a | input | submit)
499  * @cfg {String} href empty or href
500  * @cfg {Boolean} disabled default false;
501  * @cfg {Boolean} isClose default false;
502  * @cfg {String} glyphicon (| 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)
503  * @cfg {String} badge text for badge
504  * @cfg {String} theme default 
505  * @cfg {Boolean} inverse 
506  * @cfg {Boolean} toggle 
507  * @cfg {String} ontext text for on toggle state
508  * @cfg {String} offtext text for off toggle state
509  * @cfg {Boolean} defaulton 
510  * @cfg {Boolean} preventDefault  default true
511  * @cfg {Boolean} removeClass remove the standard class..
512  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
513  * 
514  * @constructor
515  * Create a new button
516  * @param {Object} config The config object
517  */
518
519
520 Roo.bootstrap.Button = function(config){
521     Roo.bootstrap.Button.superclass.constructor.call(this, config);
522     this.addEvents({
523         // raw events
524         /**
525          * @event click
526          * When a butotn is pressed
527          * @param {Roo.bootstrap.Button} this
528          * @param {Roo.EventObject} e
529          */
530         "click" : true,
531          /**
532          * @event toggle
533          * After the button has been toggles
534          * @param {Roo.EventObject} e
535          * @param {boolean} pressed (also available as button.pressed)
536          */
537         "toggle" : true
538     });
539 };
540
541 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
542     html: false,
543     active: false,
544     weight: '',
545     size: '',
546     tag: 'button',
547     href: '',
548     disabled: false,
549     isClose: false,
550     glyphicon: '',
551     badge: '',
552     theme: 'default',
553     inverse: false,
554     
555     toggle: false,
556     ontext: 'ON',
557     offtext: 'OFF',
558     defaulton: true,
559     preventDefault: true,
560     removeClass: false,
561     name: false,
562     target: false,
563     
564     
565     pressed : null,
566      
567     
568     getAutoCreate : function(){
569         
570         var cfg = {
571             tag : 'button',
572             cls : 'roo-button',
573             html: ''
574         };
575         
576         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
577             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
578             this.tag = 'button';
579         } else {
580             cfg.tag = this.tag;
581         }
582         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
583         
584         if (this.toggle == true) {
585             cfg={
586                 tag: 'div',
587                 cls: 'slider-frame roo-button',
588                 cn: [
589                     {
590                         tag: 'span',
591                         'data-on-text':'ON',
592                         'data-off-text':'OFF',
593                         cls: 'slider-button',
594                         html: this.offtext
595                     }
596                 ]
597             };
598             
599             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
600                 cfg.cls += ' '+this.weight;
601             }
602             
603             return cfg;
604         }
605         
606         if (this.isClose) {
607             cfg.cls += ' close';
608             
609             cfg["aria-hidden"] = true;
610             
611             cfg.html = "&times;";
612             
613             return cfg;
614         }
615         
616          
617         if (this.theme==='default') {
618             cfg.cls = 'btn roo-button';
619             
620             //if (this.parentType != 'Navbar') {
621             this.weight = this.weight.length ?  this.weight : 'default';
622             //}
623             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
624                 
625                 cfg.cls += ' btn-' + this.weight;
626             }
627         } else if (this.theme==='glow') {
628             
629             cfg.tag = 'a';
630             cfg.cls = 'btn-glow roo-button';
631             
632             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
633                 
634                 cfg.cls += ' ' + this.weight;
635             }
636         }
637    
638         
639         if (this.inverse) {
640             this.cls += ' inverse';
641         }
642         
643         
644         if (this.active) {
645             cfg.cls += ' active';
646         }
647         
648         if (this.disabled) {
649             cfg.disabled = 'disabled';
650         }
651         
652         if (this.items) {
653             Roo.log('changing to ul' );
654             cfg.tag = 'ul';
655             this.glyphicon = 'caret';
656         }
657         
658         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
659          
660         //gsRoo.log(this.parentType);
661         if (this.parentType === 'Navbar' && !this.parent().bar) {
662             Roo.log('changing to li?');
663             
664             cfg.tag = 'li';
665             
666             cfg.cls = '';
667             cfg.cn =  [{
668                 tag : 'a',
669                 cls : 'roo-button',
670                 html : this.html,
671                 href : this.href || '#'
672             }];
673             if (this.menu) {
674                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
675                 cfg.cls += ' dropdown';
676             }   
677             
678             delete cfg.html;
679             
680         }
681         
682        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
683         
684         if (this.glyphicon) {
685             cfg.html = ' ' + cfg.html;
686             
687             cfg.cn = [
688                 {
689                     tag: 'span',
690                     cls: 'glyphicon glyphicon-' + this.glyphicon
691                 }
692             ];
693         }
694         
695         if (this.badge) {
696             cfg.html += ' ';
697             
698             cfg.tag = 'a';
699             
700 //            cfg.cls='btn roo-button';
701             
702             cfg.href=this.href;
703             
704             var value = cfg.html;
705             
706             if(this.glyphicon){
707                 value = {
708                             tag: 'span',
709                             cls: 'glyphicon glyphicon-' + this.glyphicon,
710                             html: this.html
711                         };
712                 
713             }
714             
715             cfg.cn = [
716                 value,
717                 {
718                     tag: 'span',
719                     cls: 'badge',
720                     html: this.badge
721                 }
722             ];
723             
724             cfg.html='';
725         }
726         
727         if (this.menu) {
728             cfg.cls += ' dropdown';
729             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
730         }
731         
732         if (cfg.tag !== 'a' && this.href !== '') {
733             throw "Tag must be a to set href.";
734         } else if (this.href.length > 0) {
735             cfg.href = this.href;
736         }
737         
738         if(this.removeClass){
739             cfg.cls = '';
740         }
741         
742         if(this.target){
743             cfg.target = this.target;
744         }
745         
746         return cfg;
747     },
748     initEvents: function() {
749        // Roo.log('init events?');
750 //        Roo.log(this.el.dom);
751         // add the menu...
752         
753         if (typeof (this.menu) != 'undefined') {
754             this.menu.parentType = this.xtype;
755             this.menu.triggerEl = this.el;
756             this.addxtype(Roo.apply({}, this.menu));
757         }
758
759
760        if (this.el.hasClass('roo-button')) {
761             this.el.on('click', this.onClick, this);
762        } else {
763             this.el.select('.roo-button').on('click', this.onClick, this);
764        }
765        
766        if(this.removeClass){
767            this.el.on('click', this.onClick, this);
768        }
769        
770        this.el.enableDisplayMode();
771         
772     },
773     onClick : function(e)
774     {
775         if (this.disabled) {
776             return;
777         }
778         
779         
780         Roo.log('button on click ');
781         if(this.preventDefault){
782             e.preventDefault();
783         }
784         if (this.pressed === true || this.pressed === false) {
785             this.pressed = !this.pressed;
786             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
787             this.fireEvent('toggle', this, e, this.pressed);
788         }
789         
790         
791         this.fireEvent('click', this, e);
792     },
793     
794     /**
795      * Enables this button
796      */
797     enable : function()
798     {
799         this.disabled = false;
800         this.el.removeClass('disabled');
801     },
802     
803     /**
804      * Disable this button
805      */
806     disable : function()
807     {
808         this.disabled = true;
809         this.el.addClass('disabled');
810     },
811      /**
812      * sets the active state on/off, 
813      * @param {Boolean} state (optional) Force a particular state
814      */
815     setActive : function(v) {
816         
817         this.el[v ? 'addClass' : 'removeClass']('active');
818     },
819      /**
820      * toggles the current active state 
821      */
822     toggleActive : function()
823     {
824        var active = this.el.hasClass('active');
825        this.setActive(!active);
826        
827         
828     },
829     setText : function(str)
830     {
831         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
832     },
833     getText : function()
834     {
835         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
836     },
837     hide: function() {
838        
839      
840         this.el.hide();   
841     },
842     show: function() {
843        
844         this.el.show();   
845     }
846     
847     
848 });
849
850  /*
851  * - LGPL
852  *
853  * column
854  * 
855  */
856
857 /**
858  * @class Roo.bootstrap.Column
859  * @extends Roo.bootstrap.Component
860  * Bootstrap Column class
861  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
862  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
863  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
864  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
865  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
866  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
867  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
868  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
869  *
870  * 
871  * @cfg {Boolean} hidden (true|false) hide the element
872  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
873  * @cfg {String} fa (ban|check|...) font awesome icon
874  * @cfg {Number} fasize (1|2|....) font awsome size
875
876  * @cfg {String} icon (info-sign|check|...) glyphicon name
877
878  * @cfg {String} html content of column.
879  * 
880  * @constructor
881  * Create a new Column
882  * @param {Object} config The config object
883  */
884
885 Roo.bootstrap.Column = function(config){
886     Roo.bootstrap.Column.superclass.constructor.call(this, config);
887 };
888
889 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
890     
891     xs: false,
892     sm: false,
893     md: false,
894     lg: false,
895     xsoff: false,
896     smoff: false,
897     mdoff: false,
898     lgoff: false,
899     html: '',
900     offset: 0,
901     alert: false,
902     fa: false,
903     icon : false,
904     hidden : false,
905     fasize : 1,
906     
907     getAutoCreate : function(){
908         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
909         
910         cfg = {
911             tag: 'div',
912             cls: 'column'
913         };
914         
915         var settings=this;
916         ['xs','sm','md','lg'].map(function(size){
917             //Roo.log( size + ':' + settings[size]);
918             
919             if (settings[size+'off'] !== false) {
920                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
921             }
922             
923             if (settings[size] === false) {
924                 return;
925             }
926             Roo.log(settings[size]);
927             if (!settings[size]) { // 0 = hidden
928                 cfg.cls += ' hidden-' + size;
929                 return;
930             }
931             cfg.cls += ' col-' + size + '-' + settings[size];
932             
933         });
934         
935         if (this.hidden) {
936             cfg.cls += ' hidden';
937         }
938         
939         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
940             cfg.cls +=' alert alert-' + this.alert;
941         }
942         
943         
944         if (this.html.length) {
945             cfg.html = this.html;
946         }
947         if (this.fa) {
948             var fasize = '';
949             if (this.fasize > 1) {
950                 fasize = ' fa-' + this.fasize + 'x';
951             }
952             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
953             
954             
955         }
956         if (this.icon) {
957             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
958         }
959         
960         return cfg;
961     }
962    
963 });
964
965  
966
967  /*
968  * - LGPL
969  *
970  * page container.
971  * 
972  */
973
974
975 /**
976  * @class Roo.bootstrap.Container
977  * @extends Roo.bootstrap.Component
978  * Bootstrap Container class
979  * @cfg {Boolean} jumbotron is it a jumbotron element
980  * @cfg {String} html content of element
981  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
982  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
983  * @cfg {String} header content of header (for panel)
984  * @cfg {String} footer content of footer (for panel)
985  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
986  * @cfg {String} tag (header|aside|section) type of HTML tag.
987  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
988  * @cfg {String} fa (ban|check|...) font awesome icon
989  * @cfg {String} icon (info-sign|check|...) glyphicon name
990  * @cfg {Boolean} hidden (true|false) hide the element
991  * @cfg {Boolean} expandable (true|false) default false
992  * @cfg {String} rheader contet on the right of header
993
994  *     
995  * @constructor
996  * Create a new Container
997  * @param {Object} config The config object
998  */
999
1000 Roo.bootstrap.Container = function(config){
1001     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1002     
1003     this.addEvents({
1004         // raw events
1005          /**
1006          * @event expand
1007          * After the panel has been expand
1008          * 
1009          * @param {Roo.bootstrap.Container} this
1010          */
1011         "expand" : true,
1012         /**
1013          * @event collapse
1014          * After the panel has been collapsed
1015          * 
1016          * @param {Roo.bootstrap.Container} this
1017          */
1018         "collapse" : true
1019     });
1020 };
1021
1022 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1023     
1024     jumbotron : false,
1025     well: '',
1026     panel : '',
1027     header: '',
1028     footer : '',
1029     sticky: '',
1030     tag : false,
1031     alert : false,
1032     fa: false,
1033     icon : false,
1034     expandable : false,
1035     rheader : '',
1036     expanded : true,
1037   
1038      
1039     getChildContainer : function() {
1040         
1041         if(!this.el){
1042             return false;
1043         }
1044         
1045         if (this.panel.length) {
1046             return this.el.select('.panel-body',true).first();
1047         }
1048         
1049         return this.el;
1050     },
1051     
1052     
1053     getAutoCreate : function(){
1054         
1055         var cfg = {
1056             tag : this.tag || 'div',
1057             html : '',
1058             cls : ''
1059         };
1060         if (this.jumbotron) {
1061             cfg.cls = 'jumbotron';
1062         }
1063         
1064         
1065         
1066         // - this is applied by the parent..
1067         //if (this.cls) {
1068         //    cfg.cls = this.cls + '';
1069         //}
1070         
1071         if (this.sticky.length) {
1072             
1073             var bd = Roo.get(document.body);
1074             if (!bd.hasClass('bootstrap-sticky')) {
1075                 bd.addClass('bootstrap-sticky');
1076                 Roo.select('html',true).setStyle('height', '100%');
1077             }
1078              
1079             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1080         }
1081         
1082         
1083         if (this.well.length) {
1084             switch (this.well) {
1085                 case 'lg':
1086                 case 'sm':
1087                     cfg.cls +=' well well-' +this.well;
1088                     break;
1089                 default:
1090                     cfg.cls +=' well';
1091                     break;
1092             }
1093         }
1094         
1095         if (this.hidden) {
1096             cfg.cls += ' hidden';
1097         }
1098         
1099         
1100         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1101             cfg.cls +=' alert alert-' + this.alert;
1102         }
1103         
1104         var body = cfg;
1105         
1106         if (this.panel.length) {
1107             cfg.cls += ' panel panel-' + this.panel;
1108             cfg.cn = [];
1109             if (this.header.length) {
1110                 
1111                 var h = [];
1112                 
1113                 if(this.expandable){
1114                     
1115                     cfg.cls = cfg.cls + ' expandable';
1116                     
1117                     h.push({
1118                         tag: 'i',
1119                         cls: 'fa fa-minus'
1120                     });
1121                 }
1122                 
1123                 h.push(
1124                     {
1125                         tag: 'span',
1126                         cls : 'panel-title',
1127                         html : this.header
1128                     },
1129                     {
1130                         tag: 'span',
1131                         cls: 'panel-header-right',
1132                         html: this.rheader
1133                     }
1134                 );
1135                 
1136                 cfg.cn.push({
1137                     cls : 'panel-heading',
1138                     cn : h
1139                 });
1140                 
1141             }
1142             
1143             body = false;
1144             cfg.cn.push({
1145                 cls : 'panel-body',
1146                 html : this.html
1147             });
1148             
1149             
1150             if (this.footer.length) {
1151                 cfg.cn.push({
1152                     cls : 'panel-footer',
1153                     html : this.footer
1154                     
1155                 });
1156             }
1157             
1158         }
1159         
1160         if (body) {
1161             body.html = this.html || cfg.html;
1162             // prefix with the icons..
1163             if (this.fa) {
1164                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1165             }
1166             if (this.icon) {
1167                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1168             }
1169             
1170             
1171         }
1172         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1173             cfg.cls =  'container';
1174         }
1175         
1176         return cfg;
1177     },
1178     
1179     initEvents: function() 
1180     {
1181         if(!this.expandable){
1182             return;
1183         }
1184         
1185         var headerEl = this.headerEl();
1186         
1187         if(!headerEl){
1188             return;
1189         }
1190         
1191         headerEl.on('click', this.onToggleClick, this);
1192         
1193     },
1194     
1195     onToggleClick : function()
1196     {
1197         var headerEl = this.headerEl();
1198         
1199         if(!headerEl){
1200             return;
1201         }
1202         
1203         if(this.expanded){
1204             this.collapse();
1205             return;
1206         }
1207         
1208         this.expand();
1209     },
1210     
1211     expand : function()
1212     {
1213         if(this.fireEvent('expand', this)) {
1214             
1215             this.expanded = true;
1216             
1217             this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1218         
1219             var toggleEl = this.toggleEl();
1220
1221             if(!toggleEl){
1222                 return;
1223             }
1224
1225             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1226         }
1227         
1228     },
1229     
1230     collapse : function()
1231     {
1232         if(this.fireEvent('collapse', this)) {
1233             
1234             this.expanded = false;
1235             
1236             this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1237         
1238             var toggleEl = this.toggleEl();
1239
1240             if(!toggleEl){
1241                 return;
1242             }
1243
1244             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1245         }
1246     },
1247     
1248     toggleEl : function()
1249     {
1250         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1251             return;
1252         }
1253         
1254         return this.el.select('.panel-heading .fa',true).first();
1255     },
1256     
1257     headerEl : function()
1258     {
1259         if(!this.el || !this.panel.length || !this.header.length){
1260             return;
1261         }
1262         
1263         return this.el.select('.panel-heading',true).first()
1264     },
1265     
1266     titleEl : function()
1267     {
1268         if(!this.el || !this.panel.length || !this.header.length){
1269             return;
1270         }
1271         
1272         return this.el.select('.panel-title',true).first();
1273     },
1274     
1275     setTitle : function(v)
1276     {
1277         var titleEl = this.titleEl();
1278         
1279         if(!titleEl){
1280             return;
1281         }
1282         
1283         titleEl.dom.innerHTML = v;
1284     },
1285     
1286     getTitle : function()
1287     {
1288         
1289         var titleEl = this.titleEl();
1290         
1291         if(!titleEl){
1292             return '';
1293         }
1294         
1295         return titleEl.dom.innerHTML;
1296     },
1297     
1298     setRightTitle : function(v)
1299     {
1300         var t = this.el.select('.panel-header-right',true).first();
1301         
1302         if(!t){
1303             return;
1304         }
1305         
1306         t.dom.innerHTML = v;
1307     }
1308    
1309 });
1310
1311  /*
1312  * - LGPL
1313  *
1314  * image
1315  * 
1316  */
1317
1318
1319 /**
1320  * @class Roo.bootstrap.Img
1321  * @extends Roo.bootstrap.Component
1322  * Bootstrap Img class
1323  * @cfg {Boolean} imgResponsive false | true
1324  * @cfg {String} border rounded | circle | thumbnail
1325  * @cfg {String} src image source
1326  * @cfg {String} alt image alternative text
1327  * @cfg {String} href a tag href
1328  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1329  * @cfg {String} xsUrl xs image source
1330  * @cfg {String} smUrl sm image source
1331  * @cfg {String} mdUrl md image source
1332  * @cfg {String} lgUrl lg image source
1333  * 
1334  * @constructor
1335  * Create a new Input
1336  * @param {Object} config The config object
1337  */
1338
1339 Roo.bootstrap.Img = function(config){
1340     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1341     
1342     this.addEvents({
1343         // img events
1344         /**
1345          * @event click
1346          * The img click event for the img.
1347          * @param {Roo.EventObject} e
1348          */
1349         "click" : true
1350     });
1351 };
1352
1353 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1354     
1355     imgResponsive: true,
1356     border: '',
1357     src: '',
1358     href: false,
1359     target: false,
1360     xsUrl: '',
1361     smUrl: '',
1362     mdUrl: '',
1363     lgUrl: '',
1364
1365     getAutoCreate : function()
1366     {   
1367         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1368             return this.createSingleImg();
1369         }
1370         
1371         var cfg = {
1372             tag: 'div',
1373             cls: 'roo-image-responsive-group',
1374             cn: []
1375         }
1376         var _this = this;
1377         
1378         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1379             
1380             if(!_this[size + 'Url']){
1381                 return;
1382             }
1383             
1384             var img = {
1385                 tag: 'img',
1386                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1387                 html: _this.html || cfg.html,
1388                 src: _this[size + 'Url']
1389             }
1390             
1391             img.cls += ' roo-image-responsive-' + size;
1392             
1393             var s = ['xs', 'sm', 'md', 'lg'];
1394             
1395             s.splice(s.indexOf(size), 1);
1396             
1397             Roo.each(s, function(ss){
1398                 img.cls += ' hidden-' + ss;
1399             });
1400             
1401             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1402                 cfg.cls += ' img-' + _this.border;
1403             }
1404             
1405             if(_this.alt){
1406                 cfg.alt = _this.alt;
1407             }
1408             
1409             if(_this.href){
1410                 var a = {
1411                     tag: 'a',
1412                     href: _this.href,
1413                     cn: [
1414                         img
1415                     ]
1416                 }
1417
1418                 if(this.target){
1419                     a.target = _this.target;
1420                 }
1421             }
1422             
1423             cfg.cn.push((_this.href) ? a : img);
1424             
1425         });
1426         
1427         return cfg;
1428     },
1429     
1430     createSingleImg : function()
1431     {
1432         var cfg = {
1433             tag: 'img',
1434             cls: (this.imgResponsive) ? 'img-responsive' : '',
1435             html : null
1436         }
1437         
1438         cfg.html = this.html || cfg.html;
1439         
1440         cfg.src = this.src || cfg.src;
1441         
1442         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1443             cfg.cls += ' img-' + this.border;
1444         }
1445         
1446         if(this.alt){
1447             cfg.alt = this.alt;
1448         }
1449         
1450         if(this.href){
1451             var a = {
1452                 tag: 'a',
1453                 href: this.href,
1454                 cn: [
1455                     cfg
1456                 ]
1457             }
1458             
1459             if(this.target){
1460                 a.target = this.target;
1461             }
1462             
1463         }
1464         
1465         return (this.href) ? a : cfg;
1466     },
1467     
1468     initEvents: function() 
1469     {
1470         if(!this.href){
1471             this.el.on('click', this.onClick, this);
1472         }
1473         
1474     },
1475     
1476     onClick : function(e)
1477     {
1478         Roo.log('img onclick');
1479         this.fireEvent('click', this, e);
1480     }
1481    
1482 });
1483
1484  /*
1485  * - LGPL
1486  *
1487  * image
1488  * 
1489  */
1490
1491
1492 /**
1493  * @class Roo.bootstrap.Link
1494  * @extends Roo.bootstrap.Component
1495  * Bootstrap Link Class
1496  * @cfg {String} alt image alternative text
1497  * @cfg {String} href a tag href
1498  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1499  * @cfg {String} html the content of the link.
1500  * @cfg {String} anchor name for the anchor link
1501
1502  * @cfg {Boolean} preventDefault (true | false) default false
1503
1504  * 
1505  * @constructor
1506  * Create a new Input
1507  * @param {Object} config The config object
1508  */
1509
1510 Roo.bootstrap.Link = function(config){
1511     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1512     
1513     this.addEvents({
1514         // img events
1515         /**
1516          * @event click
1517          * The img click event for the img.
1518          * @param {Roo.EventObject} e
1519          */
1520         "click" : true
1521     });
1522 };
1523
1524 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1525     
1526     href: false,
1527     target: false,
1528     preventDefault: false,
1529     anchor : false,
1530     alt : false,
1531
1532     getAutoCreate : function()
1533     {
1534         
1535         var cfg = {
1536             tag: 'a'
1537         };
1538         // anchor's do not require html/href...
1539         if (this.anchor === false) {
1540             cfg.html = this.html || '';
1541             cfg.href = this.href || '#';
1542         } else {
1543             cfg.name = this.anchor;
1544             if (this.html !== false) {
1545                 cfg.html = this.html;
1546             }
1547             if (this.href !== false) {
1548                 cfg.href = this.href;
1549             }
1550         }
1551         
1552         if(this.alt !== false){
1553             cfg.alt = this.alt;
1554         }
1555         
1556         
1557         if(this.target !== false) {
1558             cfg.target = this.target;
1559         }
1560         
1561         return cfg;
1562     },
1563     
1564     initEvents: function() {
1565         
1566         if(!this.href || this.preventDefault){
1567             this.el.on('click', this.onClick, this);
1568         }
1569     },
1570     
1571     onClick : function(e)
1572     {
1573         if(this.preventDefault){
1574             e.preventDefault();
1575         }
1576         //Roo.log('img onclick');
1577         this.fireEvent('click', this, e);
1578     }
1579    
1580 });
1581
1582  /*
1583  * - LGPL
1584  *
1585  * header
1586  * 
1587  */
1588
1589 /**
1590  * @class Roo.bootstrap.Header
1591  * @extends Roo.bootstrap.Component
1592  * Bootstrap Header class
1593  * @cfg {String} html content of header
1594  * @cfg {Number} level (1|2|3|4|5|6) default 1
1595  * 
1596  * @constructor
1597  * Create a new Header
1598  * @param {Object} config The config object
1599  */
1600
1601
1602 Roo.bootstrap.Header  = function(config){
1603     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1604 };
1605
1606 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1607     
1608     //href : false,
1609     html : false,
1610     level : 1,
1611     
1612     
1613     
1614     getAutoCreate : function(){
1615         
1616         
1617         
1618         var cfg = {
1619             tag: 'h' + (1 *this.level),
1620             html: this.html || ''
1621         } ;
1622         
1623         return cfg;
1624     }
1625    
1626 });
1627
1628  
1629
1630  /*
1631  * Based on:
1632  * Ext JS Library 1.1.1
1633  * Copyright(c) 2006-2007, Ext JS, LLC.
1634  *
1635  * Originally Released Under LGPL - original licence link has changed is not relivant.
1636  *
1637  * Fork - LGPL
1638  * <script type="text/javascript">
1639  */
1640  
1641 /**
1642  * @class Roo.bootstrap.MenuMgr
1643  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1644  * @singleton
1645  */
1646 Roo.bootstrap.MenuMgr = function(){
1647    var menus, active, groups = {}, attached = false, lastShow = new Date();
1648
1649    // private - called when first menu is created
1650    function init(){
1651        menus = {};
1652        active = new Roo.util.MixedCollection();
1653        Roo.get(document).addKeyListener(27, function(){
1654            if(active.length > 0){
1655                hideAll();
1656            }
1657        });
1658    }
1659
1660    // private
1661    function hideAll(){
1662        if(active && active.length > 0){
1663            var c = active.clone();
1664            c.each(function(m){
1665                m.hide();
1666            });
1667        }
1668    }
1669
1670    // private
1671    function onHide(m){
1672        active.remove(m);
1673        if(active.length < 1){
1674            Roo.get(document).un("mouseup", onMouseDown);
1675             
1676            attached = false;
1677        }
1678    }
1679
1680    // private
1681    function onShow(m){
1682        var last = active.last();
1683        lastShow = new Date();
1684        active.add(m);
1685        if(!attached){
1686           Roo.get(document).on("mouseup", onMouseDown);
1687            
1688            attached = true;
1689        }
1690        if(m.parentMenu){
1691           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1692           m.parentMenu.activeChild = m;
1693        }else if(last && last.isVisible()){
1694           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1695        }
1696    }
1697
1698    // private
1699    function onBeforeHide(m){
1700        if(m.activeChild){
1701            m.activeChild.hide();
1702        }
1703        if(m.autoHideTimer){
1704            clearTimeout(m.autoHideTimer);
1705            delete m.autoHideTimer;
1706        }
1707    }
1708
1709    // private
1710    function onBeforeShow(m){
1711        var pm = m.parentMenu;
1712        if(!pm && !m.allowOtherMenus){
1713            hideAll();
1714        }else if(pm && pm.activeChild && active != m){
1715            pm.activeChild.hide();
1716        }
1717    }
1718
1719    // private this should really trigger on mouseup..
1720    function onMouseDown(e){
1721         Roo.log("on Mouse Up");
1722         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1723             Roo.log("hideAll");
1724             hideAll();
1725             e.stopEvent();
1726         }
1727         
1728         
1729    }
1730
1731    // private
1732    function onBeforeCheck(mi, state){
1733        if(state){
1734            var g = groups[mi.group];
1735            for(var i = 0, l = g.length; i < l; i++){
1736                if(g[i] != mi){
1737                    g[i].setChecked(false);
1738                }
1739            }
1740        }
1741    }
1742
1743    return {
1744
1745        /**
1746         * Hides all menus that are currently visible
1747         */
1748        hideAll : function(){
1749             hideAll();  
1750        },
1751
1752        // private
1753        register : function(menu){
1754            if(!menus){
1755                init();
1756            }
1757            menus[menu.id] = menu;
1758            menu.on("beforehide", onBeforeHide);
1759            menu.on("hide", onHide);
1760            menu.on("beforeshow", onBeforeShow);
1761            menu.on("show", onShow);
1762            var g = menu.group;
1763            if(g && menu.events["checkchange"]){
1764                if(!groups[g]){
1765                    groups[g] = [];
1766                }
1767                groups[g].push(menu);
1768                menu.on("checkchange", onCheck);
1769            }
1770        },
1771
1772         /**
1773          * Returns a {@link Roo.menu.Menu} object
1774          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1775          * be used to generate and return a new Menu instance.
1776          */
1777        get : function(menu){
1778            if(typeof menu == "string"){ // menu id
1779                return menus[menu];
1780            }else if(menu.events){  // menu instance
1781                return menu;
1782            }
1783            /*else if(typeof menu.length == 'number'){ // array of menu items?
1784                return new Roo.bootstrap.Menu({items:menu});
1785            }else{ // otherwise, must be a config
1786                return new Roo.bootstrap.Menu(menu);
1787            }
1788            */
1789            return false;
1790        },
1791
1792        // private
1793        unregister : function(menu){
1794            delete menus[menu.id];
1795            menu.un("beforehide", onBeforeHide);
1796            menu.un("hide", onHide);
1797            menu.un("beforeshow", onBeforeShow);
1798            menu.un("show", onShow);
1799            var g = menu.group;
1800            if(g && menu.events["checkchange"]){
1801                groups[g].remove(menu);
1802                menu.un("checkchange", onCheck);
1803            }
1804        },
1805
1806        // private
1807        registerCheckable : function(menuItem){
1808            var g = menuItem.group;
1809            if(g){
1810                if(!groups[g]){
1811                    groups[g] = [];
1812                }
1813                groups[g].push(menuItem);
1814                menuItem.on("beforecheckchange", onBeforeCheck);
1815            }
1816        },
1817
1818        // private
1819        unregisterCheckable : function(menuItem){
1820            var g = menuItem.group;
1821            if(g){
1822                groups[g].remove(menuItem);
1823                menuItem.un("beforecheckchange", onBeforeCheck);
1824            }
1825        }
1826    };
1827 }();/*
1828  * - LGPL
1829  *
1830  * menu
1831  * 
1832  */
1833
1834 /**
1835  * @class Roo.bootstrap.Menu
1836  * @extends Roo.bootstrap.Component
1837  * Bootstrap Menu class - container for MenuItems
1838  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1839  * 
1840  * @constructor
1841  * Create a new Menu
1842  * @param {Object} config The config object
1843  */
1844
1845
1846 Roo.bootstrap.Menu = function(config){
1847     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1848     if (this.registerMenu) {
1849         Roo.bootstrap.MenuMgr.register(this);
1850     }
1851     this.addEvents({
1852         /**
1853          * @event beforeshow
1854          * Fires before this menu is displayed
1855          * @param {Roo.menu.Menu} this
1856          */
1857         beforeshow : true,
1858         /**
1859          * @event beforehide
1860          * Fires before this menu is hidden
1861          * @param {Roo.menu.Menu} this
1862          */
1863         beforehide : true,
1864         /**
1865          * @event show
1866          * Fires after this menu is displayed
1867          * @param {Roo.menu.Menu} this
1868          */
1869         show : true,
1870         /**
1871          * @event hide
1872          * Fires after this menu is hidden
1873          * @param {Roo.menu.Menu} this
1874          */
1875         hide : true,
1876         /**
1877          * @event click
1878          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1879          * @param {Roo.menu.Menu} this
1880          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1881          * @param {Roo.EventObject} e
1882          */
1883         click : true,
1884         /**
1885          * @event mouseover
1886          * Fires when the mouse is hovering over this menu
1887          * @param {Roo.menu.Menu} this
1888          * @param {Roo.EventObject} e
1889          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1890          */
1891         mouseover : true,
1892         /**
1893          * @event mouseout
1894          * Fires when the mouse exits this menu
1895          * @param {Roo.menu.Menu} this
1896          * @param {Roo.EventObject} e
1897          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1898          */
1899         mouseout : true,
1900         /**
1901          * @event itemclick
1902          * Fires when a menu item contained in this menu is clicked
1903          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1904          * @param {Roo.EventObject} e
1905          */
1906         itemclick: true
1907     });
1908     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1909 };
1910
1911 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1912     
1913    /// html : false,
1914     //align : '',
1915     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1916     type: false,
1917     /**
1918      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1919      */
1920     registerMenu : true,
1921     
1922     menuItems :false, // stores the menu items..
1923     
1924     hidden:true,
1925     
1926     parentMenu : false,
1927     
1928     getChildContainer : function() {
1929         return this.el;  
1930     },
1931     
1932     getAutoCreate : function(){
1933          
1934         //if (['right'].indexOf(this.align)!==-1) {
1935         //    cfg.cn[1].cls += ' pull-right'
1936         //}
1937         
1938         
1939         var cfg = {
1940             tag : 'ul',
1941             cls : 'dropdown-menu' ,
1942             style : 'z-index:1000'
1943             
1944         }
1945         
1946         if (this.type === 'submenu') {
1947             cfg.cls = 'submenu active';
1948         }
1949         if (this.type === 'treeview') {
1950             cfg.cls = 'treeview-menu';
1951         }
1952         
1953         return cfg;
1954     },
1955     initEvents : function() {
1956         
1957        // Roo.log("ADD event");
1958        // Roo.log(this.triggerEl.dom);
1959         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
1960         
1961         this.triggerEl.addClass('dropdown-toggle');
1962         this.el.on(Roo.isTouch ? 'touchstart' : 'click' , this.onClick, this);
1963
1964         this.el.on("mouseover", this.onMouseOver, this);
1965         this.el.on("mouseout", this.onMouseOut, this);
1966         
1967         
1968     },
1969     findTargetItem : function(e){
1970         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1971         if(!t){
1972             return false;
1973         }
1974         //Roo.log(t);         Roo.log(t.id);
1975         if(t && t.id){
1976             //Roo.log(this.menuitems);
1977             return this.menuitems.get(t.id);
1978             
1979             //return this.items.get(t.menuItemId);
1980         }
1981         
1982         return false;
1983     },
1984     onClick : function(e){
1985         Roo.log("menu.onClick");
1986         var t = this.findTargetItem(e);
1987         if(!t || t.isContainer){
1988             return;
1989         }
1990         Roo.log(e);
1991         /*
1992         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1993             if(t == this.activeItem && t.shouldDeactivate(e)){
1994                 this.activeItem.deactivate();
1995                 delete this.activeItem;
1996                 return;
1997             }
1998             if(t.canActivate){
1999                 this.setActiveItem(t, true);
2000             }
2001             return;
2002             
2003             
2004         }
2005         */
2006        
2007         Roo.log('pass click event');
2008         
2009         t.onClick(e);
2010         
2011         this.fireEvent("click", this, t, e);
2012         
2013         this.hide();
2014     },
2015      onMouseOver : function(e){
2016         var t  = this.findTargetItem(e);
2017         //Roo.log(t);
2018         //if(t){
2019         //    if(t.canActivate && !t.disabled){
2020         //        this.setActiveItem(t, true);
2021         //    }
2022         //}
2023         
2024         this.fireEvent("mouseover", this, e, t);
2025     },
2026     isVisible : function(){
2027         return !this.hidden;
2028     },
2029      onMouseOut : function(e){
2030         var t  = this.findTargetItem(e);
2031         
2032         //if(t ){
2033         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2034         //        this.activeItem.deactivate();
2035         //        delete this.activeItem;
2036         //    }
2037         //}
2038         this.fireEvent("mouseout", this, e, t);
2039     },
2040     
2041     
2042     /**
2043      * Displays this menu relative to another element
2044      * @param {String/HTMLElement/Roo.Element} element The element to align to
2045      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2046      * the element (defaults to this.defaultAlign)
2047      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2048      */
2049     show : function(el, pos, parentMenu){
2050         this.parentMenu = parentMenu;
2051         if(!this.el){
2052             this.render();
2053         }
2054         this.fireEvent("beforeshow", this);
2055         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2056     },
2057      /**
2058      * Displays this menu at a specific xy position
2059      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2060      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2061      */
2062     showAt : function(xy, parentMenu, /* private: */_e){
2063         this.parentMenu = parentMenu;
2064         if(!this.el){
2065             this.render();
2066         }
2067         if(_e !== false){
2068             this.fireEvent("beforeshow", this);
2069             //xy = this.el.adjustForConstraints(xy);
2070         }
2071         
2072         //this.el.show();
2073         this.hideMenuItems();
2074         this.hidden = false;
2075         this.triggerEl.addClass('open');
2076         
2077         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2078             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2079         }
2080         
2081         this.el.setXY(xy);
2082         this.focus();
2083         this.fireEvent("show", this);
2084     },
2085     
2086     focus : function(){
2087         return;
2088         if(!this.hidden){
2089             this.doFocus.defer(50, this);
2090         }
2091     },
2092
2093     doFocus : function(){
2094         if(!this.hidden){
2095             this.focusEl.focus();
2096         }
2097     },
2098
2099     /**
2100      * Hides this menu and optionally all parent menus
2101      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2102      */
2103     hide : function(deep){
2104         
2105         this.hideMenuItems();
2106         if(this.el && this.isVisible()){
2107             this.fireEvent("beforehide", this);
2108             if(this.activeItem){
2109                 this.activeItem.deactivate();
2110                 this.activeItem = null;
2111             }
2112             this.triggerEl.removeClass('open');;
2113             this.hidden = true;
2114             this.fireEvent("hide", this);
2115         }
2116         if(deep === true && this.parentMenu){
2117             this.parentMenu.hide(true);
2118         }
2119     },
2120     
2121     onTriggerPress  : function(e)
2122     {
2123         
2124         Roo.log('trigger press');
2125         //Roo.log(e.getTarget());
2126        // Roo.log(this.triggerEl.dom);
2127         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
2128             return;
2129         }
2130         
2131         if (this.isVisible()) {
2132             Roo.log('hide');
2133             this.hide();
2134         } else {
2135             Roo.log('show');
2136             this.show(this.triggerEl, false, false);
2137         }
2138         
2139         e.stopEvent();
2140     },
2141     
2142          
2143        
2144     
2145     hideMenuItems : function()
2146     {
2147         //$(backdrop).remove()
2148         Roo.select('.open',true).each(function(aa) {
2149             
2150             aa.removeClass('open');
2151           //var parent = getParent($(this))
2152           //var relatedTarget = { relatedTarget: this }
2153           
2154            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2155           //if (e.isDefaultPrevented()) return
2156            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2157         })
2158     },
2159     addxtypeChild : function (tree, cntr) {
2160         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2161           
2162         this.menuitems.add(comp);
2163         return comp;
2164
2165     },
2166     getEl : function()
2167     {
2168         Roo.log(this.el);
2169         return this.el;
2170     }
2171 });
2172
2173  
2174  /*
2175  * - LGPL
2176  *
2177  * menu item
2178  * 
2179  */
2180
2181
2182 /**
2183  * @class Roo.bootstrap.MenuItem
2184  * @extends Roo.bootstrap.Component
2185  * Bootstrap MenuItem class
2186  * @cfg {String} html the menu label
2187  * @cfg {String} href the link
2188  * @cfg {Boolean} preventDefault (true | false) default true
2189  * @cfg {Boolean} isContainer (true | false) default false
2190  * 
2191  * 
2192  * @constructor
2193  * Create a new MenuItem
2194  * @param {Object} config The config object
2195  */
2196
2197
2198 Roo.bootstrap.MenuItem = function(config){
2199     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2200     this.addEvents({
2201         // raw events
2202         /**
2203          * @event click
2204          * The raw click event for the entire grid.
2205          * @param {Roo.bootstrap.MenuItem} this
2206          * @param {Roo.EventObject} e
2207          */
2208         "click" : true
2209     });
2210 };
2211
2212 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2213     
2214     href : false,
2215     html : false,
2216     preventDefault: true,
2217     isContainer : false,
2218     
2219     getAutoCreate : function(){
2220         
2221         if(this.isContainer){
2222             return {
2223                 tag: 'li',
2224                 cls: 'dropdown-menu-item'
2225             };
2226         }
2227         
2228         var cfg= {
2229             tag: 'li',
2230             cls: 'dropdown-menu-item',
2231             cn: [
2232                     {
2233                         tag : 'a',
2234                         href : '#',
2235                         html : 'Link'
2236                     }
2237                 ]
2238         };
2239         if (this.parent().type == 'treeview') {
2240             cfg.cls = 'treeview-menu';
2241         }
2242         
2243         cfg.cn[0].href = this.href || cfg.cn[0].href ;
2244         cfg.cn[0].html = this.html || cfg.cn[0].html ;
2245         return cfg;
2246     },
2247     
2248     initEvents: function() {
2249         
2250         //this.el.select('a').on('click', this.onClick, this);
2251         
2252     },
2253     onClick : function(e)
2254     {
2255         Roo.log('item on click ');
2256         //if(this.preventDefault){
2257         //    e.preventDefault();
2258         //}
2259         //this.parent().hideMenuItems();
2260         
2261         this.fireEvent('click', this, e);
2262     },
2263     getEl : function()
2264     {
2265         return this.el;
2266     }
2267 });
2268
2269  
2270
2271  /*
2272  * - LGPL
2273  *
2274  * menu separator
2275  * 
2276  */
2277
2278
2279 /**
2280  * @class Roo.bootstrap.MenuSeparator
2281  * @extends Roo.bootstrap.Component
2282  * Bootstrap MenuSeparator class
2283  * 
2284  * @constructor
2285  * Create a new MenuItem
2286  * @param {Object} config The config object
2287  */
2288
2289
2290 Roo.bootstrap.MenuSeparator = function(config){
2291     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2292 };
2293
2294 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2295     
2296     getAutoCreate : function(){
2297         var cfg = {
2298             cls: 'divider',
2299             tag : 'li'
2300         };
2301         
2302         return cfg;
2303     }
2304    
2305 });
2306
2307  
2308
2309  
2310 /*
2311 * Licence: LGPL
2312 */
2313
2314 /**
2315  * @class Roo.bootstrap.Modal
2316  * @extends Roo.bootstrap.Component
2317  * Bootstrap Modal class
2318  * @cfg {String} title Title of dialog
2319  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2320  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn 
2321  * @cfg {Boolean} specificTitle default false
2322  * @cfg {Array} buttons Array of buttons or standard button set..
2323  * @cfg {String} buttonPosition (left|right|center) default right
2324  * @cfg {Boolean} animate default true
2325  * @cfg {Boolean} allow_close default true
2326  * 
2327  * @constructor
2328  * Create a new Modal Dialog
2329  * @param {Object} config The config object
2330  */
2331
2332 Roo.bootstrap.Modal = function(config){
2333     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2334     this.addEvents({
2335         // raw events
2336         /**
2337          * @event btnclick
2338          * The raw btnclick event for the button
2339          * @param {Roo.EventObject} e
2340          */
2341         "btnclick" : true
2342     });
2343     this.buttons = this.buttons || [];
2344      
2345     if (this.tmpl) {
2346         this.tmpl = Roo.factory(this.tmpl);
2347     }
2348     
2349 };
2350
2351 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2352     
2353     title : 'test dialog',
2354    
2355     buttons : false,
2356     
2357     // set on load...
2358      
2359     html: false,
2360     
2361     tmp: false,
2362     
2363     specificTitle: false,
2364     
2365     buttonPosition: 'right',
2366     
2367     allow_close : true,
2368     
2369     animate : true,
2370     
2371     
2372      // private
2373     bodyEl:  false,
2374     footerEl:  false,
2375     titleEl:  false,
2376     closeEl:  false,
2377     
2378     
2379     onRender : function(ct, position)
2380     {
2381         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2382      
2383         if(!this.el){
2384             var cfg = Roo.apply({},  this.getAutoCreate());
2385             cfg.id = Roo.id();
2386             //if(!cfg.name){
2387             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2388             //}
2389             //if (!cfg.name.length) {
2390             //    delete cfg.name;
2391            // }
2392             if (this.cls) {
2393                 cfg.cls += ' ' + this.cls;
2394             }
2395             if (this.style) {
2396                 cfg.style = this.style;
2397             }
2398             this.el = Roo.get(document.body).createChild(cfg, position);
2399         }
2400         //var type = this.el.dom.type;
2401         
2402         
2403         
2404         
2405         if(this.tabIndex !== undefined){
2406             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2407         }
2408         
2409         
2410         this.bodyEl = this.el.select('.modal-body',true).first();
2411         this.closeEl = this.el.select('.modal-header .close', true).first();
2412         this.footerEl = this.el.select('.modal-footer',true).first();
2413         this.titleEl = this.el.select('.modal-title',true).first();
2414         
2415         
2416          
2417         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2418         this.maskEl.enableDisplayMode("block");
2419         this.maskEl.hide();
2420         //this.el.addClass("x-dlg-modal");
2421     
2422         if (this.buttons.length) {
2423             Roo.each(this.buttons, function(bb) {
2424                 b = Roo.apply({}, bb);
2425                 b.xns = b.xns || Roo.bootstrap;
2426                 b.xtype = b.xtype || 'Button';
2427                 if (typeof(b.listeners) == 'undefined') {
2428                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2429                 }
2430                 
2431                 var btn = Roo.factory(b);
2432                 
2433                 btn.onRender(this.el.select('.modal-footer div').first());
2434                 
2435             },this);
2436         }
2437         // render the children.
2438         var nitems = [];
2439         
2440         if(typeof(this.items) != 'undefined'){
2441             var items = this.items;
2442             delete this.items;
2443
2444             for(var i =0;i < items.length;i++) {
2445                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2446             }
2447         }
2448         
2449         this.items = nitems;
2450         
2451         // where are these used - they used to be body/close/footer
2452         
2453        
2454         this.initEvents();
2455         //this.el.addClass([this.fieldClass, this.cls]);
2456         
2457     },
2458     getAutoCreate : function(){
2459         
2460         
2461         var bdy = {
2462                 cls : 'modal-body',
2463                 html : this.html || ''
2464         };
2465         
2466         var title = {
2467             tag: 'h4',
2468             cls : 'modal-title',
2469             html : this.title
2470         };
2471         
2472         if(this.specificTitle){
2473             title = this.title;
2474             
2475         };
2476         
2477         var header = [];
2478         if (this.allow_close) {
2479             header.push({
2480                 tag: 'button',
2481                 cls : 'close',
2482                 html : '&times'
2483             });
2484         }
2485         header.push(title);
2486         
2487         var modal = {
2488             cls: "modal",
2489             style : 'display: none',
2490             cn : [
2491                 {
2492                     cls: "modal-dialog",
2493                     cn : [
2494                         {
2495                             cls : "modal-content",
2496                             cn : [
2497                                 {
2498                                     cls : 'modal-header',
2499                                     cn : header
2500                                 },
2501                                 bdy,
2502                                 {
2503                                     cls : 'modal-footer',
2504                                     cn : [
2505                                         {
2506                                             tag: 'div',
2507                                             cls: 'btn-' + this.buttonPosition
2508                                         }
2509                                     ]
2510                                     
2511                                 }
2512                                 
2513                                 
2514                             ]
2515                             
2516                         }
2517                     ]
2518                         
2519                 }
2520             ]
2521         };
2522         
2523         if(this.animate){
2524             modal.cls += ' fade';
2525         }
2526         
2527         return modal;
2528           
2529     },
2530     getChildContainer : function() {
2531          
2532          return this.bodyEl;
2533         
2534     },
2535     getButtonContainer : function() {
2536          return this.el.select('.modal-footer div',true).first();
2537         
2538     },
2539     initEvents : function()
2540     {
2541         if (this.allow_close) {
2542             this.closeEl.on('click', this.hide, this);
2543         }
2544
2545     },
2546     show : function() {
2547         
2548         if (!this.rendered) {
2549             this.render();
2550         }
2551         
2552         this.el.setStyle('display', 'block');
2553         
2554         if(this.animate){
2555             var _this = this;
2556             (function(){ _this.el.addClass('in'); }).defer(50);
2557         }else{
2558             this.el.addClass('in');
2559         }
2560         
2561         // not sure how we can show data in here.. 
2562         //if (this.tmpl) {
2563         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2564         //}
2565         
2566         Roo.get(document.body).addClass("x-body-masked");
2567         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2568         this.maskEl.show();
2569         this.el.setStyle('zIndex', '10001');
2570        
2571         this.fireEvent('show', this);
2572         
2573         
2574     },
2575     hide : function()
2576     {
2577         this.maskEl.hide();
2578         Roo.get(document.body).removeClass("x-body-masked");
2579         this.el.removeClass('in');
2580         
2581         if(this.animate){
2582             var _this = this;
2583             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2584         }else{
2585             this.el.setStyle('display', 'none');
2586         }
2587         
2588         this.fireEvent('hide', this);
2589     },
2590     
2591     addButton : function(str, cb)
2592     {
2593          
2594         
2595         var b = Roo.apply({}, { html : str } );
2596         b.xns = b.xns || Roo.bootstrap;
2597         b.xtype = b.xtype || 'Button';
2598         if (typeof(b.listeners) == 'undefined') {
2599             b.listeners = { click : cb.createDelegate(this)  };
2600         }
2601         
2602         var btn = Roo.factory(b);
2603            
2604         btn.onRender(this.el.select('.modal-footer div').first());
2605         
2606         return btn;   
2607        
2608     },
2609     
2610     setDefaultButton : function(btn)
2611     {
2612         //this.el.select('.modal-footer').()
2613     },
2614     resizeTo: function(w,h)
2615     {
2616         // skip..
2617     },
2618     setContentSize  : function(w, h)
2619     {
2620         
2621     },
2622     onButtonClick: function(btn,e)
2623     {
2624         //Roo.log([a,b,c]);
2625         this.fireEvent('btnclick', btn.name, e);
2626     },
2627      /**
2628      * Set the title of the Dialog
2629      * @param {String} str new Title
2630      */
2631     setTitle: function(str) {
2632         this.titleEl.dom.innerHTML = str;    
2633     },
2634     /**
2635      * Set the body of the Dialog
2636      * @param {String} str new Title
2637      */
2638     setBody: function(str) {
2639         this.bodyEl.dom.innerHTML = str;    
2640     },
2641     /**
2642      * Set the body of the Dialog using the template
2643      * @param {Obj} data - apply this data to the template and replace the body contents.
2644      */
2645     applyBody: function(obj)
2646     {
2647         if (!this.tmpl) {
2648             Roo.log("Error - using apply Body without a template");
2649             //code
2650         }
2651         this.tmpl.overwrite(this.bodyEl, obj);
2652     }
2653     
2654 });
2655
2656
2657 Roo.apply(Roo.bootstrap.Modal,  {
2658     /**
2659          * Button config that displays a single OK button
2660          * @type Object
2661          */
2662         OK :  [{
2663             name : 'ok',
2664             weight : 'primary',
2665             html : 'OK'
2666         }], 
2667         /**
2668          * Button config that displays Yes and No buttons
2669          * @type Object
2670          */
2671         YESNO : [
2672             {
2673                 name  : 'no',
2674                 html : 'No'
2675             },
2676             {
2677                 name  :'yes',
2678                 weight : 'primary',
2679                 html : 'Yes'
2680             }
2681         ],
2682         
2683         /**
2684          * Button config that displays OK and Cancel buttons
2685          * @type Object
2686          */
2687         OKCANCEL : [
2688             {
2689                name : 'cancel',
2690                 html : 'Cancel'
2691             },
2692             {
2693                 name : 'ok',
2694                 weight : 'primary',
2695                 html : 'OK'
2696             }
2697         ],
2698         /**
2699          * Button config that displays Yes, No and Cancel buttons
2700          * @type Object
2701          */
2702         YESNOCANCEL : [
2703             {
2704                 name : 'yes',
2705                 weight : 'primary',
2706                 html : 'Yes'
2707             },
2708             {
2709                 name : 'no',
2710                 html : 'No'
2711             },
2712             {
2713                 name : 'cancel',
2714                 html : 'Cancel'
2715             }
2716         ]
2717 });
2718  
2719  /*
2720  * - LGPL
2721  *
2722  * messagebox - can be used as a replace
2723  * 
2724  */
2725 /**
2726  * @class Roo.MessageBox
2727  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2728  * Example usage:
2729  *<pre><code>
2730 // Basic alert:
2731 Roo.Msg.alert('Status', 'Changes saved successfully.');
2732
2733 // Prompt for user data:
2734 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2735     if (btn == 'ok'){
2736         // process text value...
2737     }
2738 });
2739
2740 // Show a dialog using config options:
2741 Roo.Msg.show({
2742    title:'Save Changes?',
2743    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2744    buttons: Roo.Msg.YESNOCANCEL,
2745    fn: processResult,
2746    animEl: 'elId'
2747 });
2748 </code></pre>
2749  * @singleton
2750  */
2751 Roo.bootstrap.MessageBox = function(){
2752     var dlg, opt, mask, waitTimer;
2753     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2754     var buttons, activeTextEl, bwidth;
2755
2756     
2757     // private
2758     var handleButton = function(button){
2759         dlg.hide();
2760         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2761     };
2762
2763     // private
2764     var handleHide = function(){
2765         if(opt && opt.cls){
2766             dlg.el.removeClass(opt.cls);
2767         }
2768         //if(waitTimer){
2769         //    Roo.TaskMgr.stop(waitTimer);
2770         //    waitTimer = null;
2771         //}
2772     };
2773
2774     // private
2775     var updateButtons = function(b){
2776         var width = 0;
2777         if(!b){
2778             buttons["ok"].hide();
2779             buttons["cancel"].hide();
2780             buttons["yes"].hide();
2781             buttons["no"].hide();
2782             //dlg.footer.dom.style.display = 'none';
2783             return width;
2784         }
2785         dlg.footerEl.dom.style.display = '';
2786         for(var k in buttons){
2787             if(typeof buttons[k] != "function"){
2788                 if(b[k]){
2789                     buttons[k].show();
2790                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2791                     width += buttons[k].el.getWidth()+15;
2792                 }else{
2793                     buttons[k].hide();
2794                 }
2795             }
2796         }
2797         return width;
2798     };
2799
2800     // private
2801     var handleEsc = function(d, k, e){
2802         if(opt && opt.closable !== false){
2803             dlg.hide();
2804         }
2805         if(e){
2806             e.stopEvent();
2807         }
2808     };
2809
2810     return {
2811         /**
2812          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2813          * @return {Roo.BasicDialog} The BasicDialog element
2814          */
2815         getDialog : function(){
2816            if(!dlg){
2817                 dlg = new Roo.bootstrap.Modal( {
2818                     //draggable: true,
2819                     //resizable:false,
2820                     //constraintoviewport:false,
2821                     //fixedcenter:true,
2822                     //collapsible : false,
2823                     //shim:true,
2824                     //modal: true,
2825                   //  width:400,
2826                   //  height:100,
2827                     //buttonAlign:"center",
2828                     closeClick : function(){
2829                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2830                             handleButton("no");
2831                         }else{
2832                             handleButton("cancel");
2833                         }
2834                     }
2835                 });
2836                 dlg.render();
2837                 dlg.on("hide", handleHide);
2838                 mask = dlg.mask;
2839                 //dlg.addKeyListener(27, handleEsc);
2840                 buttons = {};
2841                 this.buttons = buttons;
2842                 var bt = this.buttonText;
2843                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2844                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2845                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2846                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2847                 Roo.log(buttons)
2848                 bodyEl = dlg.bodyEl.createChild({
2849
2850                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2851                         '<textarea class="roo-mb-textarea"></textarea>' +
2852                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2853                 });
2854                 msgEl = bodyEl.dom.firstChild;
2855                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2856                 textboxEl.enableDisplayMode();
2857                 textboxEl.addKeyListener([10,13], function(){
2858                     if(dlg.isVisible() && opt && opt.buttons){
2859                         if(opt.buttons.ok){
2860                             handleButton("ok");
2861                         }else if(opt.buttons.yes){
2862                             handleButton("yes");
2863                         }
2864                     }
2865                 });
2866                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2867                 textareaEl.enableDisplayMode();
2868                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2869                 progressEl.enableDisplayMode();
2870                 var pf = progressEl.dom.firstChild;
2871                 if (pf) {
2872                     pp = Roo.get(pf.firstChild);
2873                     pp.setHeight(pf.offsetHeight);
2874                 }
2875                 
2876             }
2877             return dlg;
2878         },
2879
2880         /**
2881          * Updates the message box body text
2882          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2883          * the XHTML-compliant non-breaking space character '&amp;#160;')
2884          * @return {Roo.MessageBox} This message box
2885          */
2886         updateText : function(text){
2887             if(!dlg.isVisible() && !opt.width){
2888                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2889             }
2890             msgEl.innerHTML = text || '&#160;';
2891       
2892             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2893             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2894             var w = Math.max(
2895                     Math.min(opt.width || cw , this.maxWidth), 
2896                     Math.max(opt.minWidth || this.minWidth, bwidth)
2897             );
2898             if(opt.prompt){
2899                 activeTextEl.setWidth(w);
2900             }
2901             if(dlg.isVisible()){
2902                 dlg.fixedcenter = false;
2903             }
2904             // to big, make it scroll. = But as usual stupid IE does not support
2905             // !important..
2906             
2907             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2908                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2909                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2910             } else {
2911                 bodyEl.dom.style.height = '';
2912                 bodyEl.dom.style.overflowY = '';
2913             }
2914             if (cw > w) {
2915                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2916             } else {
2917                 bodyEl.dom.style.overflowX = '';
2918             }
2919             
2920             dlg.setContentSize(w, bodyEl.getHeight());
2921             if(dlg.isVisible()){
2922                 dlg.fixedcenter = true;
2923             }
2924             return this;
2925         },
2926
2927         /**
2928          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2929          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2930          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2931          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2932          * @return {Roo.MessageBox} This message box
2933          */
2934         updateProgress : function(value, text){
2935             if(text){
2936                 this.updateText(text);
2937             }
2938             if (pp) { // weird bug on my firefox - for some reason this is not defined
2939                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2940             }
2941             return this;
2942         },        
2943
2944         /**
2945          * Returns true if the message box is currently displayed
2946          * @return {Boolean} True if the message box is visible, else false
2947          */
2948         isVisible : function(){
2949             return dlg && dlg.isVisible();  
2950         },
2951
2952         /**
2953          * Hides the message box if it is displayed
2954          */
2955         hide : function(){
2956             if(this.isVisible()){
2957                 dlg.hide();
2958             }  
2959         },
2960
2961         /**
2962          * Displays a new message box, or reinitializes an existing message box, based on the config options
2963          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2964          * The following config object properties are supported:
2965          * <pre>
2966 Property    Type             Description
2967 ----------  ---------------  ------------------------------------------------------------------------------------
2968 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2969                                    closes (defaults to undefined)
2970 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2971                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2972 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2973                                    progress and wait dialogs will ignore this property and always hide the
2974                                    close button as they can only be closed programmatically.
2975 cls               String           A custom CSS class to apply to the message box element
2976 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2977                                    displayed (defaults to 75)
2978 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2979                                    function will be btn (the name of the button that was clicked, if applicable,
2980                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2981                                    Progress and wait dialogs will ignore this option since they do not respond to
2982                                    user actions and can only be closed programmatically, so any required function
2983                                    should be called by the same code after it closes the dialog.
2984 icon              String           A CSS class that provides a background image to be used as an icon for
2985                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2986 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2987 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2988 modal             Boolean          False to allow user interaction with the page while the message box is
2989                                    displayed (defaults to true)
2990 msg               String           A string that will replace the existing message box body text (defaults
2991                                    to the XHTML-compliant non-breaking space character '&#160;')
2992 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2993 progress          Boolean          True to display a progress bar (defaults to false)
2994 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2995 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2996 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2997 title             String           The title text
2998 value             String           The string value to set into the active textbox element if displayed
2999 wait              Boolean          True to display a progress bar (defaults to false)
3000 width             Number           The width of the dialog in pixels
3001 </pre>
3002          *
3003          * Example usage:
3004          * <pre><code>
3005 Roo.Msg.show({
3006    title: 'Address',
3007    msg: 'Please enter your address:',
3008    width: 300,
3009    buttons: Roo.MessageBox.OKCANCEL,
3010    multiline: true,
3011    fn: saveAddress,
3012    animEl: 'addAddressBtn'
3013 });
3014 </code></pre>
3015          * @param {Object} config Configuration options
3016          * @return {Roo.MessageBox} This message box
3017          */
3018         show : function(options)
3019         {
3020             
3021             // this causes nightmares if you show one dialog after another
3022             // especially on callbacks..
3023              
3024             if(this.isVisible()){
3025                 
3026                 this.hide();
3027                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3028                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3029                 Roo.log("New Dialog Message:" +  options.msg )
3030                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3031                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3032                 
3033             }
3034             var d = this.getDialog();
3035             opt = options;
3036             d.setTitle(opt.title || "&#160;");
3037             d.closeEl.setDisplayed(opt.closable !== false);
3038             activeTextEl = textboxEl;
3039             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3040             if(opt.prompt){
3041                 if(opt.multiline){
3042                     textboxEl.hide();
3043                     textareaEl.show();
3044                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3045                         opt.multiline : this.defaultTextHeight);
3046                     activeTextEl = textareaEl;
3047                 }else{
3048                     textboxEl.show();
3049                     textareaEl.hide();
3050                 }
3051             }else{
3052                 textboxEl.hide();
3053                 textareaEl.hide();
3054             }
3055             progressEl.setDisplayed(opt.progress === true);
3056             this.updateProgress(0);
3057             activeTextEl.dom.value = opt.value || "";
3058             if(opt.prompt){
3059                 dlg.setDefaultButton(activeTextEl);
3060             }else{
3061                 var bs = opt.buttons;
3062                 var db = null;
3063                 if(bs && bs.ok){
3064                     db = buttons["ok"];
3065                 }else if(bs && bs.yes){
3066                     db = buttons["yes"];
3067                 }
3068                 dlg.setDefaultButton(db);
3069             }
3070             bwidth = updateButtons(opt.buttons);
3071             this.updateText(opt.msg);
3072             if(opt.cls){
3073                 d.el.addClass(opt.cls);
3074             }
3075             d.proxyDrag = opt.proxyDrag === true;
3076             d.modal = opt.modal !== false;
3077             d.mask = opt.modal !== false ? mask : false;
3078             if(!d.isVisible()){
3079                 // force it to the end of the z-index stack so it gets a cursor in FF
3080                 document.body.appendChild(dlg.el.dom);
3081                 d.animateTarget = null;
3082                 d.show(options.animEl);
3083             }
3084             return this;
3085         },
3086
3087         /**
3088          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3089          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3090          * and closing the message box when the process is complete.
3091          * @param {String} title The title bar text
3092          * @param {String} msg The message box body text
3093          * @return {Roo.MessageBox} This message box
3094          */
3095         progress : function(title, msg){
3096             this.show({
3097                 title : title,
3098                 msg : msg,
3099                 buttons: false,
3100                 progress:true,
3101                 closable:false,
3102                 minWidth: this.minProgressWidth,
3103                 modal : true
3104             });
3105             return this;
3106         },
3107
3108         /**
3109          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3110          * If a callback function is passed it will be called after the user clicks the button, and the
3111          * id of the button that was clicked will be passed as the only parameter to the callback
3112          * (could also be the top-right close button).
3113          * @param {String} title The title bar text
3114          * @param {String} msg The message box body text
3115          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3116          * @param {Object} scope (optional) The scope of the callback function
3117          * @return {Roo.MessageBox} This message box
3118          */
3119         alert : function(title, msg, fn, scope){
3120             this.show({
3121                 title : title,
3122                 msg : msg,
3123                 buttons: this.OK,
3124                 fn: fn,
3125                 scope : scope,
3126                 modal : true
3127             });
3128             return this;
3129         },
3130
3131         /**
3132          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3133          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3134          * You are responsible for closing the message box when the process is complete.
3135          * @param {String} msg The message box body text
3136          * @param {String} title (optional) The title bar text
3137          * @return {Roo.MessageBox} This message box
3138          */
3139         wait : function(msg, title){
3140             this.show({
3141                 title : title,
3142                 msg : msg,
3143                 buttons: false,
3144                 closable:false,
3145                 progress:true,
3146                 modal:true,
3147                 width:300,
3148                 wait:true
3149             });
3150             waitTimer = Roo.TaskMgr.start({
3151                 run: function(i){
3152                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3153                 },
3154                 interval: 1000
3155             });
3156             return this;
3157         },
3158
3159         /**
3160          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3161          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3162          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3163          * @param {String} title The title bar text
3164          * @param {String} msg The message box body text
3165          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3166          * @param {Object} scope (optional) The scope of the callback function
3167          * @return {Roo.MessageBox} This message box
3168          */
3169         confirm : function(title, msg, fn, scope){
3170             this.show({
3171                 title : title,
3172                 msg : msg,
3173                 buttons: this.YESNO,
3174                 fn: fn,
3175                 scope : scope,
3176                 modal : true
3177             });
3178             return this;
3179         },
3180
3181         /**
3182          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3183          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3184          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3185          * (could also be the top-right close button) and the text that was entered will be passed as the two
3186          * parameters to the callback.
3187          * @param {String} title The title bar text
3188          * @param {String} msg The message box body text
3189          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3190          * @param {Object} scope (optional) The scope of the callback function
3191          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3192          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3193          * @return {Roo.MessageBox} This message box
3194          */
3195         prompt : function(title, msg, fn, scope, multiline){
3196             this.show({
3197                 title : title,
3198                 msg : msg,
3199                 buttons: this.OKCANCEL,
3200                 fn: fn,
3201                 minWidth:250,
3202                 scope : scope,
3203                 prompt:true,
3204                 multiline: multiline,
3205                 modal : true
3206             });
3207             return this;
3208         },
3209
3210         /**
3211          * Button config that displays a single OK button
3212          * @type Object
3213          */
3214         OK : {ok:true},
3215         /**
3216          * Button config that displays Yes and No buttons
3217          * @type Object
3218          */
3219         YESNO : {yes:true, no:true},
3220         /**
3221          * Button config that displays OK and Cancel buttons
3222          * @type Object
3223          */
3224         OKCANCEL : {ok:true, cancel:true},
3225         /**
3226          * Button config that displays Yes, No and Cancel buttons
3227          * @type Object
3228          */
3229         YESNOCANCEL : {yes:true, no:true, cancel:true},
3230
3231         /**
3232          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3233          * @type Number
3234          */
3235         defaultTextHeight : 75,
3236         /**
3237          * The maximum width in pixels of the message box (defaults to 600)
3238          * @type Number
3239          */
3240         maxWidth : 600,
3241         /**
3242          * The minimum width in pixels of the message box (defaults to 100)
3243          * @type Number
3244          */
3245         minWidth : 100,
3246         /**
3247          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3248          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3249          * @type Number
3250          */
3251         minProgressWidth : 250,
3252         /**
3253          * An object containing the default button text strings that can be overriden for localized language support.
3254          * Supported properties are: ok, cancel, yes and no.
3255          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3256          * @type Object
3257          */
3258         buttonText : {
3259             ok : "OK",
3260             cancel : "Cancel",
3261             yes : "Yes",
3262             no : "No"
3263         }
3264     };
3265 }();
3266
3267 /**
3268  * Shorthand for {@link Roo.MessageBox}
3269  */
3270 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3271 Roo.Msg = Roo.Msg || Roo.MessageBox;
3272 /*
3273  * - LGPL
3274  *
3275  * navbar
3276  * 
3277  */
3278
3279 /**
3280  * @class Roo.bootstrap.Navbar
3281  * @extends Roo.bootstrap.Component
3282  * Bootstrap Navbar class
3283
3284  * @constructor
3285  * Create a new Navbar
3286  * @param {Object} config The config object
3287  */
3288
3289
3290 Roo.bootstrap.Navbar = function(config){
3291     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3292     
3293 };
3294
3295 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3296     
3297     
3298    
3299     // private
3300     navItems : false,
3301     loadMask : false,
3302     
3303     
3304     getAutoCreate : function(){
3305         
3306         
3307         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3308         
3309     },
3310     
3311     initEvents :function ()
3312     {
3313         //Roo.log(this.el.select('.navbar-toggle',true));
3314         this.el.select('.navbar-toggle',true).on('click', function() {
3315            // Roo.log('click');
3316             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3317         }, this);
3318         
3319         var mark = {
3320             tag: "div",
3321             cls:"x-dlg-mask"
3322         }
3323         
3324         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3325         
3326         var size = this.el.getSize();
3327         this.maskEl.setSize(size.width, size.height);
3328         this.maskEl.enableDisplayMode("block");
3329         this.maskEl.hide();
3330         
3331         if(this.loadMask){
3332             this.maskEl.show();
3333         }
3334     },
3335     
3336     
3337     getChildContainer : function()
3338     {
3339         if (this.el.select('.collapse').getCount()) {
3340             return this.el.select('.collapse',true).first();
3341         }
3342         
3343         return this.el;
3344     },
3345     
3346     mask : function()
3347     {
3348         this.maskEl.show();
3349     },
3350     
3351     unmask : function()
3352     {
3353         this.maskEl.hide();
3354     } 
3355     
3356     
3357     
3358     
3359 });
3360
3361
3362
3363  
3364
3365  /*
3366  * - LGPL
3367  *
3368  * navbar
3369  * 
3370  */
3371
3372 /**
3373  * @class Roo.bootstrap.NavSimplebar
3374  * @extends Roo.bootstrap.Navbar
3375  * Bootstrap Sidebar class
3376  *
3377  * @cfg {Boolean} inverse is inverted color
3378  * 
3379  * @cfg {String} type (nav | pills | tabs)
3380  * @cfg {Boolean} arrangement stacked | justified
3381  * @cfg {String} align (left | right) alignment
3382  * 
3383  * @cfg {Boolean} main (true|false) main nav bar? default false
3384  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3385  * 
3386  * @cfg {String} tag (header|footer|nav|div) default is nav 
3387
3388  * 
3389  * 
3390  * 
3391  * @constructor
3392  * Create a new Sidebar
3393  * @param {Object} config The config object
3394  */
3395
3396
3397 Roo.bootstrap.NavSimplebar = function(config){
3398     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3399 };
3400
3401 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3402     
3403     inverse: false,
3404     
3405     type: false,
3406     arrangement: '',
3407     align : false,
3408     
3409     
3410     
3411     main : false,
3412     
3413     
3414     tag : false,
3415     
3416     
3417     getAutoCreate : function(){
3418         
3419         
3420         var cfg = {
3421             tag : this.tag || 'div',
3422             cls : 'navbar'
3423         };
3424           
3425         
3426         cfg.cn = [
3427             {
3428                 cls: 'nav',
3429                 tag : 'ul'
3430             }
3431         ];
3432         
3433          
3434         this.type = this.type || 'nav';
3435         if (['tabs','pills'].indexOf(this.type)!==-1) {
3436             cfg.cn[0].cls += ' nav-' + this.type
3437         
3438         
3439         } else {
3440             if (this.type!=='nav') {
3441                 Roo.log('nav type must be nav/tabs/pills')
3442             }
3443             cfg.cn[0].cls += ' navbar-nav'
3444         }
3445         
3446         
3447         
3448         
3449         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3450             cfg.cn[0].cls += ' nav-' + this.arrangement;
3451         }
3452         
3453         
3454         if (this.align === 'right') {
3455             cfg.cn[0].cls += ' navbar-right';
3456         }
3457         
3458         if (this.inverse) {
3459             cfg.cls += ' navbar-inverse';
3460             
3461         }
3462         
3463         
3464         return cfg;
3465     
3466         
3467     }
3468     
3469     
3470     
3471 });
3472
3473
3474
3475  
3476
3477  
3478        /*
3479  * - LGPL
3480  *
3481  * navbar
3482  * 
3483  */
3484
3485 /**
3486  * @class Roo.bootstrap.NavHeaderbar
3487  * @extends Roo.bootstrap.NavSimplebar
3488  * Bootstrap Sidebar class
3489  *
3490  * @cfg {String} brand what is brand
3491  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3492  * @cfg {String} brand_href href of the brand
3493  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3494  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3495  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3496  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3497  * 
3498  * @constructor
3499  * Create a new Sidebar
3500  * @param {Object} config The config object
3501  */
3502
3503
3504 Roo.bootstrap.NavHeaderbar = function(config){
3505     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3506       
3507 };
3508
3509 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3510     
3511     position: '',
3512     brand: '',
3513     brand_href: false,
3514     srButton : true,
3515     autohide : false,
3516     desktopCenter : false,
3517    
3518     
3519     getAutoCreate : function(){
3520         
3521         var   cfg = {
3522             tag: this.nav || 'nav',
3523             cls: 'navbar',
3524             role: 'navigation',
3525             cn: []
3526         };
3527         
3528         var cn = cfg.cn;
3529         if (this.desktopCenter) {
3530             cn.push({cls : 'container', cn : []});
3531             cn = cn[0].cn;
3532         }
3533         
3534         if(this.srButton){
3535             cn.push({
3536                 tag: 'div',
3537                 cls: 'navbar-header',
3538                 cn: [
3539                     {
3540                         tag: 'button',
3541                         type: 'button',
3542                         cls: 'navbar-toggle',
3543                         'data-toggle': 'collapse',
3544                         cn: [
3545                             {
3546                                 tag: 'span',
3547                                 cls: 'sr-only',
3548                                 html: 'Toggle navigation'
3549                             },
3550                             {
3551                                 tag: 'span',
3552                                 cls: 'icon-bar'
3553                             },
3554                             {
3555                                 tag: 'span',
3556                                 cls: 'icon-bar'
3557                             },
3558                             {
3559                                 tag: 'span',
3560                                 cls: 'icon-bar'
3561                             }
3562                         ]
3563                     }
3564                 ]
3565             });
3566         }
3567         
3568         cn.push({
3569             tag: 'div',
3570             cls: 'collapse navbar-collapse',
3571             cn : []
3572         });
3573         
3574         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3575         
3576         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3577             cfg.cls += ' navbar-' + this.position;
3578             
3579             // tag can override this..
3580             
3581             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3582         }
3583         
3584         if (this.brand !== '') {
3585             cn[0].cn.push({
3586                 tag: 'a',
3587                 href: this.brand_href ? this.brand_href : '#',
3588                 cls: 'navbar-brand',
3589                 cn: [
3590                 this.brand
3591                 ]
3592             });
3593         }
3594         
3595         if(this.main){
3596             cfg.cls += ' main-nav';
3597         }
3598         
3599         
3600         return cfg;
3601
3602         
3603     },
3604     getHeaderChildContainer : function()
3605     {
3606         if (this.el.select('.navbar-header').getCount()) {
3607             return this.el.select('.navbar-header',true).first();
3608         }
3609         
3610         return this.getChildContainer();
3611     },
3612     
3613     
3614     initEvents : function()
3615     {
3616         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3617         
3618         if (this.autohide) {
3619             
3620             var prevScroll = 0;
3621             var ft = this.el;
3622             
3623             Roo.get(document).on('scroll',function(e) {
3624                 var ns = Roo.get(document).getScroll().top;
3625                 var os = prevScroll;
3626                 prevScroll = ns;
3627                 
3628                 if(ns > os){
3629                     ft.removeClass('slideDown');
3630                     ft.addClass('slideUp');
3631                     return;
3632                 }
3633                 ft.removeClass('slideUp');
3634                 ft.addClass('slideDown');
3635                  
3636               
3637           },this);
3638         }
3639     }    
3640     
3641 });
3642
3643
3644
3645  
3646
3647  /*
3648  * - LGPL
3649  *
3650  * navbar
3651  * 
3652  */
3653
3654 /**
3655  * @class Roo.bootstrap.NavSidebar
3656  * @extends Roo.bootstrap.Navbar
3657  * Bootstrap Sidebar class
3658  * 
3659  * @constructor
3660  * Create a new Sidebar
3661  * @param {Object} config The config object
3662  */
3663
3664
3665 Roo.bootstrap.NavSidebar = function(config){
3666     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3667 };
3668
3669 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3670     
3671     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3672     
3673     getAutoCreate : function(){
3674         
3675         
3676         return  {
3677             tag: 'div',
3678             cls: 'sidebar sidebar-nav'
3679         };
3680     
3681         
3682     }
3683     
3684     
3685     
3686 });
3687
3688
3689
3690  
3691
3692  /*
3693  * - LGPL
3694  *
3695  * nav group
3696  * 
3697  */
3698
3699 /**
3700  * @class Roo.bootstrap.NavGroup
3701  * @extends Roo.bootstrap.Component
3702  * Bootstrap NavGroup class
3703  * @cfg {String} align (left|right)
3704  * @cfg {Boolean} inverse
3705  * @cfg {String} type (nav|pills|tab) default nav
3706  * @cfg {String} navId - reference Id for navbar.
3707
3708  * 
3709  * @constructor
3710  * Create a new nav group
3711  * @param {Object} config The config object
3712  */
3713
3714 Roo.bootstrap.NavGroup = function(config){
3715     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3716     this.navItems = [];
3717    
3718     Roo.bootstrap.NavGroup.register(this);
3719      this.addEvents({
3720         /**
3721              * @event changed
3722              * Fires when the active item changes
3723              * @param {Roo.bootstrap.NavGroup} this
3724              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3725              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3726          */
3727         'changed': true
3728      });
3729     
3730 };
3731
3732 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3733     
3734     align: '',
3735     inverse: false,
3736     form: false,
3737     type: 'nav',
3738     navId : '',
3739     // private
3740     
3741     navItems : false, 
3742     
3743     getAutoCreate : function()
3744     {
3745         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3746         
3747         cfg = {
3748             tag : 'ul',
3749             cls: 'nav' 
3750         }
3751         
3752         if (['tabs','pills'].indexOf(this.type)!==-1) {
3753             cfg.cls += ' nav-' + this.type
3754         } else {
3755             if (this.type!=='nav') {
3756                 Roo.log('nav type must be nav/tabs/pills')
3757             }
3758             cfg.cls += ' navbar-nav'
3759         }
3760         
3761         if (this.parent().sidebar) {
3762             cfg = {
3763                 tag: 'ul',
3764                 cls: 'dashboard-menu sidebar-menu'
3765             }
3766             
3767             return cfg;
3768         }
3769         
3770         if (this.form === true) {
3771             cfg = {
3772                 tag: 'form',
3773                 cls: 'navbar-form'
3774             }
3775             
3776             if (this.align === 'right') {
3777                 cfg.cls += ' navbar-right';
3778             } else {
3779                 cfg.cls += ' navbar-left';
3780             }
3781         }
3782         
3783         if (this.align === 'right') {
3784             cfg.cls += ' navbar-right';
3785         }
3786         
3787         if (this.inverse) {
3788             cfg.cls += ' navbar-inverse';
3789             
3790         }
3791         
3792         
3793         return cfg;
3794     },
3795     /**
3796     * sets the active Navigation item
3797     * @param {Roo.bootstrap.NavItem} the new current navitem
3798     */
3799     setActiveItem : function(item)
3800     {
3801         var prev = false;
3802         Roo.each(this.navItems, function(v){
3803             if (v == item) {
3804                 return ;
3805             }
3806             if (v.isActive()) {
3807                 v.setActive(false, true);
3808                 prev = v;
3809                 
3810             }
3811             
3812         });
3813
3814         item.setActive(true, true);
3815         this.fireEvent('changed', this, item, prev);
3816         
3817         
3818     },
3819     /**
3820     * gets the active Navigation item
3821     * @return {Roo.bootstrap.NavItem} the current navitem
3822     */
3823     getActive : function()
3824     {
3825         
3826         var prev = false;
3827         Roo.each(this.navItems, function(v){
3828             
3829             if (v.isActive()) {
3830                 prev = v;
3831                 
3832             }
3833             
3834         });
3835         return prev;
3836     },
3837     
3838     indexOfNav : function()
3839     {
3840         
3841         var prev = false;
3842         Roo.each(this.navItems, function(v,i){
3843             
3844             if (v.isActive()) {
3845                 prev = i;
3846                 
3847             }
3848             
3849         });
3850         return prev;
3851     },
3852     /**
3853     * adds a Navigation item
3854     * @param {Roo.bootstrap.NavItem} the navitem to add
3855     */
3856     addItem : function(cfg)
3857     {
3858         var cn = new Roo.bootstrap.NavItem(cfg);
3859         this.register(cn);
3860         cn.parentId = this.id;
3861         cn.onRender(this.el, null);
3862         return cn;
3863     },
3864     /**
3865     * register a Navigation item
3866     * @param {Roo.bootstrap.NavItem} the navitem to add
3867     */
3868     register : function(item)
3869     {
3870         this.navItems.push( item);
3871         item.navId = this.navId;
3872     
3873     },
3874     
3875     /**
3876     * clear all the Navigation item
3877     */
3878    
3879     clearAll : function()
3880     {
3881         this.navItems = [];
3882         this.el.dom.innerHTML = '';
3883     },
3884     
3885     getNavItem: function(tabId)
3886     {
3887         var ret = false;
3888         Roo.each(this.navItems, function(e) {
3889             if (e.tabId == tabId) {
3890                ret =  e;
3891                return false;
3892             }
3893             return true;
3894             
3895         });
3896         return ret;
3897     },
3898     
3899     setActiveNext : function()
3900     {
3901         var i = this.indexOfNav(this.getActive());
3902         if (i > this.navItems.length) {
3903             return;
3904         }
3905         this.setActiveItem(this.navItems[i+1]);
3906     },
3907     setActivePrev : function()
3908     {
3909         var i = this.indexOfNav(this.getActive());
3910         if (i  < 1) {
3911             return;
3912         }
3913         this.setActiveItem(this.navItems[i-1]);
3914     },
3915     clearWasActive : function(except) {
3916         Roo.each(this.navItems, function(e) {
3917             if (e.tabId != except.tabId && e.was_active) {
3918                e.was_active = false;
3919                return false;
3920             }
3921             return true;
3922             
3923         });
3924     },
3925     getWasActive : function ()
3926     {
3927         var r = false;
3928         Roo.each(this.navItems, function(e) {
3929             if (e.was_active) {
3930                r = e;
3931                return false;
3932             }
3933             return true;
3934             
3935         });
3936         return r;
3937     }
3938     
3939     
3940 });
3941
3942  
3943 Roo.apply(Roo.bootstrap.NavGroup, {
3944     
3945     groups: {},
3946      /**
3947     * register a Navigation Group
3948     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3949     */
3950     register : function(navgrp)
3951     {
3952         this.groups[navgrp.navId] = navgrp;
3953         
3954     },
3955     /**
3956     * fetch a Navigation Group based on the navigation ID
3957     * @param {string} the navgroup to add
3958     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3959     */
3960     get: function(navId) {
3961         if (typeof(this.groups[navId]) == 'undefined') {
3962             return false;
3963             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3964         }
3965         return this.groups[navId] ;
3966     }
3967     
3968     
3969     
3970 });
3971
3972  /*
3973  * - LGPL
3974  *
3975  * row
3976  * 
3977  */
3978
3979 /**
3980  * @class Roo.bootstrap.NavItem
3981  * @extends Roo.bootstrap.Component
3982  * Bootstrap Navbar.NavItem class
3983  * @cfg {String} href  link to
3984  * @cfg {String} html content of button
3985  * @cfg {String} badge text inside badge
3986  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3987  * @cfg {String} glyphicon name of glyphicon
3988  * @cfg {String} icon name of font awesome icon
3989  * @cfg {Boolean} active Is item active
3990  * @cfg {Boolean} disabled Is item disabled
3991  
3992  * @cfg {Boolean} preventDefault (true | false) default false
3993  * @cfg {String} tabId the tab that this item activates.
3994  * @cfg {String} tagtype (a|span) render as a href or span?
3995  * @cfg {Boolean} animateRef (true|false) link to element default false  
3996   
3997  * @constructor
3998  * Create a new Navbar Item
3999  * @param {Object} config The config object
4000  */
4001 Roo.bootstrap.NavItem = function(config){
4002     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4003     this.addEvents({
4004         // raw events
4005         /**
4006          * @event click
4007          * The raw click event for the entire grid.
4008          * @param {Roo.EventObject} e
4009          */
4010         "click" : true,
4011          /**
4012             * @event changed
4013             * Fires when the active item active state changes
4014             * @param {Roo.bootstrap.NavItem} this
4015             * @param {boolean} state the new state
4016              
4017          */
4018         'changed': true,
4019         /**
4020             * @event scrollto
4021             * Fires when scroll to element
4022             * @param {Roo.bootstrap.NavItem} this
4023             * @param {Object} options
4024             * @param {Roo.EventObject} e
4025              
4026          */
4027         'scrollto': true
4028     });
4029    
4030 };
4031
4032 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4033     
4034     href: false,
4035     html: '',
4036     badge: '',
4037     icon: false,
4038     glyphicon: false,
4039     active: false,
4040     preventDefault : false,
4041     tabId : false,
4042     tagtype : 'a',
4043     disabled : false,
4044     animateRef : false,
4045     was_active : false,
4046     
4047     getAutoCreate : function(){
4048          
4049         var cfg = {
4050             tag: 'li',
4051             cls: 'nav-item'
4052             
4053         }
4054         if (this.active) {
4055             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4056         }
4057         if (this.disabled) {
4058             cfg.cls += ' disabled';
4059         }
4060         
4061         if (this.href || this.html || this.glyphicon || this.icon) {
4062             cfg.cn = [
4063                 {
4064                     tag: this.tagtype,
4065                     href : this.href || "#",
4066                     html: this.html || ''
4067                 }
4068             ];
4069             
4070             if (this.icon) {
4071                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4072             }
4073
4074             if(this.glyphicon) {
4075                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4076             }
4077             
4078             if (this.menu) {
4079                 
4080                 cfg.cn[0].html += " <span class='caret'></span>";
4081              
4082             }
4083             
4084             if (this.badge !== '') {
4085                  
4086                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4087             }
4088         }
4089         
4090         
4091         
4092         return cfg;
4093     },
4094     initEvents: function() 
4095     {
4096         if (typeof (this.menu) != 'undefined') {
4097             this.menu.parentType = this.xtype;
4098             this.menu.triggerEl = this.el;
4099             this.menu = this.addxtype(Roo.apply({}, this.menu));
4100         }
4101         
4102         this.el.select('a',true).on('click', this.onClick, this);
4103         
4104         if(this.tagtype == 'span'){
4105             this.el.select('span',true).on('click', this.onClick, this);
4106         }
4107        
4108         // at this point parent should be available..
4109         this.parent().register(this);
4110     },
4111     
4112     onClick : function(e)
4113     {
4114         if(
4115                 this.preventDefault || 
4116                 this.href == '#' 
4117         ){
4118             
4119             e.preventDefault();
4120         }
4121         
4122         if (this.disabled) {
4123             return;
4124         }
4125         
4126         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4127         if (tg && tg.transition) {
4128             Roo.log("waiting for the transitionend");
4129             return;
4130         }
4131         
4132         
4133         
4134         //Roo.log("fire event clicked");
4135         if(this.fireEvent('click', this, e) === false){
4136             return;
4137         };
4138         
4139         if(this.tagtype == 'span'){
4140             return;
4141         }
4142         
4143         //Roo.log(this.href);
4144         var ael = this.el.select('a',true).first();
4145         //Roo.log(ael);
4146         
4147         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4148             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4149             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4150                 return; // ignore... - it's a 'hash' to another page.
4151             }
4152             
4153             e.preventDefault();
4154             this.scrollToElement(e);
4155         }
4156         
4157         
4158         var p =  this.parent();
4159    
4160         if (['tabs','pills'].indexOf(p.type)!==-1) {
4161             if (typeof(p.setActiveItem) !== 'undefined') {
4162                 p.setActiveItem(this);
4163             }
4164         }
4165         
4166         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4167         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4168             // remove the collapsed menu expand...
4169             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4170         }
4171     },
4172     
4173     isActive: function () {
4174         return this.active
4175     },
4176     setActive : function(state, fire, is_was_active)
4177     {
4178         if (this.active && !state & this.navId) {
4179             this.was_active = true;
4180             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4181             if (nv) {
4182                 nv.clearWasActive(this);
4183             }
4184             
4185         }
4186         this.active = state;
4187         
4188         if (!state ) {
4189             this.el.removeClass('active');
4190         } else if (!this.el.hasClass('active')) {
4191             this.el.addClass('active');
4192         }
4193         if (fire) {
4194             this.fireEvent('changed', this, state);
4195         }
4196         
4197         // show a panel if it's registered and related..
4198         
4199         if (!this.navId || !this.tabId || !state || is_was_active) {
4200             return;
4201         }
4202         
4203         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4204         if (!tg) {
4205             return;
4206         }
4207         var pan = tg.getPanelByName(this.tabId);
4208         if (!pan) {
4209             return;
4210         }
4211         // if we can not flip to new panel - go back to old nav highlight..
4212         if (false == tg.showPanel(pan)) {
4213             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4214             if (nv) {
4215                 var onav = nv.getWasActive();
4216                 if (onav) {
4217                     onav.setActive(true, false, true);
4218                 }
4219             }
4220             
4221         }
4222         
4223         
4224         
4225     },
4226      // this should not be here...
4227     setDisabled : function(state)
4228     {
4229         this.disabled = state;
4230         if (!state ) {
4231             this.el.removeClass('disabled');
4232         } else if (!this.el.hasClass('disabled')) {
4233             this.el.addClass('disabled');
4234         }
4235         
4236     },
4237     
4238     /**
4239      * Fetch the element to display the tooltip on.
4240      * @return {Roo.Element} defaults to this.el
4241      */
4242     tooltipEl : function()
4243     {
4244         return this.el.select('' + this.tagtype + '', true).first();
4245     },
4246     
4247     scrollToElement : function(e)
4248     {
4249         var c = document.body;
4250         
4251         /*
4252          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4253          */
4254         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4255             c = document.documentElement;
4256         }
4257         
4258         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4259         
4260         if(!target){
4261             return;
4262         }
4263
4264         var o = target.calcOffsetsTo(c);
4265         
4266         var options = {
4267             target : target,
4268             value : o[1]
4269         }
4270         
4271         this.fireEvent('scrollto', this, options, e);
4272         
4273         Roo.get(c).scrollTo('top', options.value, true);
4274         
4275         return;
4276     }
4277 });
4278  
4279
4280  /*
4281  * - LGPL
4282  *
4283  * sidebar item
4284  *
4285  *  li
4286  *    <span> icon </span>
4287  *    <span> text </span>
4288  *    <span>badge </span>
4289  */
4290
4291 /**
4292  * @class Roo.bootstrap.NavSidebarItem
4293  * @extends Roo.bootstrap.NavItem
4294  * Bootstrap Navbar.NavSidebarItem class
4295  * @constructor
4296  * Create a new Navbar Button
4297  * @param {Object} config The config object
4298  */
4299 Roo.bootstrap.NavSidebarItem = function(config){
4300     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4301     this.addEvents({
4302         // raw events
4303         /**
4304          * @event click
4305          * The raw click event for the entire grid.
4306          * @param {Roo.EventObject} e
4307          */
4308         "click" : true,
4309          /**
4310             * @event changed
4311             * Fires when the active item active state changes
4312             * @param {Roo.bootstrap.NavSidebarItem} this
4313             * @param {boolean} state the new state
4314              
4315          */
4316         'changed': true
4317     });
4318    
4319 };
4320
4321 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4322     
4323     
4324     getAutoCreate : function(){
4325         
4326         
4327         var a = {
4328                 tag: 'a',
4329                 href : this.href || '#',
4330                 cls: '',
4331                 html : '',
4332                 cn : []
4333         };
4334         var cfg = {
4335             tag: 'li',
4336             cls: '',
4337             cn: [ a ]
4338         }
4339         var span = {
4340             tag: 'span',
4341             html : this.html || ''
4342         }
4343         
4344         
4345         if (this.active) {
4346             cfg.cls += ' active';
4347         }
4348         
4349         // left icon..
4350         if (this.glyphicon || this.icon) {
4351             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4352             a.cn.push({ tag : 'i', cls : c }) ;
4353         }
4354         // html..
4355         a.cn.push(span);
4356         // then badge..
4357         if (this.badge !== '') {
4358             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
4359         }
4360         // fi
4361         if (this.menu) {
4362             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4363             a.cls += 'dropdown-toggle treeview' ;
4364             
4365         }
4366         
4367         
4368         
4369         return cfg;
4370          
4371            
4372     }
4373    
4374      
4375  
4376 });
4377  
4378
4379  /*
4380  * - LGPL
4381  *
4382  * row
4383  * 
4384  */
4385
4386 /**
4387  * @class Roo.bootstrap.Row
4388  * @extends Roo.bootstrap.Component
4389  * Bootstrap Row class (contains columns...)
4390  * 
4391  * @constructor
4392  * Create a new Row
4393  * @param {Object} config The config object
4394  */
4395
4396 Roo.bootstrap.Row = function(config){
4397     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4398 };
4399
4400 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4401     
4402     getAutoCreate : function(){
4403        return {
4404             cls: 'row clearfix'
4405        };
4406     }
4407     
4408     
4409 });
4410
4411  
4412
4413  /*
4414  * - LGPL
4415  *
4416  * element
4417  * 
4418  */
4419
4420 /**
4421  * @class Roo.bootstrap.Element
4422  * @extends Roo.bootstrap.Component
4423  * Bootstrap Element class
4424  * @cfg {String} html contents of the element
4425  * @cfg {String} tag tag of the element
4426  * @cfg {String} cls class of the element
4427  * @cfg {Boolean} preventDefault (true|false) default false
4428  * @cfg {Boolean} clickable (true|false) default false
4429  * 
4430  * @constructor
4431  * Create a new Element
4432  * @param {Object} config The config object
4433  */
4434
4435 Roo.bootstrap.Element = function(config){
4436     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4437     
4438     this.addEvents({
4439         // raw events
4440         /**
4441          * @event click
4442          * When a element is chick
4443          * @param {Roo.bootstrap.Element} this
4444          * @param {Roo.EventObject} e
4445          */
4446         "click" : true
4447     });
4448 };
4449
4450 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4451     
4452     tag: 'div',
4453     cls: '',
4454     html: '',
4455     preventDefault: false, 
4456     clickable: false,
4457     
4458     getAutoCreate : function(){
4459         
4460         var cfg = {
4461             tag: this.tag,
4462             cls: this.cls,
4463             html: this.html
4464         }
4465         
4466         return cfg;
4467     },
4468     
4469     initEvents: function() 
4470     {
4471         Roo.bootstrap.Element.superclass.initEvents.call(this);
4472         
4473         if(this.clickable){
4474             this.el.on('click', this.onClick, this);
4475         }
4476         
4477     },
4478     
4479     onClick : function(e)
4480     {
4481         if(this.preventDefault){
4482             e.preventDefault();
4483         }
4484         
4485         this.fireEvent('click', this, e);
4486     },
4487     
4488     getValue : function()
4489     {
4490         return this.el.dom.innerHTML;
4491     },
4492     
4493     setValue : function(value)
4494     {
4495         this.el.dom.innerHTML = value;
4496     }
4497    
4498 });
4499
4500  
4501
4502  /*
4503  * - LGPL
4504  *
4505  * pagination
4506  * 
4507  */
4508
4509 /**
4510  * @class Roo.bootstrap.Pagination
4511  * @extends Roo.bootstrap.Component
4512  * Bootstrap Pagination class
4513  * @cfg {String} size xs | sm | md | lg
4514  * @cfg {Boolean} inverse false | true
4515  * 
4516  * @constructor
4517  * Create a new Pagination
4518  * @param {Object} config The config object
4519  */
4520
4521 Roo.bootstrap.Pagination = function(config){
4522     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4523 };
4524
4525 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4526     
4527     cls: false,
4528     size: false,
4529     inverse: false,
4530     
4531     getAutoCreate : function(){
4532         var cfg = {
4533             tag: 'ul',
4534                 cls: 'pagination'
4535         };
4536         if (this.inverse) {
4537             cfg.cls += ' inverse';
4538         }
4539         if (this.html) {
4540             cfg.html=this.html;
4541         }
4542         if (this.cls) {
4543             cfg.cls += " " + this.cls;
4544         }
4545         return cfg;
4546     }
4547    
4548 });
4549
4550  
4551
4552  /*
4553  * - LGPL
4554  *
4555  * Pagination item
4556  * 
4557  */
4558
4559
4560 /**
4561  * @class Roo.bootstrap.PaginationItem
4562  * @extends Roo.bootstrap.Component
4563  * Bootstrap PaginationItem class
4564  * @cfg {String} html text
4565  * @cfg {String} href the link
4566  * @cfg {Boolean} preventDefault (true | false) default true
4567  * @cfg {Boolean} active (true | false) default false
4568  * @cfg {Boolean} disabled default false
4569  * 
4570  * 
4571  * @constructor
4572  * Create a new PaginationItem
4573  * @param {Object} config The config object
4574  */
4575
4576
4577 Roo.bootstrap.PaginationItem = function(config){
4578     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4579     this.addEvents({
4580         // raw events
4581         /**
4582          * @event click
4583          * The raw click event for the entire grid.
4584          * @param {Roo.EventObject} e
4585          */
4586         "click" : true
4587     });
4588 };
4589
4590 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4591     
4592     href : false,
4593     html : false,
4594     preventDefault: true,
4595     active : false,
4596     cls : false,
4597     disabled: false,
4598     
4599     getAutoCreate : function(){
4600         var cfg= {
4601             tag: 'li',
4602             cn: [
4603                 {
4604                     tag : 'a',
4605                     href : this.href ? this.href : '#',
4606                     html : this.html ? this.html : ''
4607                 }
4608             ]
4609         };
4610         
4611         if(this.cls){
4612             cfg.cls = this.cls;
4613         }
4614         
4615         if(this.disabled){
4616             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4617         }
4618         
4619         if(this.active){
4620             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4621         }
4622         
4623         return cfg;
4624     },
4625     
4626     initEvents: function() {
4627         
4628         this.el.on('click', this.onClick, this);
4629         
4630     },
4631     onClick : function(e)
4632     {
4633         Roo.log('PaginationItem on click ');
4634         if(this.preventDefault){
4635             e.preventDefault();
4636         }
4637         
4638         if(this.disabled){
4639             return;
4640         }
4641         
4642         this.fireEvent('click', this, e);
4643     }
4644    
4645 });
4646
4647  
4648
4649  /*
4650  * - LGPL
4651  *
4652  * slider
4653  * 
4654  */
4655
4656
4657 /**
4658  * @class Roo.bootstrap.Slider
4659  * @extends Roo.bootstrap.Component
4660  * Bootstrap Slider class
4661  *    
4662  * @constructor
4663  * Create a new Slider
4664  * @param {Object} config The config object
4665  */
4666
4667 Roo.bootstrap.Slider = function(config){
4668     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4669 };
4670
4671 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4672     
4673     getAutoCreate : function(){
4674         
4675         var cfg = {
4676             tag: 'div',
4677             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4678             cn: [
4679                 {
4680                     tag: 'a',
4681                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4682                 }
4683             ]
4684         }
4685         
4686         return cfg;
4687     }
4688    
4689 });
4690
4691  /*
4692  * Based on:
4693  * Ext JS Library 1.1.1
4694  * Copyright(c) 2006-2007, Ext JS, LLC.
4695  *
4696  * Originally Released Under LGPL - original licence link has changed is not relivant.
4697  *
4698  * Fork - LGPL
4699  * <script type="text/javascript">
4700  */
4701  
4702
4703 /**
4704  * @class Roo.grid.ColumnModel
4705  * @extends Roo.util.Observable
4706  * This is the default implementation of a ColumnModel used by the Grid. It defines
4707  * the columns in the grid.
4708  * <br>Usage:<br>
4709  <pre><code>
4710  var colModel = new Roo.grid.ColumnModel([
4711         {header: "Ticker", width: 60, sortable: true, locked: true},
4712         {header: "Company Name", width: 150, sortable: true},
4713         {header: "Market Cap.", width: 100, sortable: true},
4714         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4715         {header: "Employees", width: 100, sortable: true, resizable: false}
4716  ]);
4717  </code></pre>
4718  * <p>
4719  
4720  * The config options listed for this class are options which may appear in each
4721  * individual column definition.
4722  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4723  * @constructor
4724  * @param {Object} config An Array of column config objects. See this class's
4725  * config objects for details.
4726 */
4727 Roo.grid.ColumnModel = function(config){
4728         /**
4729      * The config passed into the constructor
4730      */
4731     this.config = config;
4732     this.lookup = {};
4733
4734     // if no id, create one
4735     // if the column does not have a dataIndex mapping,
4736     // map it to the order it is in the config
4737     for(var i = 0, len = config.length; i < len; i++){
4738         var c = config[i];
4739         if(typeof c.dataIndex == "undefined"){
4740             c.dataIndex = i;
4741         }
4742         if(typeof c.renderer == "string"){
4743             c.renderer = Roo.util.Format[c.renderer];
4744         }
4745         if(typeof c.id == "undefined"){
4746             c.id = Roo.id();
4747         }
4748         if(c.editor && c.editor.xtype){
4749             c.editor  = Roo.factory(c.editor, Roo.grid);
4750         }
4751         if(c.editor && c.editor.isFormField){
4752             c.editor = new Roo.grid.GridEditor(c.editor);
4753         }
4754         this.lookup[c.id] = c;
4755     }
4756
4757     /**
4758      * The width of columns which have no width specified (defaults to 100)
4759      * @type Number
4760      */
4761     this.defaultWidth = 100;
4762
4763     /**
4764      * Default sortable of columns which have no sortable specified (defaults to false)
4765      * @type Boolean
4766      */
4767     this.defaultSortable = false;
4768
4769     this.addEvents({
4770         /**
4771              * @event widthchange
4772              * Fires when the width of a column changes.
4773              * @param {ColumnModel} this
4774              * @param {Number} columnIndex The column index
4775              * @param {Number} newWidth The new width
4776              */
4777             "widthchange": true,
4778         /**
4779              * @event headerchange
4780              * Fires when the text of a header changes.
4781              * @param {ColumnModel} this
4782              * @param {Number} columnIndex The column index
4783              * @param {Number} newText The new header text
4784              */
4785             "headerchange": true,
4786         /**
4787              * @event hiddenchange
4788              * Fires when a column is hidden or "unhidden".
4789              * @param {ColumnModel} this
4790              * @param {Number} columnIndex The column index
4791              * @param {Boolean} hidden true if hidden, false otherwise
4792              */
4793             "hiddenchange": true,
4794             /**
4795          * @event columnmoved
4796          * Fires when a column is moved.
4797          * @param {ColumnModel} this
4798          * @param {Number} oldIndex
4799          * @param {Number} newIndex
4800          */
4801         "columnmoved" : true,
4802         /**
4803          * @event columlockchange
4804          * Fires when a column's locked state is changed
4805          * @param {ColumnModel} this
4806          * @param {Number} colIndex
4807          * @param {Boolean} locked true if locked
4808          */
4809         "columnlockchange" : true
4810     });
4811     Roo.grid.ColumnModel.superclass.constructor.call(this);
4812 };
4813 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4814     /**
4815      * @cfg {String} header The header text to display in the Grid view.
4816      */
4817     /**
4818      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4819      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4820      * specified, the column's index is used as an index into the Record's data Array.
4821      */
4822     /**
4823      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4824      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4825      */
4826     /**
4827      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4828      * Defaults to the value of the {@link #defaultSortable} property.
4829      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4830      */
4831     /**
4832      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4833      */
4834     /**
4835      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4836      */
4837     /**
4838      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4839      */
4840     /**
4841      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4842      */
4843     /**
4844      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4845      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4846      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4847      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4848      */
4849        /**
4850      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4851      */
4852     /**
4853      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4854      */
4855     /**
4856      * @cfg {String} cursor (Optional)
4857      */
4858     /**
4859      * @cfg {String} tooltip (Optional)
4860      */
4861     /**
4862      * Returns the id of the column at the specified index.
4863      * @param {Number} index The column index
4864      * @return {String} the id
4865      */
4866     getColumnId : function(index){
4867         return this.config[index].id;
4868     },
4869
4870     /**
4871      * Returns the column for a specified id.
4872      * @param {String} id The column id
4873      * @return {Object} the column
4874      */
4875     getColumnById : function(id){
4876         return this.lookup[id];
4877     },
4878
4879     
4880     /**
4881      * Returns the column for a specified dataIndex.
4882      * @param {String} dataIndex The column dataIndex
4883      * @return {Object|Boolean} the column or false if not found
4884      */
4885     getColumnByDataIndex: function(dataIndex){
4886         var index = this.findColumnIndex(dataIndex);
4887         return index > -1 ? this.config[index] : false;
4888     },
4889     
4890     /**
4891      * Returns the index for a specified column id.
4892      * @param {String} id The column id
4893      * @return {Number} the index, or -1 if not found
4894      */
4895     getIndexById : function(id){
4896         for(var i = 0, len = this.config.length; i < len; i++){
4897             if(this.config[i].id == id){
4898                 return i;
4899             }
4900         }
4901         return -1;
4902     },
4903     
4904     /**
4905      * Returns the index for a specified column dataIndex.
4906      * @param {String} dataIndex The column dataIndex
4907      * @return {Number} the index, or -1 if not found
4908      */
4909     
4910     findColumnIndex : function(dataIndex){
4911         for(var i = 0, len = this.config.length; i < len; i++){
4912             if(this.config[i].dataIndex == dataIndex){
4913                 return i;
4914             }
4915         }
4916         return -1;
4917     },
4918     
4919     
4920     moveColumn : function(oldIndex, newIndex){
4921         var c = this.config[oldIndex];
4922         this.config.splice(oldIndex, 1);
4923         this.config.splice(newIndex, 0, c);
4924         this.dataMap = null;
4925         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4926     },
4927
4928     isLocked : function(colIndex){
4929         return this.config[colIndex].locked === true;
4930     },
4931
4932     setLocked : function(colIndex, value, suppressEvent){
4933         if(this.isLocked(colIndex) == value){
4934             return;
4935         }
4936         this.config[colIndex].locked = value;
4937         if(!suppressEvent){
4938             this.fireEvent("columnlockchange", this, colIndex, value);
4939         }
4940     },
4941
4942     getTotalLockedWidth : function(){
4943         var totalWidth = 0;
4944         for(var i = 0; i < this.config.length; i++){
4945             if(this.isLocked(i) && !this.isHidden(i)){
4946                 this.totalWidth += this.getColumnWidth(i);
4947             }
4948         }
4949         return totalWidth;
4950     },
4951
4952     getLockedCount : function(){
4953         for(var i = 0, len = this.config.length; i < len; i++){
4954             if(!this.isLocked(i)){
4955                 return i;
4956             }
4957         }
4958     },
4959
4960     /**
4961      * Returns the number of columns.
4962      * @return {Number}
4963      */
4964     getColumnCount : function(visibleOnly){
4965         if(visibleOnly === true){
4966             var c = 0;
4967             for(var i = 0, len = this.config.length; i < len; i++){
4968                 if(!this.isHidden(i)){
4969                     c++;
4970                 }
4971             }
4972             return c;
4973         }
4974         return this.config.length;
4975     },
4976
4977     /**
4978      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4979      * @param {Function} fn
4980      * @param {Object} scope (optional)
4981      * @return {Array} result
4982      */
4983     getColumnsBy : function(fn, scope){
4984         var r = [];
4985         for(var i = 0, len = this.config.length; i < len; i++){
4986             var c = this.config[i];
4987             if(fn.call(scope||this, c, i) === true){
4988                 r[r.length] = c;
4989             }
4990         }
4991         return r;
4992     },
4993
4994     /**
4995      * Returns true if the specified column is sortable.
4996      * @param {Number} col The column index
4997      * @return {Boolean}
4998      */
4999     isSortable : function(col){
5000         if(typeof this.config[col].sortable == "undefined"){
5001             return this.defaultSortable;
5002         }
5003         return this.config[col].sortable;
5004     },
5005
5006     /**
5007      * Returns the rendering (formatting) function defined for the column.
5008      * @param {Number} col The column index.
5009      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5010      */
5011     getRenderer : function(col){
5012         if(!this.config[col].renderer){
5013             return Roo.grid.ColumnModel.defaultRenderer;
5014         }
5015         return this.config[col].renderer;
5016     },
5017
5018     /**
5019      * Sets the rendering (formatting) function for a column.
5020      * @param {Number} col The column index
5021      * @param {Function} fn The function to use to process the cell's raw data
5022      * to return HTML markup for the grid view. The render function is called with
5023      * the following parameters:<ul>
5024      * <li>Data value.</li>
5025      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5026      * <li>css A CSS style string to apply to the table cell.</li>
5027      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5028      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5029      * <li>Row index</li>
5030      * <li>Column index</li>
5031      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5032      */
5033     setRenderer : function(col, fn){
5034         this.config[col].renderer = fn;
5035     },
5036
5037     /**
5038      * Returns the width for the specified column.
5039      * @param {Number} col The column index
5040      * @return {Number}
5041      */
5042     getColumnWidth : function(col){
5043         return this.config[col].width * 1 || this.defaultWidth;
5044     },
5045
5046     /**
5047      * Sets the width for a column.
5048      * @param {Number} col The column index
5049      * @param {Number} width The new width
5050      */
5051     setColumnWidth : function(col, width, suppressEvent){
5052         this.config[col].width = width;
5053         this.totalWidth = null;
5054         if(!suppressEvent){
5055              this.fireEvent("widthchange", this, col, width);
5056         }
5057     },
5058
5059     /**
5060      * Returns the total width of all columns.
5061      * @param {Boolean} includeHidden True to include hidden column widths
5062      * @return {Number}
5063      */
5064     getTotalWidth : function(includeHidden){
5065         if(!this.totalWidth){
5066             this.totalWidth = 0;
5067             for(var i = 0, len = this.config.length; i < len; i++){
5068                 if(includeHidden || !this.isHidden(i)){
5069                     this.totalWidth += this.getColumnWidth(i);
5070                 }
5071             }
5072         }
5073         return this.totalWidth;
5074     },
5075
5076     /**
5077      * Returns the header for the specified column.
5078      * @param {Number} col The column index
5079      * @return {String}
5080      */
5081     getColumnHeader : function(col){
5082         return this.config[col].header;
5083     },
5084
5085     /**
5086      * Sets the header for a column.
5087      * @param {Number} col The column index
5088      * @param {String} header The new header
5089      */
5090     setColumnHeader : function(col, header){
5091         this.config[col].header = header;
5092         this.fireEvent("headerchange", this, col, header);
5093     },
5094
5095     /**
5096      * Returns the tooltip for the specified column.
5097      * @param {Number} col The column index
5098      * @return {String}
5099      */
5100     getColumnTooltip : function(col){
5101             return this.config[col].tooltip;
5102     },
5103     /**
5104      * Sets the tooltip for a column.
5105      * @param {Number} col The column index
5106      * @param {String} tooltip The new tooltip
5107      */
5108     setColumnTooltip : function(col, tooltip){
5109             this.config[col].tooltip = tooltip;
5110     },
5111
5112     /**
5113      * Returns the dataIndex for the specified column.
5114      * @param {Number} col The column index
5115      * @return {Number}
5116      */
5117     getDataIndex : function(col){
5118         return this.config[col].dataIndex;
5119     },
5120
5121     /**
5122      * Sets the dataIndex for a column.
5123      * @param {Number} col The column index
5124      * @param {Number} dataIndex The new dataIndex
5125      */
5126     setDataIndex : function(col, dataIndex){
5127         this.config[col].dataIndex = dataIndex;
5128     },
5129
5130     
5131     
5132     /**
5133      * Returns true if the cell is editable.
5134      * @param {Number} colIndex The column index
5135      * @param {Number} rowIndex The row index
5136      * @return {Boolean}
5137      */
5138     isCellEditable : function(colIndex, rowIndex){
5139         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5140     },
5141
5142     /**
5143      * Returns the editor defined for the cell/column.
5144      * return false or null to disable editing.
5145      * @param {Number} colIndex The column index
5146      * @param {Number} rowIndex The row index
5147      * @return {Object}
5148      */
5149     getCellEditor : function(colIndex, rowIndex){
5150         return this.config[colIndex].editor;
5151     },
5152
5153     /**
5154      * Sets if a column is editable.
5155      * @param {Number} col The column index
5156      * @param {Boolean} editable True if the column is editable
5157      */
5158     setEditable : function(col, editable){
5159         this.config[col].editable = editable;
5160     },
5161
5162
5163     /**
5164      * Returns true if the column is hidden.
5165      * @param {Number} colIndex The column index
5166      * @return {Boolean}
5167      */
5168     isHidden : function(colIndex){
5169         return this.config[colIndex].hidden;
5170     },
5171
5172
5173     /**
5174      * Returns true if the column width cannot be changed
5175      */
5176     isFixed : function(colIndex){
5177         return this.config[colIndex].fixed;
5178     },
5179
5180     /**
5181      * Returns true if the column can be resized
5182      * @return {Boolean}
5183      */
5184     isResizable : function(colIndex){
5185         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5186     },
5187     /**
5188      * Sets if a column is hidden.
5189      * @param {Number} colIndex The column index
5190      * @param {Boolean} hidden True if the column is hidden
5191      */
5192     setHidden : function(colIndex, hidden){
5193         this.config[colIndex].hidden = hidden;
5194         this.totalWidth = null;
5195         this.fireEvent("hiddenchange", this, colIndex, hidden);
5196     },
5197
5198     /**
5199      * Sets the editor for a column.
5200      * @param {Number} col The column index
5201      * @param {Object} editor The editor object
5202      */
5203     setEditor : function(col, editor){
5204         this.config[col].editor = editor;
5205     }
5206 });
5207
5208 Roo.grid.ColumnModel.defaultRenderer = function(value){
5209         if(typeof value == "string" && value.length < 1){
5210             return "&#160;";
5211         }
5212         return value;
5213 };
5214
5215 // Alias for backwards compatibility
5216 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5217 /*
5218  * Based on:
5219  * Ext JS Library 1.1.1
5220  * Copyright(c) 2006-2007, Ext JS, LLC.
5221  *
5222  * Originally Released Under LGPL - original licence link has changed is not relivant.
5223  *
5224  * Fork - LGPL
5225  * <script type="text/javascript">
5226  */
5227  
5228 /**
5229  * @class Roo.LoadMask
5230  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5231  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5232  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5233  * element's UpdateManager load indicator and will be destroyed after the initial load.
5234  * @constructor
5235  * Create a new LoadMask
5236  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5237  * @param {Object} config The config object
5238  */
5239 Roo.LoadMask = function(el, config){
5240     this.el = Roo.get(el);
5241     Roo.apply(this, config);
5242     if(this.store){
5243         this.store.on('beforeload', this.onBeforeLoad, this);
5244         this.store.on('load', this.onLoad, this);
5245         this.store.on('loadexception', this.onLoadException, this);
5246         this.removeMask = false;
5247     }else{
5248         var um = this.el.getUpdateManager();
5249         um.showLoadIndicator = false; // disable the default indicator
5250         um.on('beforeupdate', this.onBeforeLoad, this);
5251         um.on('update', this.onLoad, this);
5252         um.on('failure', this.onLoad, this);
5253         this.removeMask = true;
5254     }
5255 };
5256
5257 Roo.LoadMask.prototype = {
5258     /**
5259      * @cfg {Boolean} removeMask
5260      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5261      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5262      */
5263     /**
5264      * @cfg {String} msg
5265      * The text to display in a centered loading message box (defaults to 'Loading...')
5266      */
5267     msg : 'Loading...',
5268     /**
5269      * @cfg {String} msgCls
5270      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5271      */
5272     msgCls : 'x-mask-loading',
5273
5274     /**
5275      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5276      * @type Boolean
5277      */
5278     disabled: false,
5279
5280     /**
5281      * Disables the mask to prevent it from being displayed
5282      */
5283     disable : function(){
5284        this.disabled = true;
5285     },
5286
5287     /**
5288      * Enables the mask so that it can be displayed
5289      */
5290     enable : function(){
5291         this.disabled = false;
5292     },
5293     
5294     onLoadException : function()
5295     {
5296         Roo.log(arguments);
5297         
5298         if (typeof(arguments[3]) != 'undefined') {
5299             Roo.MessageBox.alert("Error loading",arguments[3]);
5300         } 
5301         /*
5302         try {
5303             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5304                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5305             }   
5306         } catch(e) {
5307             
5308         }
5309         */
5310     
5311         
5312         
5313         this.el.unmask(this.removeMask);
5314     },
5315     // private
5316     onLoad : function()
5317     {
5318         this.el.unmask(this.removeMask);
5319     },
5320
5321     // private
5322     onBeforeLoad : function(){
5323         if(!this.disabled){
5324             this.el.mask(this.msg, this.msgCls);
5325         }
5326     },
5327
5328     // private
5329     destroy : function(){
5330         if(this.store){
5331             this.store.un('beforeload', this.onBeforeLoad, this);
5332             this.store.un('load', this.onLoad, this);
5333             this.store.un('loadexception', this.onLoadException, this);
5334         }else{
5335             var um = this.el.getUpdateManager();
5336             um.un('beforeupdate', this.onBeforeLoad, this);
5337             um.un('update', this.onLoad, this);
5338             um.un('failure', this.onLoad, this);
5339         }
5340     }
5341 };/*
5342  * - LGPL
5343  *
5344  * table
5345  * 
5346  */
5347
5348 /**
5349  * @class Roo.bootstrap.Table
5350  * @extends Roo.bootstrap.Component
5351  * Bootstrap Table class
5352  * @cfg {String} cls table class
5353  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5354  * @cfg {String} bgcolor Specifies the background color for a table
5355  * @cfg {Number} border Specifies whether the table cells should have borders or not
5356  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5357  * @cfg {Number} cellspacing Specifies the space between cells
5358  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5359  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5360  * @cfg {String} sortable Specifies that the table should be sortable
5361  * @cfg {String} summary Specifies a summary of the content of a table
5362  * @cfg {Number} width Specifies the width of a table
5363  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5364  * 
5365  * @cfg {boolean} striped Should the rows be alternative striped
5366  * @cfg {boolean} bordered Add borders to the table
5367  * @cfg {boolean} hover Add hover highlighting
5368  * @cfg {boolean} condensed Format condensed
5369  * @cfg {boolean} responsive Format condensed
5370  * @cfg {Boolean} loadMask (true|false) default false
5371  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
5372  * @cfg {Boolean} thead (true|false) generate thead, default true
5373  * @cfg {Boolean} RowSelection (true|false) default false
5374  * @cfg {Boolean} CellSelection (true|false) default false
5375  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5376  
5377  * 
5378  * @constructor
5379  * Create a new Table
5380  * @param {Object} config The config object
5381  */
5382
5383 Roo.bootstrap.Table = function(config){
5384     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5385     
5386     if (this.sm) {
5387         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5388         this.sm = this.selModel;
5389         this.sm.xmodule = this.xmodule || false;
5390     }
5391     if (this.cm && typeof(this.cm.config) == 'undefined') {
5392         this.colModel = new Roo.grid.ColumnModel(this.cm);
5393         this.cm = this.colModel;
5394         this.cm.xmodule = this.xmodule || false;
5395     }
5396     if (this.store) {
5397         this.store= Roo.factory(this.store, Roo.data);
5398         this.ds = this.store;
5399         this.ds.xmodule = this.xmodule || false;
5400          
5401     }
5402     if (this.footer && this.store) {
5403         this.footer.dataSource = this.ds;
5404         this.footer = Roo.factory(this.footer);
5405     }
5406     
5407     /** @private */
5408     this.addEvents({
5409         /**
5410          * @event cellclick
5411          * Fires when a cell is clicked
5412          * @param {Roo.bootstrap.Table} this
5413          * @param {Roo.Element} el
5414          * @param {Number} rowIndex
5415          * @param {Number} columnIndex
5416          * @param {Roo.EventObject} e
5417          */
5418         "cellclick" : true,
5419         /**
5420          * @event celldblclick
5421          * Fires when a cell is double clicked
5422          * @param {Roo.bootstrap.Table} this
5423          * @param {Roo.Element} el
5424          * @param {Number} rowIndex
5425          * @param {Number} columnIndex
5426          * @param {Roo.EventObject} e
5427          */
5428         "celldblclick" : true,
5429         /**
5430          * @event rowclick
5431          * Fires when a row is clicked
5432          * @param {Roo.bootstrap.Table} this
5433          * @param {Roo.Element} el
5434          * @param {Number} rowIndex
5435          * @param {Roo.EventObject} e
5436          */
5437         "rowclick" : true,
5438         /**
5439          * @event rowdblclick
5440          * Fires when a row is double clicked
5441          * @param {Roo.bootstrap.Table} this
5442          * @param {Roo.Element} el
5443          * @param {Number} rowIndex
5444          * @param {Roo.EventObject} e
5445          */
5446         "rowdblclick" : true,
5447         /**
5448          * @event mouseover
5449          * Fires when a mouseover occur
5450          * @param {Roo.bootstrap.Table} this
5451          * @param {Roo.Element} el
5452          * @param {Number} rowIndex
5453          * @param {Number} columnIndex
5454          * @param {Roo.EventObject} e
5455          */
5456         "mouseover" : true,
5457         /**
5458          * @event mouseout
5459          * Fires when a mouseout occur
5460          * @param {Roo.bootstrap.Table} this
5461          * @param {Roo.Element} el
5462          * @param {Number} rowIndex
5463          * @param {Number} columnIndex
5464          * @param {Roo.EventObject} e
5465          */
5466         "mouseout" : true,
5467         /**
5468          * @event rowclass
5469          * Fires when a row is rendered, so you can change add a style to it.
5470          * @param {Roo.bootstrap.Table} this
5471          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5472          */
5473         'rowclass' : true,
5474           /**
5475          * @event rowsrendered
5476          * Fires when all the  rows have been rendered
5477          * @param {Roo.bootstrap.Table} this
5478          */
5479         'rowsrendered' : true
5480         
5481     });
5482 };
5483
5484 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5485     
5486     cls: false,
5487     align: false,
5488     bgcolor: false,
5489     border: false,
5490     cellpadding: false,
5491     cellspacing: false,
5492     frame: false,
5493     rules: false,
5494     sortable: false,
5495     summary: false,
5496     width: false,
5497     striped : false,
5498     bordered: false,
5499     hover:  false,
5500     condensed : false,
5501     responsive : false,
5502     sm : false,
5503     cm : false,
5504     store : false,
5505     loadMask : false,
5506     tfoot : true,
5507     thead : true,
5508     RowSelection : false,
5509     CellSelection : false,
5510     layout : false,
5511     
5512     // Roo.Element - the tbody
5513     mainBody: false, 
5514     
5515     getAutoCreate : function(){
5516         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5517         
5518         cfg = {
5519             tag: 'table',
5520             cls : 'table',
5521             cn : []
5522         }
5523             
5524         if (this.striped) {
5525             cfg.cls += ' table-striped';
5526         }
5527         
5528         if (this.hover) {
5529             cfg.cls += ' table-hover';
5530         }
5531         if (this.bordered) {
5532             cfg.cls += ' table-bordered';
5533         }
5534         if (this.condensed) {
5535             cfg.cls += ' table-condensed';
5536         }
5537         if (this.responsive) {
5538             cfg.cls += ' table-responsive';
5539         }
5540         
5541         if (this.cls) {
5542             cfg.cls+=  ' ' +this.cls;
5543         }
5544         
5545         // this lot should be simplifed...
5546         
5547         if (this.align) {
5548             cfg.align=this.align;
5549         }
5550         if (this.bgcolor) {
5551             cfg.bgcolor=this.bgcolor;
5552         }
5553         if (this.border) {
5554             cfg.border=this.border;
5555         }
5556         if (this.cellpadding) {
5557             cfg.cellpadding=this.cellpadding;
5558         }
5559         if (this.cellspacing) {
5560             cfg.cellspacing=this.cellspacing;
5561         }
5562         if (this.frame) {
5563             cfg.frame=this.frame;
5564         }
5565         if (this.rules) {
5566             cfg.rules=this.rules;
5567         }
5568         if (this.sortable) {
5569             cfg.sortable=this.sortable;
5570         }
5571         if (this.summary) {
5572             cfg.summary=this.summary;
5573         }
5574         if (this.width) {
5575             cfg.width=this.width;
5576         }
5577         if (this.layout) {
5578             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5579         }
5580         
5581         if(this.store || this.cm){
5582             if(this.thead){
5583                 cfg.cn.push(this.renderHeader());
5584             }
5585             
5586             cfg.cn.push(this.renderBody());
5587             
5588             if(this.tfoot){
5589                 cfg.cn.push(this.renderFooter());
5590             }
5591             
5592             cfg.cls+=  ' TableGrid';
5593         }
5594         
5595         return { cn : [ cfg ] };
5596     },
5597     
5598     initEvents : function()
5599     {   
5600         if(!this.store || !this.cm){
5601             return;
5602         }
5603         
5604         //Roo.log('initEvents with ds!!!!');
5605         
5606         this.mainBody = this.el.select('tbody', true).first();
5607         
5608         
5609         var _this = this;
5610         
5611         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5612             e.on('click', _this.sort, _this);
5613         });
5614         
5615         this.el.on("click", this.onClick, this);
5616         this.el.on("dblclick", this.onDblClick, this);
5617         
5618         // why is this done????? = it breaks dialogs??
5619         //this.parent().el.setStyle('position', 'relative');
5620         
5621         
5622         if (this.footer) {
5623             this.footer.parentId = this.id;
5624             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5625         }
5626         
5627         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5628         
5629         this.store.on('load', this.onLoad, this);
5630         this.store.on('beforeload', this.onBeforeLoad, this);
5631         this.store.on('update', this.onUpdate, this);
5632         this.store.on('add', this.onAdd, this);
5633         
5634     },
5635     
5636     onMouseover : function(e, el)
5637     {
5638         var cell = Roo.get(el);
5639         
5640         if(!cell){
5641             return;
5642         }
5643         
5644         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5645             cell = cell.findParent('td', false, true);
5646         }
5647         
5648         var row = cell.findParent('tr', false, true);
5649         var cellIndex = cell.dom.cellIndex;
5650         var rowIndex = row.dom.rowIndex - 1; // start from 0
5651         
5652         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5653         
5654     },
5655     
5656     onMouseout : function(e, el)
5657     {
5658         var cell = Roo.get(el);
5659         
5660         if(!cell){
5661             return;
5662         }
5663         
5664         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5665             cell = cell.findParent('td', false, true);
5666         }
5667         
5668         var row = cell.findParent('tr', false, true);
5669         var cellIndex = cell.dom.cellIndex;
5670         var rowIndex = row.dom.rowIndex - 1; // start from 0
5671         
5672         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5673         
5674     },
5675     
5676     onClick : function(e, el)
5677     {
5678         var cell = Roo.get(el);
5679         
5680         if(!cell || (!this.CellSelection && !this.RowSelection)){
5681             return;
5682         }
5683         
5684         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5685             cell = cell.findParent('td', false, true);
5686         }
5687         
5688         if(!cell || typeof(cell) == 'undefined'){
5689             return;
5690         }
5691         
5692         var row = cell.findParent('tr', false, true);
5693         
5694         if(!row || typeof(row) == 'undefined'){
5695             return;
5696         }
5697         
5698         var cellIndex = cell.dom.cellIndex;
5699         var rowIndex = this.getRowIndex(row);
5700         
5701         if(this.CellSelection){
5702             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5703         }
5704         
5705         if(this.RowSelection){
5706             this.fireEvent('rowclick', this, row, rowIndex, e);
5707         }
5708         
5709         
5710     },
5711     
5712     onDblClick : function(e,el)
5713     {
5714         var cell = Roo.get(el);
5715         
5716         if(!cell || (!this.CellSelection && !this.RowSelection)){
5717             return;
5718         }
5719         
5720         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5721             cell = cell.findParent('td', false, true);
5722         }
5723         
5724         if(!cell || typeof(cell) == 'undefined'){
5725             return;
5726         }
5727         
5728         var row = cell.findParent('tr', false, true);
5729         
5730         if(!row || typeof(row) == 'undefined'){
5731             return;
5732         }
5733         
5734         var cellIndex = cell.dom.cellIndex;
5735         var rowIndex = this.getRowIndex(row);
5736         
5737         if(this.CellSelection){
5738             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5739         }
5740         
5741         if(this.RowSelection){
5742             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5743         }
5744     },
5745     
5746     sort : function(e,el)
5747     {
5748         var col = Roo.get(el);
5749         
5750         if(!col.hasClass('sortable')){
5751             return;
5752         }
5753         
5754         var sort = col.attr('sort');
5755         var dir = 'ASC';
5756         
5757         if(col.hasClass('glyphicon-arrow-up')){
5758             dir = 'DESC';
5759         }
5760         
5761         this.store.sortInfo = {field : sort, direction : dir};
5762         
5763         if (this.footer) {
5764             Roo.log("calling footer first");
5765             this.footer.onClick('first');
5766         } else {
5767         
5768             this.store.load({ params : { start : 0 } });
5769         }
5770     },
5771     
5772     renderHeader : function()
5773     {
5774         var header = {
5775             tag: 'thead',
5776             cn : []
5777         };
5778         
5779         var cm = this.cm;
5780         
5781         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5782             
5783             var config = cm.config[i];
5784                     
5785             var c = {
5786                 tag: 'th',
5787                 style : '',
5788                 html: cm.getColumnHeader(i)
5789             };
5790             
5791             if(typeof(config.tooltip) != 'undefined'){
5792                 c.tooltip = config.tooltip;
5793             }
5794             
5795             if(typeof(config.colspan) != 'undefined'){
5796                 c.colspan = config.colspan;
5797             }
5798             
5799             if(typeof(config.hidden) != 'undefined' && config.hidden){
5800                 c.style += ' display:none;';
5801             }
5802             
5803             if(typeof(config.dataIndex) != 'undefined'){
5804                 c.sort = config.dataIndex;
5805             }
5806             
5807             if(typeof(config.sortable) != 'undefined' && config.sortable){
5808                 c.cls = 'sortable';
5809             }
5810             
5811             if(typeof(config.align) != 'undefined' && config.align.length){
5812                 c.style += ' text-align:' + config.align + ';';
5813             }
5814             
5815             if(typeof(config.width) != 'undefined'){
5816                 c.style += ' width:' + config.width + 'px;';
5817             }
5818             
5819             if(typeof(config.cls) != 'undefined'){
5820                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
5821             }
5822             
5823             header.cn.push(c)
5824         }
5825         
5826         return header;
5827     },
5828     
5829     renderBody : function()
5830     {
5831         var body = {
5832             tag: 'tbody',
5833             cn : [
5834                 {
5835                     tag: 'tr',
5836                     cn : [
5837                         {
5838                             tag : 'td',
5839                             colspan :  this.cm.getColumnCount()
5840                         }
5841                     ]
5842                 }
5843             ]
5844         };
5845         
5846         return body;
5847     },
5848     
5849     renderFooter : function()
5850     {
5851         var footer = {
5852             tag: 'tfoot',
5853             cn : [
5854                 {
5855                     tag: 'tr',
5856                     cn : [
5857                         {
5858                             tag : 'td',
5859                             colspan :  this.cm.getColumnCount()
5860                         }
5861                     ]
5862                 }
5863             ]
5864         };
5865         
5866         return footer;
5867     },
5868     
5869     
5870     
5871     onLoad : function()
5872     {
5873         Roo.log('ds onload');
5874         this.clear();
5875         
5876         var _this = this;
5877         var cm = this.cm;
5878         var ds = this.store;
5879         
5880         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5881             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5882             
5883             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5884                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5885             }
5886             
5887             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5888                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5889             }
5890         });
5891         
5892         var tbody =  this.mainBody;
5893               
5894         if(ds.getCount() > 0){
5895             ds.data.each(function(d,rowIndex){
5896                 var row =  this.renderRow(cm, ds, rowIndex);
5897                 
5898                 tbody.createChild(row);
5899                 
5900                 var _this = this;
5901                 
5902                 if(row.cellObjects.length){
5903                     Roo.each(row.cellObjects, function(r){
5904                         _this.renderCellObject(r);
5905                     })
5906                 }
5907                 
5908             }, this);
5909         }
5910         
5911         Roo.each(this.el.select('tbody td', true).elements, function(e){
5912             e.on('mouseover', _this.onMouseover, _this);
5913         });
5914         
5915         Roo.each(this.el.select('tbody td', true).elements, function(e){
5916             e.on('mouseout', _this.onMouseout, _this);
5917         });
5918         this.fireEvent('rowsrendered', this);
5919         //if(this.loadMask){
5920         //    this.maskEl.hide();
5921         //}
5922     },
5923     
5924     
5925     onUpdate : function(ds,record)
5926     {
5927         this.refreshRow(record);
5928     },
5929     
5930     onRemove : function(ds, record, index, isUpdate){
5931         if(isUpdate !== true){
5932             this.fireEvent("beforerowremoved", this, index, record);
5933         }
5934         var bt = this.mainBody.dom;
5935         
5936         var rows = this.el.select('tbody > tr', true).elements;
5937         
5938         if(typeof(rows[index]) != 'undefined'){
5939             bt.removeChild(rows[index].dom);
5940         }
5941         
5942 //        if(bt.rows[index]){
5943 //            bt.removeChild(bt.rows[index]);
5944 //        }
5945         
5946         if(isUpdate !== true){
5947             //this.stripeRows(index);
5948             //this.syncRowHeights(index, index);
5949             //this.layout();
5950             this.fireEvent("rowremoved", this, index, record);
5951         }
5952     },
5953     
5954     onAdd : function(ds, records, rowIndex)
5955     {
5956         //Roo.log('on Add called');
5957         // - note this does not handle multiple adding very well..
5958         var bt = this.mainBody.dom;
5959         for (var i =0 ; i < records.length;i++) {
5960             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
5961             //Roo.log(records[i]);
5962             //Roo.log(this.store.getAt(rowIndex+i));
5963             this.insertRow(this.store, rowIndex + i, false);
5964             return;
5965         }
5966         
5967     },
5968     
5969     
5970     refreshRow : function(record){
5971         var ds = this.store, index;
5972         if(typeof record == 'number'){
5973             index = record;
5974             record = ds.getAt(index);
5975         }else{
5976             index = ds.indexOf(record);
5977         }
5978         this.insertRow(ds, index, true);
5979         this.onRemove(ds, record, index+1, true);
5980         //this.syncRowHeights(index, index);
5981         //this.layout();
5982         this.fireEvent("rowupdated", this, index, record);
5983     },
5984     
5985     insertRow : function(dm, rowIndex, isUpdate){
5986         
5987         if(!isUpdate){
5988             this.fireEvent("beforerowsinserted", this, rowIndex);
5989         }
5990             //var s = this.getScrollState();
5991         var row = this.renderRow(this.cm, this.store, rowIndex);
5992         // insert before rowIndex..
5993         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5994         
5995         var _this = this;
5996                 
5997         if(row.cellObjects.length){
5998             Roo.each(row.cellObjects, function(r){
5999                 _this.renderCellObject(r);
6000             })
6001         }
6002             
6003         if(!isUpdate){
6004             this.fireEvent("rowsinserted", this, rowIndex);
6005             //this.syncRowHeights(firstRow, lastRow);
6006             //this.stripeRows(firstRow);
6007             //this.layout();
6008         }
6009         
6010     },
6011     
6012     
6013     getRowDom : function(rowIndex)
6014     {
6015         var rows = this.el.select('tbody > tr', true).elements;
6016         
6017         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6018         
6019     },
6020     // returns the object tree for a tr..
6021   
6022     
6023     renderRow : function(cm, ds, rowIndex) 
6024     {
6025         
6026         var d = ds.getAt(rowIndex);
6027         
6028         var row = {
6029             tag : 'tr',
6030             cn : []
6031         };
6032             
6033         var cellObjects = [];
6034         
6035         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6036             var config = cm.config[i];
6037             
6038             var renderer = cm.getRenderer(i);
6039             var value = '';
6040             var id = false;
6041             
6042             if(typeof(renderer) !== 'undefined'){
6043                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6044             }
6045             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6046             // and are rendered into the cells after the row is rendered - using the id for the element.
6047             
6048             if(typeof(value) === 'object'){
6049                 id = Roo.id();
6050                 cellObjects.push({
6051                     container : id,
6052                     cfg : value 
6053                 })
6054             }
6055             
6056             var rowcfg = {
6057                 record: d,
6058                 rowIndex : rowIndex,
6059                 colIndex : i,
6060                 rowClass : ''
6061             }
6062
6063             this.fireEvent('rowclass', this, rowcfg);
6064             
6065             var td = {
6066                 tag: 'td',
6067                 cls : rowcfg.rowClass,
6068                 style: '',
6069                 html: (typeof(value) === 'object') ? '' : value
6070             };
6071             
6072             if (id) {
6073                 td.id = id;
6074             }
6075             
6076             if(typeof(config.colspan) != 'undefined'){
6077                 td.colspan = config.colspan;
6078             }
6079             
6080             if(typeof(config.hidden) != 'undefined' && config.hidden){
6081                 td.style += ' display:none;';
6082             }
6083             
6084             if(typeof(config.align) != 'undefined' && config.align.length){
6085                 td.style += ' text-align:' + config.align + ';';
6086             }
6087             
6088             if(typeof(config.width) != 'undefined'){
6089                 td.style += ' width:' +  config.width + 'px;';
6090             }
6091             
6092             if(typeof(config.cursor) != 'undefined'){
6093                 td.style += ' cursor:' +  config.cursor + ';';
6094             }
6095             
6096             if(typeof(config.cls) != 'undefined'){
6097                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6098             }
6099              
6100             row.cn.push(td);
6101            
6102         }
6103         
6104         row.cellObjects = cellObjects;
6105         
6106         return row;
6107           
6108     },
6109     
6110     
6111     
6112     onBeforeLoad : function()
6113     {
6114         //Roo.log('ds onBeforeLoad');
6115         
6116         //this.clear();
6117         
6118         //if(this.loadMask){
6119         //    this.maskEl.show();
6120         //}
6121     },
6122      /**
6123      * Remove all rows
6124      */
6125     clear : function()
6126     {
6127         this.el.select('tbody', true).first().dom.innerHTML = '';
6128     },
6129     /**
6130      * Show or hide a row.
6131      * @param {Number} rowIndex to show or hide
6132      * @param {Boolean} state hide
6133      */
6134     setRowVisibility : function(rowIndex, state)
6135     {
6136         var bt = this.mainBody.dom;
6137         
6138         var rows = this.el.select('tbody > tr', true).elements;
6139         
6140         if(typeof(rows[rowIndex]) == 'undefined'){
6141             return;
6142         }
6143         rows[rowIndex].dom.style.display = state ? '' : 'none';
6144     },
6145     
6146     
6147     getSelectionModel : function(){
6148         if(!this.selModel){
6149             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6150         }
6151         return this.selModel;
6152     },
6153     /*
6154      * Render the Roo.bootstrap object from renderder
6155      */
6156     renderCellObject : function(r)
6157     {
6158         var _this = this;
6159         
6160         var t = r.cfg.render(r.container);
6161         
6162         if(r.cfg.cn){
6163             Roo.each(r.cfg.cn, function(c){
6164                 var child = {
6165                     container: t.getChildContainer(),
6166                     cfg: c
6167                 }
6168                 _this.renderCellObject(child);
6169             })
6170         }
6171     },
6172     
6173     getRowIndex : function(row)
6174     {
6175         var rowIndex = -1;
6176         
6177         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6178             if(el != row){
6179                 return;
6180             }
6181             
6182             rowIndex = index;
6183         });
6184         
6185         return rowIndex;
6186     }
6187    
6188 });
6189
6190  
6191
6192  /*
6193  * - LGPL
6194  *
6195  * table cell
6196  * 
6197  */
6198
6199 /**
6200  * @class Roo.bootstrap.TableCell
6201  * @extends Roo.bootstrap.Component
6202  * Bootstrap TableCell class
6203  * @cfg {String} html cell contain text
6204  * @cfg {String} cls cell class
6205  * @cfg {String} tag cell tag (td|th) default td
6206  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6207  * @cfg {String} align Aligns the content in a cell
6208  * @cfg {String} axis Categorizes cells
6209  * @cfg {String} bgcolor Specifies the background color of a cell
6210  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6211  * @cfg {Number} colspan Specifies the number of columns a cell should span
6212  * @cfg {String} headers Specifies one or more header cells a cell is related to
6213  * @cfg {Number} height Sets the height of a cell
6214  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6215  * @cfg {Number} rowspan Sets the number of rows a cell should span
6216  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6217  * @cfg {String} valign Vertical aligns the content in a cell
6218  * @cfg {Number} width Specifies the width of a cell
6219  * 
6220  * @constructor
6221  * Create a new TableCell
6222  * @param {Object} config The config object
6223  */
6224
6225 Roo.bootstrap.TableCell = function(config){
6226     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6227 };
6228
6229 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6230     
6231     html: false,
6232     cls: false,
6233     tag: false,
6234     abbr: false,
6235     align: false,
6236     axis: false,
6237     bgcolor: false,
6238     charoff: false,
6239     colspan: false,
6240     headers: false,
6241     height: false,
6242     nowrap: false,
6243     rowspan: false,
6244     scope: false,
6245     valign: false,
6246     width: false,
6247     
6248     
6249     getAutoCreate : function(){
6250         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6251         
6252         cfg = {
6253             tag: 'td'
6254         }
6255         
6256         if(this.tag){
6257             cfg.tag = this.tag;
6258         }
6259         
6260         if (this.html) {
6261             cfg.html=this.html
6262         }
6263         if (this.cls) {
6264             cfg.cls=this.cls
6265         }
6266         if (this.abbr) {
6267             cfg.abbr=this.abbr
6268         }
6269         if (this.align) {
6270             cfg.align=this.align
6271         }
6272         if (this.axis) {
6273             cfg.axis=this.axis
6274         }
6275         if (this.bgcolor) {
6276             cfg.bgcolor=this.bgcolor
6277         }
6278         if (this.charoff) {
6279             cfg.charoff=this.charoff
6280         }
6281         if (this.colspan) {
6282             cfg.colspan=this.colspan
6283         }
6284         if (this.headers) {
6285             cfg.headers=this.headers
6286         }
6287         if (this.height) {
6288             cfg.height=this.height
6289         }
6290         if (this.nowrap) {
6291             cfg.nowrap=this.nowrap
6292         }
6293         if (this.rowspan) {
6294             cfg.rowspan=this.rowspan
6295         }
6296         if (this.scope) {
6297             cfg.scope=this.scope
6298         }
6299         if (this.valign) {
6300             cfg.valign=this.valign
6301         }
6302         if (this.width) {
6303             cfg.width=this.width
6304         }
6305         
6306         
6307         return cfg;
6308     }
6309    
6310 });
6311
6312  
6313
6314  /*
6315  * - LGPL
6316  *
6317  * table row
6318  * 
6319  */
6320
6321 /**
6322  * @class Roo.bootstrap.TableRow
6323  * @extends Roo.bootstrap.Component
6324  * Bootstrap TableRow class
6325  * @cfg {String} cls row class
6326  * @cfg {String} align Aligns the content in a table row
6327  * @cfg {String} bgcolor Specifies a background color for a table row
6328  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6329  * @cfg {String} valign Vertical aligns the content in a table row
6330  * 
6331  * @constructor
6332  * Create a new TableRow
6333  * @param {Object} config The config object
6334  */
6335
6336 Roo.bootstrap.TableRow = function(config){
6337     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6338 };
6339
6340 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6341     
6342     cls: false,
6343     align: false,
6344     bgcolor: false,
6345     charoff: false,
6346     valign: false,
6347     
6348     getAutoCreate : function(){
6349         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6350         
6351         cfg = {
6352             tag: 'tr'
6353         }
6354             
6355         if(this.cls){
6356             cfg.cls = this.cls;
6357         }
6358         if(this.align){
6359             cfg.align = this.align;
6360         }
6361         if(this.bgcolor){
6362             cfg.bgcolor = this.bgcolor;
6363         }
6364         if(this.charoff){
6365             cfg.charoff = this.charoff;
6366         }
6367         if(this.valign){
6368             cfg.valign = this.valign;
6369         }
6370         
6371         return cfg;
6372     }
6373    
6374 });
6375
6376  
6377
6378  /*
6379  * - LGPL
6380  *
6381  * table body
6382  * 
6383  */
6384
6385 /**
6386  * @class Roo.bootstrap.TableBody
6387  * @extends Roo.bootstrap.Component
6388  * Bootstrap TableBody class
6389  * @cfg {String} cls element class
6390  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6391  * @cfg {String} align Aligns the content inside the element
6392  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6393  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6394  * 
6395  * @constructor
6396  * Create a new TableBody
6397  * @param {Object} config The config object
6398  */
6399
6400 Roo.bootstrap.TableBody = function(config){
6401     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6402 };
6403
6404 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6405     
6406     cls: false,
6407     tag: false,
6408     align: false,
6409     charoff: false,
6410     valign: false,
6411     
6412     getAutoCreate : function(){
6413         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6414         
6415         cfg = {
6416             tag: 'tbody'
6417         }
6418             
6419         if (this.cls) {
6420             cfg.cls=this.cls
6421         }
6422         if(this.tag){
6423             cfg.tag = this.tag;
6424         }
6425         
6426         if(this.align){
6427             cfg.align = this.align;
6428         }
6429         if(this.charoff){
6430             cfg.charoff = this.charoff;
6431         }
6432         if(this.valign){
6433             cfg.valign = this.valign;
6434         }
6435         
6436         return cfg;
6437     }
6438     
6439     
6440 //    initEvents : function()
6441 //    {
6442 //        
6443 //        if(!this.store){
6444 //            return;
6445 //        }
6446 //        
6447 //        this.store = Roo.factory(this.store, Roo.data);
6448 //        this.store.on('load', this.onLoad, this);
6449 //        
6450 //        this.store.load();
6451 //        
6452 //    },
6453 //    
6454 //    onLoad: function () 
6455 //    {   
6456 //        this.fireEvent('load', this);
6457 //    }
6458 //    
6459 //   
6460 });
6461
6462  
6463
6464  /*
6465  * Based on:
6466  * Ext JS Library 1.1.1
6467  * Copyright(c) 2006-2007, Ext JS, LLC.
6468  *
6469  * Originally Released Under LGPL - original licence link has changed is not relivant.
6470  *
6471  * Fork - LGPL
6472  * <script type="text/javascript">
6473  */
6474
6475 // as we use this in bootstrap.
6476 Roo.namespace('Roo.form');
6477  /**
6478  * @class Roo.form.Action
6479  * Internal Class used to handle form actions
6480  * @constructor
6481  * @param {Roo.form.BasicForm} el The form element or its id
6482  * @param {Object} config Configuration options
6483  */
6484
6485  
6486  
6487 // define the action interface
6488 Roo.form.Action = function(form, options){
6489     this.form = form;
6490     this.options = options || {};
6491 };
6492 /**
6493  * Client Validation Failed
6494  * @const 
6495  */
6496 Roo.form.Action.CLIENT_INVALID = 'client';
6497 /**
6498  * Server Validation Failed
6499  * @const 
6500  */
6501 Roo.form.Action.SERVER_INVALID = 'server';
6502  /**
6503  * Connect to Server Failed
6504  * @const 
6505  */
6506 Roo.form.Action.CONNECT_FAILURE = 'connect';
6507 /**
6508  * Reading Data from Server Failed
6509  * @const 
6510  */
6511 Roo.form.Action.LOAD_FAILURE = 'load';
6512
6513 Roo.form.Action.prototype = {
6514     type : 'default',
6515     failureType : undefined,
6516     response : undefined,
6517     result : undefined,
6518
6519     // interface method
6520     run : function(options){
6521
6522     },
6523
6524     // interface method
6525     success : function(response){
6526
6527     },
6528
6529     // interface method
6530     handleResponse : function(response){
6531
6532     },
6533
6534     // default connection failure
6535     failure : function(response){
6536         
6537         this.response = response;
6538         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6539         this.form.afterAction(this, false);
6540     },
6541
6542     processResponse : function(response){
6543         this.response = response;
6544         if(!response.responseText){
6545             return true;
6546         }
6547         this.result = this.handleResponse(response);
6548         return this.result;
6549     },
6550
6551     // utility functions used internally
6552     getUrl : function(appendParams){
6553         var url = this.options.url || this.form.url || this.form.el.dom.action;
6554         if(appendParams){
6555             var p = this.getParams();
6556             if(p){
6557                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6558             }
6559         }
6560         return url;
6561     },
6562
6563     getMethod : function(){
6564         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6565     },
6566
6567     getParams : function(){
6568         var bp = this.form.baseParams;
6569         var p = this.options.params;
6570         if(p){
6571             if(typeof p == "object"){
6572                 p = Roo.urlEncode(Roo.applyIf(p, bp));
6573             }else if(typeof p == 'string' && bp){
6574                 p += '&' + Roo.urlEncode(bp);
6575             }
6576         }else if(bp){
6577             p = Roo.urlEncode(bp);
6578         }
6579         return p;
6580     },
6581
6582     createCallback : function(){
6583         return {
6584             success: this.success,
6585             failure: this.failure,
6586             scope: this,
6587             timeout: (this.form.timeout*1000),
6588             upload: this.form.fileUpload ? this.success : undefined
6589         };
6590     }
6591 };
6592
6593 Roo.form.Action.Submit = function(form, options){
6594     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6595 };
6596
6597 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6598     type : 'submit',
6599
6600     haveProgress : false,
6601     uploadComplete : false,
6602     
6603     // uploadProgress indicator.
6604     uploadProgress : function()
6605     {
6606         if (!this.form.progressUrl) {
6607             return;
6608         }
6609         
6610         if (!this.haveProgress) {
6611             Roo.MessageBox.progress("Uploading", "Uploading");
6612         }
6613         if (this.uploadComplete) {
6614            Roo.MessageBox.hide();
6615            return;
6616         }
6617         
6618         this.haveProgress = true;
6619    
6620         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6621         
6622         var c = new Roo.data.Connection();
6623         c.request({
6624             url : this.form.progressUrl,
6625             params: {
6626                 id : uid
6627             },
6628             method: 'GET',
6629             success : function(req){
6630                //console.log(data);
6631                 var rdata = false;
6632                 var edata;
6633                 try  {
6634                    rdata = Roo.decode(req.responseText)
6635                 } catch (e) {
6636                     Roo.log("Invalid data from server..");
6637                     Roo.log(edata);
6638                     return;
6639                 }
6640                 if (!rdata || !rdata.success) {
6641                     Roo.log(rdata);
6642                     Roo.MessageBox.alert(Roo.encode(rdata));
6643                     return;
6644                 }
6645                 var data = rdata.data;
6646                 
6647                 if (this.uploadComplete) {
6648                    Roo.MessageBox.hide();
6649                    return;
6650                 }
6651                    
6652                 if (data){
6653                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6654                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6655                     );
6656                 }
6657                 this.uploadProgress.defer(2000,this);
6658             },
6659        
6660             failure: function(data) {
6661                 Roo.log('progress url failed ');
6662                 Roo.log(data);
6663             },
6664             scope : this
6665         });
6666            
6667     },
6668     
6669     
6670     run : function()
6671     {
6672         // run get Values on the form, so it syncs any secondary forms.
6673         this.form.getValues();
6674         
6675         var o = this.options;
6676         var method = this.getMethod();
6677         var isPost = method == 'POST';
6678         if(o.clientValidation === false || this.form.isValid()){
6679             
6680             if (this.form.progressUrl) {
6681                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6682                     (new Date() * 1) + '' + Math.random());
6683                     
6684             } 
6685             
6686             
6687             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6688                 form:this.form.el.dom,
6689                 url:this.getUrl(!isPost),
6690                 method: method,
6691                 params:isPost ? this.getParams() : null,
6692                 isUpload: this.form.fileUpload
6693             }));
6694             
6695             this.uploadProgress();
6696
6697         }else if (o.clientValidation !== false){ // client validation failed
6698             this.failureType = Roo.form.Action.CLIENT_INVALID;
6699             this.form.afterAction(this, false);
6700         }
6701     },
6702
6703     success : function(response)
6704     {
6705         this.uploadComplete= true;
6706         if (this.haveProgress) {
6707             Roo.MessageBox.hide();
6708         }
6709         
6710         
6711         var result = this.processResponse(response);
6712         if(result === true || result.success){
6713             this.form.afterAction(this, true);
6714             return;
6715         }
6716         if(result.errors){
6717             this.form.markInvalid(result.errors);
6718             this.failureType = Roo.form.Action.SERVER_INVALID;
6719         }
6720         this.form.afterAction(this, false);
6721     },
6722     failure : function(response)
6723     {
6724         this.uploadComplete= true;
6725         if (this.haveProgress) {
6726             Roo.MessageBox.hide();
6727         }
6728         
6729         this.response = response;
6730         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6731         this.form.afterAction(this, false);
6732     },
6733     
6734     handleResponse : function(response){
6735         if(this.form.errorReader){
6736             var rs = this.form.errorReader.read(response);
6737             var errors = [];
6738             if(rs.records){
6739                 for(var i = 0, len = rs.records.length; i < len; i++) {
6740                     var r = rs.records[i];
6741                     errors[i] = r.data;
6742                 }
6743             }
6744             if(errors.length < 1){
6745                 errors = null;
6746             }
6747             return {
6748                 success : rs.success,
6749                 errors : errors
6750             };
6751         }
6752         var ret = false;
6753         try {
6754             ret = Roo.decode(response.responseText);
6755         } catch (e) {
6756             ret = {
6757                 success: false,
6758                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6759                 errors : []
6760             };
6761         }
6762         return ret;
6763         
6764     }
6765 });
6766
6767
6768 Roo.form.Action.Load = function(form, options){
6769     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6770     this.reader = this.form.reader;
6771 };
6772
6773 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6774     type : 'load',
6775
6776     run : function(){
6777         
6778         Roo.Ajax.request(Roo.apply(
6779                 this.createCallback(), {
6780                     method:this.getMethod(),
6781                     url:this.getUrl(false),
6782                     params:this.getParams()
6783         }));
6784     },
6785
6786     success : function(response){
6787         
6788         var result = this.processResponse(response);
6789         if(result === true || !result.success || !result.data){
6790             this.failureType = Roo.form.Action.LOAD_FAILURE;
6791             this.form.afterAction(this, false);
6792             return;
6793         }
6794         this.form.clearInvalid();
6795         this.form.setValues(result.data);
6796         this.form.afterAction(this, true);
6797     },
6798
6799     handleResponse : function(response){
6800         if(this.form.reader){
6801             var rs = this.form.reader.read(response);
6802             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6803             return {
6804                 success : rs.success,
6805                 data : data
6806             };
6807         }
6808         return Roo.decode(response.responseText);
6809     }
6810 });
6811
6812 Roo.form.Action.ACTION_TYPES = {
6813     'load' : Roo.form.Action.Load,
6814     'submit' : Roo.form.Action.Submit
6815 };/*
6816  * - LGPL
6817  *
6818  * form
6819  * 
6820  */
6821
6822 /**
6823  * @class Roo.bootstrap.Form
6824  * @extends Roo.bootstrap.Component
6825  * Bootstrap Form class
6826  * @cfg {String} method  GET | POST (default POST)
6827  * @cfg {String} labelAlign top | left (default top)
6828  * @cfg {String} align left  | right - for navbars
6829  * @cfg {Boolean} loadMask load mask when submit (default true)
6830
6831  * 
6832  * @constructor
6833  * Create a new Form
6834  * @param {Object} config The config object
6835  */
6836
6837
6838 Roo.bootstrap.Form = function(config){
6839     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6840     this.addEvents({
6841         /**
6842          * @event clientvalidation
6843          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6844          * @param {Form} this
6845          * @param {Boolean} valid true if the form has passed client-side validation
6846          */
6847         clientvalidation: true,
6848         /**
6849          * @event beforeaction
6850          * Fires before any action is performed. Return false to cancel the action.
6851          * @param {Form} this
6852          * @param {Action} action The action to be performed
6853          */
6854         beforeaction: true,
6855         /**
6856          * @event actionfailed
6857          * Fires when an action fails.
6858          * @param {Form} this
6859          * @param {Action} action The action that failed
6860          */
6861         actionfailed : true,
6862         /**
6863          * @event actioncomplete
6864          * Fires when an action is completed.
6865          * @param {Form} this
6866          * @param {Action} action The action that completed
6867          */
6868         actioncomplete : true
6869     });
6870     
6871 };
6872
6873 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6874       
6875      /**
6876      * @cfg {String} method
6877      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6878      */
6879     method : 'POST',
6880     /**
6881      * @cfg {String} url
6882      * The URL to use for form actions if one isn't supplied in the action options.
6883      */
6884     /**
6885      * @cfg {Boolean} fileUpload
6886      * Set to true if this form is a file upload.
6887      */
6888      
6889     /**
6890      * @cfg {Object} baseParams
6891      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6892      */
6893       
6894     /**
6895      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6896      */
6897     timeout: 30,
6898     /**
6899      * @cfg {Sting} align (left|right) for navbar forms
6900      */
6901     align : 'left',
6902
6903     // private
6904     activeAction : null,
6905  
6906     /**
6907      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6908      * element by passing it or its id or mask the form itself by passing in true.
6909      * @type Mixed
6910      */
6911     waitMsgTarget : false,
6912     
6913     loadMask : true,
6914     
6915     getAutoCreate : function(){
6916         
6917         var cfg = {
6918             tag: 'form',
6919             method : this.method || 'POST',
6920             id : this.id || Roo.id(),
6921             cls : ''
6922         }
6923         if (this.parent().xtype.match(/^Nav/)) {
6924             cfg.cls = 'navbar-form navbar-' + this.align;
6925             
6926         }
6927         
6928         if (this.labelAlign == 'left' ) {
6929             cfg.cls += ' form-horizontal';
6930         }
6931         
6932         
6933         return cfg;
6934     },
6935     initEvents : function()
6936     {
6937         this.el.on('submit', this.onSubmit, this);
6938         // this was added as random key presses on the form where triggering form submit.
6939         this.el.on('keypress', function(e) {
6940             if (e.getCharCode() != 13) {
6941                 return true;
6942             }
6943             // we might need to allow it for textareas.. and some other items.
6944             // check e.getTarget().
6945             
6946             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6947                 return true;
6948             }
6949         
6950             Roo.log("keypress blocked");
6951             
6952             e.preventDefault();
6953             return false;
6954         });
6955         
6956     },
6957     // private
6958     onSubmit : function(e){
6959         e.stopEvent();
6960     },
6961     
6962      /**
6963      * Returns true if client-side validation on the form is successful.
6964      * @return Boolean
6965      */
6966     isValid : function(){
6967         var items = this.getItems();
6968         var valid = true;
6969         items.each(function(f){
6970            if(!f.validate()){
6971                valid = false;
6972                
6973            }
6974         });
6975         return valid;
6976     },
6977     /**
6978      * Returns true if any fields in this form have changed since their original load.
6979      * @return Boolean
6980      */
6981     isDirty : function(){
6982         var dirty = false;
6983         var items = this.getItems();
6984         items.each(function(f){
6985            if(f.isDirty()){
6986                dirty = true;
6987                return false;
6988            }
6989            return true;
6990         });
6991         return dirty;
6992     },
6993      /**
6994      * Performs a predefined action (submit or load) or custom actions you define on this form.
6995      * @param {String} actionName The name of the action type
6996      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6997      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6998      * accept other config options):
6999      * <pre>
7000 Property          Type             Description
7001 ----------------  ---------------  ----------------------------------------------------------------------------------
7002 url               String           The url for the action (defaults to the form's url)
7003 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7004 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7005 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7006                                    validate the form on the client (defaults to false)
7007      * </pre>
7008      * @return {BasicForm} this
7009      */
7010     doAction : function(action, options){
7011         if(typeof action == 'string'){
7012             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7013         }
7014         if(this.fireEvent('beforeaction', this, action) !== false){
7015             this.beforeAction(action);
7016             action.run.defer(100, action);
7017         }
7018         return this;
7019     },
7020     
7021     // private
7022     beforeAction : function(action){
7023         var o = action.options;
7024         
7025         if(this.loadMask){
7026             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7027         }
7028         // not really supported yet.. ??
7029         
7030         //if(this.waitMsgTarget === true){
7031         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7032         //}else if(this.waitMsgTarget){
7033         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7034         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7035         //}else {
7036         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7037        // }
7038          
7039     },
7040
7041     // private
7042     afterAction : function(action, success){
7043         this.activeAction = null;
7044         var o = action.options;
7045         
7046         //if(this.waitMsgTarget === true){
7047             this.el.unmask();
7048         //}else if(this.waitMsgTarget){
7049         //    this.waitMsgTarget.unmask();
7050         //}else{
7051         //    Roo.MessageBox.updateProgress(1);
7052         //    Roo.MessageBox.hide();
7053        // }
7054         // 
7055         if(success){
7056             if(o.reset){
7057                 this.reset();
7058             }
7059             Roo.callback(o.success, o.scope, [this, action]);
7060             this.fireEvent('actioncomplete', this, action);
7061             
7062         }else{
7063             
7064             // failure condition..
7065             // we have a scenario where updates need confirming.
7066             // eg. if a locking scenario exists..
7067             // we look for { errors : { needs_confirm : true }} in the response.
7068             if (
7069                 (typeof(action.result) != 'undefined')  &&
7070                 (typeof(action.result.errors) != 'undefined')  &&
7071                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7072            ){
7073                 var _t = this;
7074                 Roo.log("not supported yet");
7075                  /*
7076                 
7077                 Roo.MessageBox.confirm(
7078                     "Change requires confirmation",
7079                     action.result.errorMsg,
7080                     function(r) {
7081                         if (r != 'yes') {
7082                             return;
7083                         }
7084                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7085                     }
7086                     
7087                 );
7088                 */
7089                 
7090                 
7091                 return;
7092             }
7093             
7094             Roo.callback(o.failure, o.scope, [this, action]);
7095             // show an error message if no failed handler is set..
7096             if (!this.hasListener('actionfailed')) {
7097                 Roo.log("need to add dialog support");
7098                 /*
7099                 Roo.MessageBox.alert("Error",
7100                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7101                         action.result.errorMsg :
7102                         "Saving Failed, please check your entries or try again"
7103                 );
7104                 */
7105             }
7106             
7107             this.fireEvent('actionfailed', this, action);
7108         }
7109         
7110     },
7111     /**
7112      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7113      * @param {String} id The value to search for
7114      * @return Field
7115      */
7116     findField : function(id){
7117         var items = this.getItems();
7118         var field = items.get(id);
7119         if(!field){
7120              items.each(function(f){
7121                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7122                     field = f;
7123                     return false;
7124                 }
7125                 return true;
7126             });
7127         }
7128         return field || null;
7129     },
7130      /**
7131      * Mark fields in this form invalid in bulk.
7132      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7133      * @return {BasicForm} this
7134      */
7135     markInvalid : function(errors){
7136         if(errors instanceof Array){
7137             for(var i = 0, len = errors.length; i < len; i++){
7138                 var fieldError = errors[i];
7139                 var f = this.findField(fieldError.id);
7140                 if(f){
7141                     f.markInvalid(fieldError.msg);
7142                 }
7143             }
7144         }else{
7145             var field, id;
7146             for(id in errors){
7147                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7148                     field.markInvalid(errors[id]);
7149                 }
7150             }
7151         }
7152         //Roo.each(this.childForms || [], function (f) {
7153         //    f.markInvalid(errors);
7154         //});
7155         
7156         return this;
7157     },
7158
7159     /**
7160      * Set values for fields in this form in bulk.
7161      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7162      * @return {BasicForm} this
7163      */
7164     setValues : function(values){
7165         if(values instanceof Array){ // array of objects
7166             for(var i = 0, len = values.length; i < len; i++){
7167                 var v = values[i];
7168                 var f = this.findField(v.id);
7169                 if(f){
7170                     f.setValue(v.value);
7171                     if(this.trackResetOnLoad){
7172                         f.originalValue = f.getValue();
7173                     }
7174                 }
7175             }
7176         }else{ // object hash
7177             var field, id;
7178             for(id in values){
7179                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7180                     
7181                     if (field.setFromData && 
7182                         field.valueField && 
7183                         field.displayField &&
7184                         // combos' with local stores can 
7185                         // be queried via setValue()
7186                         // to set their value..
7187                         (field.store && !field.store.isLocal)
7188                         ) {
7189                         // it's a combo
7190                         var sd = { };
7191                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7192                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7193                         field.setFromData(sd);
7194                         
7195                     } else {
7196                         field.setValue(values[id]);
7197                     }
7198                     
7199                     
7200                     if(this.trackResetOnLoad){
7201                         field.originalValue = field.getValue();
7202                     }
7203                 }
7204             }
7205         }
7206          
7207         //Roo.each(this.childForms || [], function (f) {
7208         //    f.setValues(values);
7209         //});
7210                 
7211         return this;
7212     },
7213
7214     /**
7215      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7216      * they are returned as an array.
7217      * @param {Boolean} asString
7218      * @return {Object}
7219      */
7220     getValues : function(asString){
7221         //if (this.childForms) {
7222             // copy values from the child forms
7223         //    Roo.each(this.childForms, function (f) {
7224         //        this.setValues(f.getValues());
7225         //    }, this);
7226         //}
7227         
7228         
7229         
7230         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7231         if(asString === true){
7232             return fs;
7233         }
7234         return Roo.urlDecode(fs);
7235     },
7236     
7237     /**
7238      * Returns the fields in this form as an object with key/value pairs. 
7239      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7240      * @return {Object}
7241      */
7242     getFieldValues : function(with_hidden)
7243     {
7244         var items = this.getItems();
7245         var ret = {};
7246         items.each(function(f){
7247             if (!f.getName()) {
7248                 return;
7249             }
7250             var v = f.getValue();
7251             if (f.inputType =='radio') {
7252                 if (typeof(ret[f.getName()]) == 'undefined') {
7253                     ret[f.getName()] = ''; // empty..
7254                 }
7255                 
7256                 if (!f.el.dom.checked) {
7257                     return;
7258                     
7259                 }
7260                 v = f.el.dom.value;
7261                 
7262             }
7263             
7264             // not sure if this supported any more..
7265             if ((typeof(v) == 'object') && f.getRawValue) {
7266                 v = f.getRawValue() ; // dates..
7267             }
7268             // combo boxes where name != hiddenName...
7269             if (f.name != f.getName()) {
7270                 ret[f.name] = f.getRawValue();
7271             }
7272             ret[f.getName()] = v;
7273         });
7274         
7275         return ret;
7276     },
7277
7278     /**
7279      * Clears all invalid messages in this form.
7280      * @return {BasicForm} this
7281      */
7282     clearInvalid : function(){
7283         var items = this.getItems();
7284         
7285         items.each(function(f){
7286            f.clearInvalid();
7287         });
7288         
7289         
7290         
7291         return this;
7292     },
7293
7294     /**
7295      * Resets this form.
7296      * @return {BasicForm} this
7297      */
7298     reset : function(){
7299         var items = this.getItems();
7300         items.each(function(f){
7301             f.reset();
7302         });
7303         
7304         Roo.each(this.childForms || [], function (f) {
7305             f.reset();
7306         });
7307        
7308         
7309         return this;
7310     },
7311     getItems : function()
7312     {
7313         var r=new Roo.util.MixedCollection(false, function(o){
7314             return o.id || (o.id = Roo.id());
7315         });
7316         var iter = function(el) {
7317             if (el.inputEl) {
7318                 r.add(el);
7319             }
7320             if (!el.items) {
7321                 return;
7322             }
7323             Roo.each(el.items,function(e) {
7324                 iter(e);
7325             });
7326             
7327             
7328         };
7329         
7330         iter(this);
7331         return r;
7332         
7333         
7334         
7335         
7336     }
7337     
7338 });
7339
7340  
7341 /*
7342  * Based on:
7343  * Ext JS Library 1.1.1
7344  * Copyright(c) 2006-2007, Ext JS, LLC.
7345  *
7346  * Originally Released Under LGPL - original licence link has changed is not relivant.
7347  *
7348  * Fork - LGPL
7349  * <script type="text/javascript">
7350  */
7351 /**
7352  * @class Roo.form.VTypes
7353  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7354  * @singleton
7355  */
7356 Roo.form.VTypes = function(){
7357     // closure these in so they are only created once.
7358     var alpha = /^[a-zA-Z_]+$/;
7359     var alphanum = /^[a-zA-Z0-9_]+$/;
7360     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7361     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7362
7363     // All these messages and functions are configurable
7364     return {
7365         /**
7366          * The function used to validate email addresses
7367          * @param {String} value The email address
7368          */
7369         'email' : function(v){
7370             return email.test(v);
7371         },
7372         /**
7373          * The error text to display when the email validation function returns false
7374          * @type String
7375          */
7376         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7377         /**
7378          * The keystroke filter mask to be applied on email input
7379          * @type RegExp
7380          */
7381         'emailMask' : /[a-z0-9_\.\-@]/i,
7382
7383         /**
7384          * The function used to validate URLs
7385          * @param {String} value The URL
7386          */
7387         'url' : function(v){
7388             return url.test(v);
7389         },
7390         /**
7391          * The error text to display when the url validation function returns false
7392          * @type String
7393          */
7394         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7395         
7396         /**
7397          * The function used to validate alpha values
7398          * @param {String} value The value
7399          */
7400         'alpha' : function(v){
7401             return alpha.test(v);
7402         },
7403         /**
7404          * The error text to display when the alpha validation function returns false
7405          * @type String
7406          */
7407         'alphaText' : 'This field should only contain letters and _',
7408         /**
7409          * The keystroke filter mask to be applied on alpha input
7410          * @type RegExp
7411          */
7412         'alphaMask' : /[a-z_]/i,
7413
7414         /**
7415          * The function used to validate alphanumeric values
7416          * @param {String} value The value
7417          */
7418         'alphanum' : function(v){
7419             return alphanum.test(v);
7420         },
7421         /**
7422          * The error text to display when the alphanumeric validation function returns false
7423          * @type String
7424          */
7425         'alphanumText' : 'This field should only contain letters, numbers and _',
7426         /**
7427          * The keystroke filter mask to be applied on alphanumeric input
7428          * @type RegExp
7429          */
7430         'alphanumMask' : /[a-z0-9_]/i
7431     };
7432 }();/*
7433  * - LGPL
7434  *
7435  * Input
7436  * 
7437  */
7438
7439 /**
7440  * @class Roo.bootstrap.Input
7441  * @extends Roo.bootstrap.Component
7442  * Bootstrap Input class
7443  * @cfg {Boolean} disabled is it disabled
7444  * @cfg {String} fieldLabel - the label associated
7445  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7446  * @cfg {String} name name of the input
7447  * @cfg {string} fieldLabel - the label associated
7448  * @cfg {string}  inputType - input / file submit ...
7449  * @cfg {string} placeholder - placeholder to put in text.
7450  * @cfg {string}  before - input group add on before
7451  * @cfg {string} after - input group add on after
7452  * @cfg {string} size - (lg|sm) or leave empty..
7453  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7454  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7455  * @cfg {Number} md colspan out of 12 for computer-sized screens
7456  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7457  * @cfg {string} value default value of the input
7458  * @cfg {Number} labelWidth set the width of label (0-12)
7459  * @cfg {String} labelAlign (top|left)
7460  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7461  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7462
7463  * @cfg {String} align (left|center|right) Default left
7464  * 
7465  * 
7466  * 
7467  * @constructor
7468  * Create a new Input
7469  * @param {Object} config The config object
7470  */
7471
7472 Roo.bootstrap.Input = function(config){
7473     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7474    
7475         this.addEvents({
7476             /**
7477              * @event focus
7478              * Fires when this field receives input focus.
7479              * @param {Roo.form.Field} this
7480              */
7481             focus : true,
7482             /**
7483              * @event blur
7484              * Fires when this field loses input focus.
7485              * @param {Roo.form.Field} this
7486              */
7487             blur : true,
7488             /**
7489              * @event specialkey
7490              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7491              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7492              * @param {Roo.form.Field} this
7493              * @param {Roo.EventObject} e The event object
7494              */
7495             specialkey : true,
7496             /**
7497              * @event change
7498              * Fires just before the field blurs if the field value has changed.
7499              * @param {Roo.form.Field} this
7500              * @param {Mixed} newValue The new value
7501              * @param {Mixed} oldValue The original value
7502              */
7503             change : true,
7504             /**
7505              * @event invalid
7506              * Fires after the field has been marked as invalid.
7507              * @param {Roo.form.Field} this
7508              * @param {String} msg The validation message
7509              */
7510             invalid : true,
7511             /**
7512              * @event valid
7513              * Fires after the field has been validated with no errors.
7514              * @param {Roo.form.Field} this
7515              */
7516             valid : true,
7517              /**
7518              * @event keyup
7519              * Fires after the key up
7520              * @param {Roo.form.Field} this
7521              * @param {Roo.EventObject}  e The event Object
7522              */
7523             keyup : true
7524         });
7525 };
7526
7527 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
7528      /**
7529      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7530       automatic validation (defaults to "keyup").
7531      */
7532     validationEvent : "keyup",
7533      /**
7534      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7535      */
7536     validateOnBlur : true,
7537     /**
7538      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7539      */
7540     validationDelay : 250,
7541      /**
7542      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7543      */
7544     focusClass : "x-form-focus",  // not needed???
7545     
7546        
7547     /**
7548      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7549      */
7550     invalidClass : "has-warning",
7551     
7552     /**
7553      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7554      */
7555     validClass : "has-success",
7556     
7557     /**
7558      * @cfg {Boolean} hasFeedback (true|false) default true
7559      */
7560     hasFeedback : true,
7561     
7562     /**
7563      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7564      */
7565     invalidFeedbackClass : "glyphicon-warning-sign",
7566     
7567     /**
7568      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7569      */
7570     validFeedbackClass : "glyphicon-ok",
7571     
7572     /**
7573      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7574      */
7575     selectOnFocus : false,
7576     
7577      /**
7578      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7579      */
7580     maskRe : null,
7581        /**
7582      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7583      */
7584     vtype : null,
7585     
7586       /**
7587      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7588      */
7589     disableKeyFilter : false,
7590     
7591        /**
7592      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7593      */
7594     disabled : false,
7595      /**
7596      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7597      */
7598     allowBlank : true,
7599     /**
7600      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7601      */
7602     blankText : "This field is required",
7603     
7604      /**
7605      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7606      */
7607     minLength : 0,
7608     /**
7609      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7610      */
7611     maxLength : Number.MAX_VALUE,
7612     /**
7613      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7614      */
7615     minLengthText : "The minimum length for this field is {0}",
7616     /**
7617      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7618      */
7619     maxLengthText : "The maximum length for this field is {0}",
7620   
7621     
7622     /**
7623      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7624      * If available, this function will be called only after the basic validators all return true, and will be passed the
7625      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7626      */
7627     validator : null,
7628     /**
7629      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7630      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7631      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7632      */
7633     regex : null,
7634     /**
7635      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7636      */
7637     regexText : "",
7638     
7639     autocomplete: false,
7640     
7641     
7642     fieldLabel : '',
7643     inputType : 'text',
7644     
7645     name : false,
7646     placeholder: false,
7647     before : false,
7648     after : false,
7649     size : false,
7650     hasFocus : false,
7651     preventMark: false,
7652     isFormField : true,
7653     value : '',
7654     labelWidth : 2,
7655     labelAlign : false,
7656     readOnly : false,
7657     align : false,
7658     formatedValue : false,
7659     
7660     parentLabelAlign : function()
7661     {
7662         var parent = this;
7663         while (parent.parent()) {
7664             parent = parent.parent();
7665             if (typeof(parent.labelAlign) !='undefined') {
7666                 return parent.labelAlign;
7667             }
7668         }
7669         return 'left';
7670         
7671     },
7672     
7673     getAutoCreate : function(){
7674         
7675         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7676         
7677         var id = Roo.id();
7678         
7679         var cfg = {};
7680         
7681         if(this.inputType != 'hidden'){
7682             cfg.cls = 'form-group' //input-group
7683         }
7684         
7685         var input =  {
7686             tag: 'input',
7687             id : id,
7688             type : this.inputType,
7689             value : this.value,
7690             cls : 'form-control',
7691             placeholder : this.placeholder || '',
7692             autocomplete : this.autocomplete || 'new-password'
7693         };
7694         
7695         
7696         if(this.align){
7697             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7698         }
7699         
7700         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7701             input.maxLength = this.maxLength;
7702         }
7703         
7704         if (this.disabled) {
7705             input.disabled=true;
7706         }
7707         
7708         if (this.readOnly) {
7709             input.readonly=true;
7710         }
7711         
7712         if (this.name) {
7713             input.name = this.name;
7714         }
7715         if (this.size) {
7716             input.cls += ' input-' + this.size;
7717         }
7718         var settings=this;
7719         ['xs','sm','md','lg'].map(function(size){
7720             if (settings[size]) {
7721                 cfg.cls += ' col-' + size + '-' + settings[size];
7722             }
7723         });
7724         
7725         var inputblock = input;
7726         
7727         var feedback = {
7728             tag: 'span',
7729             cls: 'glyphicon form-control-feedback'
7730         };
7731             
7732         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7733             
7734             inputblock = {
7735                 cls : 'has-feedback',
7736                 cn :  [
7737                     input,
7738                     feedback
7739                 ] 
7740             };  
7741         }
7742         
7743         if (this.before || this.after) {
7744             
7745             inputblock = {
7746                 cls : 'input-group',
7747                 cn :  [] 
7748             };
7749             
7750             if (this.before && typeof(this.before) == 'string') {
7751                 
7752                 inputblock.cn.push({
7753                     tag :'span',
7754                     cls : 'roo-input-before input-group-addon',
7755                     html : this.before
7756                 });
7757             }
7758             if (this.before && typeof(this.before) == 'object') {
7759                 this.before = Roo.factory(this.before);
7760                 Roo.log(this.before);
7761                 inputblock.cn.push({
7762                     tag :'span',
7763                     cls : 'roo-input-before input-group-' +
7764                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7765                 });
7766             }
7767             
7768             inputblock.cn.push(input);
7769             
7770             if (this.after && typeof(this.after) == 'string') {
7771                 inputblock.cn.push({
7772                     tag :'span',
7773                     cls : 'roo-input-after input-group-addon',
7774                     html : this.after
7775                 });
7776             }
7777             if (this.after && typeof(this.after) == 'object') {
7778                 this.after = Roo.factory(this.after);
7779                 Roo.log(this.after);
7780                 inputblock.cn.push({
7781                     tag :'span',
7782                     cls : 'roo-input-after input-group-' +
7783                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7784                 });
7785             }
7786             
7787             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7788                 inputblock.cls += ' has-feedback';
7789                 inputblock.cn.push(feedback);
7790             }
7791         };
7792         
7793         if (align ==='left' && this.fieldLabel.length) {
7794                 Roo.log("left and has label");
7795                 cfg.cn = [
7796                     
7797                     {
7798                         tag: 'label',
7799                         'for' :  id,
7800                         cls : 'control-label col-sm-' + this.labelWidth,
7801                         html : this.fieldLabel
7802                         
7803                     },
7804                     {
7805                         cls : "col-sm-" + (12 - this.labelWidth), 
7806                         cn: [
7807                             inputblock
7808                         ]
7809                     }
7810                     
7811                 ];
7812         } else if ( this.fieldLabel.length) {
7813                 Roo.log(" label");
7814                  cfg.cn = [
7815                    
7816                     {
7817                         tag: 'label',
7818                         //cls : 'input-group-addon',
7819                         html : this.fieldLabel
7820                         
7821                     },
7822                     
7823                     inputblock
7824                     
7825                 ];
7826
7827         } else {
7828             
7829                 Roo.log(" no label && no align");
7830                 cfg.cn = [
7831                     
7832                         inputblock
7833                     
7834                 ];
7835                 
7836                 
7837         };
7838         Roo.log('input-parentType: ' + this.parentType);
7839         
7840         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7841            cfg.cls += ' navbar-form';
7842            Roo.log(cfg);
7843         }
7844         
7845         return cfg;
7846         
7847     },
7848     /**
7849      * return the real input element.
7850      */
7851     inputEl: function ()
7852     {
7853         return this.el.select('input.form-control',true).first();
7854     },
7855     
7856     tooltipEl : function()
7857     {
7858         return this.inputEl();
7859     },
7860     
7861     setDisabled : function(v)
7862     {
7863         var i  = this.inputEl().dom;
7864         if (!v) {
7865             i.removeAttribute('disabled');
7866             return;
7867             
7868         }
7869         i.setAttribute('disabled','true');
7870     },
7871     initEvents : function()
7872     {
7873           
7874         this.inputEl().on("keydown" , this.fireKey,  this);
7875         this.inputEl().on("focus", this.onFocus,  this);
7876         this.inputEl().on("blur", this.onBlur,  this);
7877         
7878         this.inputEl().relayEvent('keyup', this);
7879
7880         // reference to original value for reset
7881         this.originalValue = this.getValue();
7882         //Roo.form.TextField.superclass.initEvents.call(this);
7883         if(this.validationEvent == 'keyup'){
7884             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7885             this.inputEl().on('keyup', this.filterValidation, this);
7886         }
7887         else if(this.validationEvent !== false){
7888             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7889         }
7890         
7891         if(this.selectOnFocus){
7892             this.on("focus", this.preFocus, this);
7893             
7894         }
7895         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7896             this.inputEl().on("keypress", this.filterKeys, this);
7897         }
7898        /* if(this.grow){
7899             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7900             this.el.on("click", this.autoSize,  this);
7901         }
7902         */
7903         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7904             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7905         }
7906         
7907         if (typeof(this.before) == 'object') {
7908             this.before.render(this.el.select('.roo-input-before',true).first());
7909         }
7910         if (typeof(this.after) == 'object') {
7911             this.after.render(this.el.select('.roo-input-after',true).first());
7912         }
7913         
7914         
7915     },
7916     filterValidation : function(e){
7917         if(!e.isNavKeyPress()){
7918             this.validationTask.delay(this.validationDelay);
7919         }
7920     },
7921      /**
7922      * Validates the field value
7923      * @return {Boolean} True if the value is valid, else false
7924      */
7925     validate : function(){
7926         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7927         if(this.disabled || this.validateValue(this.getRawValue())){
7928             this.markValid();
7929             return true;
7930         }
7931         
7932         this.markInvalid();
7933         return false;
7934     },
7935     
7936     
7937     /**
7938      * Validates a value according to the field's validation rules and marks the field as invalid
7939      * if the validation fails
7940      * @param {Mixed} value The value to validate
7941      * @return {Boolean} True if the value is valid, else false
7942      */
7943     validateValue : function(value){
7944         if(value.length < 1)  { // if it's blank
7945             if(this.allowBlank){
7946                 return true;
7947             }
7948             return false;
7949         }
7950         
7951         if(value.length < this.minLength){
7952             return false;
7953         }
7954         if(value.length > this.maxLength){
7955             return false;
7956         }
7957         if(this.vtype){
7958             var vt = Roo.form.VTypes;
7959             if(!vt[this.vtype](value, this)){
7960                 return false;
7961             }
7962         }
7963         if(typeof this.validator == "function"){
7964             var msg = this.validator(value);
7965             if(msg !== true){
7966                 return false;
7967             }
7968         }
7969         
7970         if(this.regex && !this.regex.test(value)){
7971             return false;
7972         }
7973         
7974         return true;
7975     },
7976
7977     
7978     
7979      // private
7980     fireKey : function(e){
7981         //Roo.log('field ' + e.getKey());
7982         if(e.isNavKeyPress()){
7983             this.fireEvent("specialkey", this, e);
7984         }
7985     },
7986     focus : function (selectText){
7987         if(this.rendered){
7988             this.inputEl().focus();
7989             if(selectText === true){
7990                 this.inputEl().dom.select();
7991             }
7992         }
7993         return this;
7994     } ,
7995     
7996     onFocus : function(){
7997         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7998            // this.el.addClass(this.focusClass);
7999         }
8000         if(!this.hasFocus){
8001             this.hasFocus = true;
8002             this.startValue = this.getValue();
8003             this.fireEvent("focus", this);
8004         }
8005     },
8006     
8007     beforeBlur : Roo.emptyFn,
8008
8009     
8010     // private
8011     onBlur : function(){
8012         this.beforeBlur();
8013         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8014             //this.el.removeClass(this.focusClass);
8015         }
8016         this.hasFocus = false;
8017         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8018             this.validate();
8019         }
8020         var v = this.getValue();
8021         if(String(v) !== String(this.startValue)){
8022             this.fireEvent('change', this, v, this.startValue);
8023         }
8024         this.fireEvent("blur", this);
8025     },
8026     
8027     /**
8028      * Resets the current field value to the originally loaded value and clears any validation messages
8029      */
8030     reset : function(){
8031         this.setValue(this.originalValue);
8032         this.validate();
8033     },
8034      /**
8035      * Returns the name of the field
8036      * @return {Mixed} name The name field
8037      */
8038     getName: function(){
8039         return this.name;
8040     },
8041      /**
8042      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8043      * @return {Mixed} value The field value
8044      */
8045     getValue : function(){
8046         
8047         var v = this.inputEl().getValue();
8048         
8049         return v;
8050     },
8051     /**
8052      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8053      * @return {Mixed} value The field value
8054      */
8055     getRawValue : function(){
8056         var v = this.inputEl().getValue();
8057         
8058         return v;
8059     },
8060     
8061     /**
8062      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8063      * @param {Mixed} value The value to set
8064      */
8065     setRawValue : function(v){
8066         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8067     },
8068     
8069     selectText : function(start, end){
8070         var v = this.getRawValue();
8071         if(v.length > 0){
8072             start = start === undefined ? 0 : start;
8073             end = end === undefined ? v.length : end;
8074             var d = this.inputEl().dom;
8075             if(d.setSelectionRange){
8076                 d.setSelectionRange(start, end);
8077             }else if(d.createTextRange){
8078                 var range = d.createTextRange();
8079                 range.moveStart("character", start);
8080                 range.moveEnd("character", v.length-end);
8081                 range.select();
8082             }
8083         }
8084     },
8085     
8086     /**
8087      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8088      * @param {Mixed} value The value to set
8089      */
8090     setValue : function(v){
8091         this.value = v;
8092         if(this.rendered){
8093             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8094             this.validate();
8095         }
8096     },
8097     
8098     /*
8099     processValue : function(value){
8100         if(this.stripCharsRe){
8101             var newValue = value.replace(this.stripCharsRe, '');
8102             if(newValue !== value){
8103                 this.setRawValue(newValue);
8104                 return newValue;
8105             }
8106         }
8107         return value;
8108     },
8109   */
8110     preFocus : function(){
8111         
8112         if(this.selectOnFocus){
8113             this.inputEl().dom.select();
8114         }
8115     },
8116     filterKeys : function(e){
8117         var k = e.getKey();
8118         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8119             return;
8120         }
8121         var c = e.getCharCode(), cc = String.fromCharCode(c);
8122         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8123             return;
8124         }
8125         if(!this.maskRe.test(cc)){
8126             e.stopEvent();
8127         }
8128     },
8129      /**
8130      * Clear any invalid styles/messages for this field
8131      */
8132     clearInvalid : function(){
8133         
8134         if(!this.el || this.preventMark){ // not rendered
8135             return;
8136         }
8137         this.el.removeClass(this.invalidClass);
8138         
8139         this.fireEvent('valid', this);
8140     },
8141     
8142      /**
8143      * Mark this field as valid
8144      */
8145     markValid : function(){
8146         if(!this.el  || this.preventMark){ // not rendered
8147             return;
8148         }
8149         
8150         this.el.removeClass([this.invalidClass, this.validClass]);
8151         
8152         if(this.disabled || this.allowBlank){
8153             return;
8154         }
8155         
8156         this.el.addClass(this.validClass);
8157         
8158         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && this.getValue().length){
8159             
8160             var feedback = this.el.select('.form-control-feedback', true).first();
8161             
8162             if(feedback){
8163                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8164                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8165             }
8166             
8167         }
8168         
8169         this.fireEvent('valid', this);
8170     },
8171     
8172      /**
8173      * Mark this field as invalid
8174      * @param {String} msg The validation message
8175      */
8176     markInvalid : function(msg){
8177         if(!this.el  || this.preventMark){ // not rendered
8178             return;
8179         }
8180         
8181         this.el.removeClass([this.invalidClass, this.validClass]);
8182         
8183         if(this.disabled || this.allowBlank){
8184             return;
8185         }
8186         
8187         this.el.addClass(this.invalidClass);
8188         
8189         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8190             
8191             var feedback = this.el.select('.form-control-feedback', true).first();
8192             
8193             if(feedback){
8194                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8195                 
8196                 if(this.getValue().length){
8197                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8198                 }
8199                 
8200             }
8201             
8202         }
8203         
8204         this.fireEvent('invalid', this, msg);
8205     },
8206     // private
8207     SafariOnKeyDown : function(event)
8208     {
8209         // this is a workaround for a password hang bug on chrome/ webkit.
8210         
8211         var isSelectAll = false;
8212         
8213         if(this.inputEl().dom.selectionEnd > 0){
8214             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8215         }
8216         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8217             event.preventDefault();
8218             this.setValue('');
8219             return;
8220         }
8221         
8222         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8223             
8224             event.preventDefault();
8225             // this is very hacky as keydown always get's upper case.
8226             //
8227             var cc = String.fromCharCode(event.getCharCode());
8228             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8229             
8230         }
8231     },
8232     adjustWidth : function(tag, w){
8233         tag = tag.toLowerCase();
8234         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8235             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8236                 if(tag == 'input'){
8237                     return w + 2;
8238                 }
8239                 if(tag == 'textarea'){
8240                     return w-2;
8241                 }
8242             }else if(Roo.isOpera){
8243                 if(tag == 'input'){
8244                     return w + 2;
8245                 }
8246                 if(tag == 'textarea'){
8247                     return w-2;
8248                 }
8249             }
8250         }
8251         return w;
8252     }
8253     
8254 });
8255
8256  
8257 /*
8258  * - LGPL
8259  *
8260  * Input
8261  * 
8262  */
8263
8264 /**
8265  * @class Roo.bootstrap.TextArea
8266  * @extends Roo.bootstrap.Input
8267  * Bootstrap TextArea class
8268  * @cfg {Number} cols Specifies the visible width of a text area
8269  * @cfg {Number} rows Specifies the visible number of lines in a text area
8270  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8271  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8272  * @cfg {string} html text
8273  * 
8274  * @constructor
8275  * Create a new TextArea
8276  * @param {Object} config The config object
8277  */
8278
8279 Roo.bootstrap.TextArea = function(config){
8280     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8281    
8282 };
8283
8284 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8285      
8286     cols : false,
8287     rows : 5,
8288     readOnly : false,
8289     warp : 'soft',
8290     resize : false,
8291     value: false,
8292     html: false,
8293     
8294     getAutoCreate : function(){
8295         
8296         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8297         
8298         var id = Roo.id();
8299         
8300         var cfg = {};
8301         
8302         var input =  {
8303             tag: 'textarea',
8304             id : id,
8305             warp : this.warp,
8306             rows : this.rows,
8307             value : this.value || '',
8308             html: this.html || '',
8309             cls : 'form-control',
8310             placeholder : this.placeholder || '' 
8311             
8312         };
8313         
8314         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8315             input.maxLength = this.maxLength;
8316         }
8317         
8318         if(this.resize){
8319             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8320         }
8321         
8322         if(this.cols){
8323             input.cols = this.cols;
8324         }
8325         
8326         if (this.readOnly) {
8327             input.readonly = true;
8328         }
8329         
8330         if (this.name) {
8331             input.name = this.name;
8332         }
8333         
8334         if (this.size) {
8335             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8336         }
8337         
8338         var settings=this;
8339         ['xs','sm','md','lg'].map(function(size){
8340             if (settings[size]) {
8341                 cfg.cls += ' col-' + size + '-' + settings[size];
8342             }
8343         });
8344         
8345         var inputblock = input;
8346         
8347         if(this.hasFeedback && !this.allowBlank){
8348             
8349             var feedback = {
8350                 tag: 'span',
8351                 cls: 'glyphicon form-control-feedback'
8352             };
8353
8354             inputblock = {
8355                 cls : 'has-feedback',
8356                 cn :  [
8357                     input,
8358                     feedback
8359                 ] 
8360             };  
8361         }
8362         
8363         
8364         if (this.before || this.after) {
8365             
8366             inputblock = {
8367                 cls : 'input-group',
8368                 cn :  [] 
8369             };
8370             if (this.before) {
8371                 inputblock.cn.push({
8372                     tag :'span',
8373                     cls : 'input-group-addon',
8374                     html : this.before
8375                 });
8376             }
8377             
8378             inputblock.cn.push(input);
8379             
8380             if(this.hasFeedback && !this.allowBlank){
8381                 inputblock.cls += ' has-feedback';
8382                 inputblock.cn.push(feedback);
8383             }
8384             
8385             if (this.after) {
8386                 inputblock.cn.push({
8387                     tag :'span',
8388                     cls : 'input-group-addon',
8389                     html : this.after
8390                 });
8391             }
8392             
8393         }
8394         
8395         if (align ==='left' && this.fieldLabel.length) {
8396                 Roo.log("left and has label");
8397                 cfg.cn = [
8398                     
8399                     {
8400                         tag: 'label',
8401                         'for' :  id,
8402                         cls : 'control-label col-sm-' + this.labelWidth,
8403                         html : this.fieldLabel
8404                         
8405                     },
8406                     {
8407                         cls : "col-sm-" + (12 - this.labelWidth), 
8408                         cn: [
8409                             inputblock
8410                         ]
8411                     }
8412                     
8413                 ];
8414         } else if ( this.fieldLabel.length) {
8415                 Roo.log(" label");
8416                  cfg.cn = [
8417                    
8418                     {
8419                         tag: 'label',
8420                         //cls : 'input-group-addon',
8421                         html : this.fieldLabel
8422                         
8423                     },
8424                     
8425                     inputblock
8426                     
8427                 ];
8428
8429         } else {
8430             
8431                    Roo.log(" no label && no align");
8432                 cfg.cn = [
8433                     
8434                         inputblock
8435                     
8436                 ];
8437                 
8438                 
8439         }
8440         
8441         if (this.disabled) {
8442             input.disabled=true;
8443         }
8444         
8445         return cfg;
8446         
8447     },
8448     /**
8449      * return the real textarea element.
8450      */
8451     inputEl: function ()
8452     {
8453         return this.el.select('textarea.form-control',true).first();
8454     }
8455 });
8456
8457  
8458 /*
8459  * - LGPL
8460  *
8461  * trigger field - base class for combo..
8462  * 
8463  */
8464  
8465 /**
8466  * @class Roo.bootstrap.TriggerField
8467  * @extends Roo.bootstrap.Input
8468  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8469  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8470  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8471  * for which you can provide a custom implementation.  For example:
8472  * <pre><code>
8473 var trigger = new Roo.bootstrap.TriggerField();
8474 trigger.onTriggerClick = myTriggerFn;
8475 trigger.applyTo('my-field');
8476 </code></pre>
8477  *
8478  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8479  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8480  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
8481  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8482  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8483
8484  * @constructor
8485  * Create a new TriggerField.
8486  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8487  * to the base TextField)
8488  */
8489 Roo.bootstrap.TriggerField = function(config){
8490     this.mimicing = false;
8491     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8492 };
8493
8494 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
8495     /**
8496      * @cfg {String} triggerClass A CSS class to apply to the trigger
8497      */
8498      /**
8499      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8500      */
8501     hideTrigger:false,
8502
8503     /**
8504      * @cfg {Boolean} removable (true|false) special filter default false
8505      */
8506     removable : false,
8507     
8508     /** @cfg {Boolean} grow @hide */
8509     /** @cfg {Number} growMin @hide */
8510     /** @cfg {Number} growMax @hide */
8511
8512     /**
8513      * @hide 
8514      * @method
8515      */
8516     autoSize: Roo.emptyFn,
8517     // private
8518     monitorTab : true,
8519     // private
8520     deferHeight : true,
8521
8522     
8523     actionMode : 'wrap',
8524     
8525     caret : false,
8526     
8527     
8528     getAutoCreate : function(){
8529        
8530         var align = this.labelAlign || this.parentLabelAlign();
8531         
8532         var id = Roo.id();
8533         
8534         var cfg = {
8535             cls: 'form-group' //input-group
8536         };
8537         
8538         
8539         var input =  {
8540             tag: 'input',
8541             id : id,
8542             type : this.inputType,
8543             cls : 'form-control',
8544             autocomplete: 'new-password',
8545             placeholder : this.placeholder || '' 
8546             
8547         };
8548         if (this.name) {
8549             input.name = this.name;
8550         }
8551         if (this.size) {
8552             input.cls += ' input-' + this.size;
8553         }
8554         
8555         if (this.disabled) {
8556             input.disabled=true;
8557         }
8558         
8559         var inputblock = input;
8560         
8561         if(this.hasFeedback && !this.allowBlank){
8562             
8563             var feedback = {
8564                 tag: 'span',
8565                 cls: 'glyphicon form-control-feedback'
8566             };
8567             
8568             if(this.removable && !this.editable && !this.tickable){
8569                 inputblock = {
8570                     cls : 'has-feedback',
8571                     cn :  [
8572                         inputblock,
8573                         {
8574                             tag: 'button',
8575                             html : 'x',
8576                             cls : 'roo-combo-removable-btn close'
8577                         },
8578                         feedback
8579                     ] 
8580                 };
8581             } else {
8582                 inputblock = {
8583                     cls : 'has-feedback',
8584                     cn :  [
8585                         inputblock,
8586                         feedback
8587                     ] 
8588                 };
8589             }
8590               
8591         } else {
8592             if(this.removable && !this.editable && !this.tickable){
8593                 inputblock = {
8594                     cls : 'roo-removable',
8595                     cn :  [
8596                         inputblock,
8597                         {
8598                             tag: 'button',
8599                             html : 'x',
8600                             cls : 'roo-combo-removable-btn close'
8601                         }
8602                     ] 
8603                 };
8604             }
8605         }
8606         
8607         if (this.before || this.after) {
8608             
8609             inputblock = {
8610                 cls : 'input-group',
8611                 cn :  [] 
8612             };
8613             if (this.before) {
8614                 inputblock.cn.push({
8615                     tag :'span',
8616                     cls : 'input-group-addon',
8617                     html : this.before
8618                 });
8619             }
8620             
8621             inputblock.cn.push(input);
8622             
8623             if(this.hasFeedback && !this.allowBlank){
8624                 inputblock.cls += ' has-feedback';
8625                 inputblock.cn.push(feedback);
8626             }
8627             
8628             if (this.after) {
8629                 inputblock.cn.push({
8630                     tag :'span',
8631                     cls : 'input-group-addon',
8632                     html : this.after
8633                 });
8634             }
8635             
8636         };
8637         
8638         var box = {
8639             tag: 'div',
8640             cn: [
8641                 {
8642                     tag: 'input',
8643                     type : 'hidden',
8644                     cls: 'form-hidden-field'
8645                 },
8646                 inputblock
8647             ]
8648             
8649         };
8650         
8651         if(this.multiple){
8652             Roo.log('multiple');
8653             
8654             box = {
8655                 tag: 'div',
8656                 cn: [
8657                     {
8658                         tag: 'input',
8659                         type : 'hidden',
8660                         cls: 'form-hidden-field'
8661                     },
8662                     {
8663                         tag: 'ul',
8664                         cls: 'select2-choices',
8665                         cn:[
8666                             {
8667                                 tag: 'li',
8668                                 cls: 'select2-search-field',
8669                                 cn: [
8670
8671                                     inputblock
8672                                 ]
8673                             }
8674                         ]
8675                     }
8676                 ]
8677             }
8678         };
8679         
8680         var combobox = {
8681             cls: 'select2-container input-group',
8682             cn: [
8683                 box
8684 //                {
8685 //                    tag: 'ul',
8686 //                    cls: 'typeahead typeahead-long dropdown-menu',
8687 //                    style: 'display:none'
8688 //                }
8689             ]
8690         };
8691         
8692         if(!this.multiple && this.showToggleBtn){
8693             
8694             var caret = {
8695                         tag: 'span',
8696                         cls: 'caret'
8697              };
8698             if (this.caret != false) {
8699                 caret = {
8700                      tag: 'i',
8701                      cls: 'fa fa-' + this.caret
8702                 };
8703                 
8704             }
8705             
8706             combobox.cn.push({
8707                 tag :'span',
8708                 cls : 'input-group-addon btn dropdown-toggle',
8709                 cn : [
8710                     caret,
8711                     {
8712                         tag: 'span',
8713                         cls: 'combobox-clear',
8714                         cn  : [
8715                             {
8716                                 tag : 'i',
8717                                 cls: 'icon-remove'
8718                             }
8719                         ]
8720                     }
8721                 ]
8722
8723             })
8724         }
8725         
8726         if(this.multiple){
8727             combobox.cls += ' select2-container-multi';
8728         }
8729         
8730         if (align ==='left' && this.fieldLabel.length) {
8731             
8732                 Roo.log("left and has label");
8733                 cfg.cn = [
8734                     
8735                     {
8736                         tag: 'label',
8737                         'for' :  id,
8738                         cls : 'control-label col-sm-' + this.labelWidth,
8739                         html : this.fieldLabel
8740                         
8741                     },
8742                     {
8743                         cls : "col-sm-" + (12 - this.labelWidth), 
8744                         cn: [
8745                             combobox
8746                         ]
8747                     }
8748                     
8749                 ];
8750         } else if ( this.fieldLabel.length) {
8751                 Roo.log(" label");
8752                  cfg.cn = [
8753                    
8754                     {
8755                         tag: 'label',
8756                         //cls : 'input-group-addon',
8757                         html : this.fieldLabel
8758                         
8759                     },
8760                     
8761                     combobox
8762                     
8763                 ];
8764
8765         } else {
8766             
8767                 Roo.log(" no label && no align");
8768                 cfg = combobox
8769                      
8770                 
8771         }
8772          
8773         var settings=this;
8774         ['xs','sm','md','lg'].map(function(size){
8775             if (settings[size]) {
8776                 cfg.cls += ' col-' + size + '-' + settings[size];
8777             }
8778         });
8779         
8780         return cfg;
8781         
8782     },
8783     
8784     
8785     
8786     // private
8787     onResize : function(w, h){
8788 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8789 //        if(typeof w == 'number'){
8790 //            var x = w - this.trigger.getWidth();
8791 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8792 //            this.trigger.setStyle('left', x+'px');
8793 //        }
8794     },
8795
8796     // private
8797     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8798
8799     // private
8800     getResizeEl : function(){
8801         return this.inputEl();
8802     },
8803
8804     // private
8805     getPositionEl : function(){
8806         return this.inputEl();
8807     },
8808
8809     // private
8810     alignErrorIcon : function(){
8811         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8812     },
8813
8814     // private
8815     initEvents : function(){
8816         
8817         this.createList();
8818         
8819         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8820         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8821         if(!this.multiple && this.showToggleBtn){
8822             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8823             if(this.hideTrigger){
8824                 this.trigger.setDisplayed(false);
8825             }
8826             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8827         }
8828         
8829         if(this.multiple){
8830             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8831         }
8832         
8833         if(this.removable && !this.editable && !this.tickable){
8834             var close = this.closeTriggerEl();
8835             
8836             if(close){
8837                 close.setVisibilityMode(Roo.Element.DISPALY).hide();
8838                 close.on('click', this.removeBtnClick, this, close);
8839             }
8840         }
8841         
8842         //this.trigger.addClassOnOver('x-form-trigger-over');
8843         //this.trigger.addClassOnClick('x-form-trigger-click');
8844         
8845         //if(!this.width){
8846         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8847         //}
8848     },
8849     
8850     closeTriggerEl : function()
8851     {
8852         var close = this.el.select('.roo-combo-removable-btn', true).first();
8853         return close ? close : false;
8854     },
8855     
8856     removeBtnClick : function(e, h, el)
8857     {
8858         e.preventDefault();
8859         
8860         this.fireEvent("remove", this);
8861     },
8862     
8863     createList : function()
8864     {
8865         this.list = Roo.get(document.body).createChild({
8866             tag: 'ul',
8867             cls: 'typeahead typeahead-long dropdown-menu',
8868             style: 'display:none'
8869         });
8870         
8871         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8872         
8873     },
8874
8875     // private
8876     initTrigger : function(){
8877        
8878     },
8879
8880     // private
8881     onDestroy : function(){
8882         if(this.trigger){
8883             this.trigger.removeAllListeners();
8884           //  this.trigger.remove();
8885         }
8886         //if(this.wrap){
8887         //    this.wrap.remove();
8888         //}
8889         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8890     },
8891
8892     // private
8893     onFocus : function(){
8894         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8895         /*
8896         if(!this.mimicing){
8897             this.wrap.addClass('x-trigger-wrap-focus');
8898             this.mimicing = true;
8899             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8900             if(this.monitorTab){
8901                 this.el.on("keydown", this.checkTab, this);
8902             }
8903         }
8904         */
8905     },
8906
8907     // private
8908     checkTab : function(e){
8909         if(e.getKey() == e.TAB){
8910             this.triggerBlur();
8911         }
8912     },
8913
8914     // private
8915     onBlur : function(){
8916         // do nothing
8917     },
8918
8919     // private
8920     mimicBlur : function(e, t){
8921         /*
8922         if(!this.wrap.contains(t) && this.validateBlur()){
8923             this.triggerBlur();
8924         }
8925         */
8926     },
8927
8928     // private
8929     triggerBlur : function(){
8930         this.mimicing = false;
8931         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8932         if(this.monitorTab){
8933             this.el.un("keydown", this.checkTab, this);
8934         }
8935         //this.wrap.removeClass('x-trigger-wrap-focus');
8936         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8937     },
8938
8939     // private
8940     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8941     validateBlur : function(e, t){
8942         return true;
8943     },
8944
8945     // private
8946     onDisable : function(){
8947         this.inputEl().dom.disabled = true;
8948         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8949         //if(this.wrap){
8950         //    this.wrap.addClass('x-item-disabled');
8951         //}
8952     },
8953
8954     // private
8955     onEnable : function(){
8956         this.inputEl().dom.disabled = false;
8957         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8958         //if(this.wrap){
8959         //    this.el.removeClass('x-item-disabled');
8960         //}
8961     },
8962
8963     // private
8964     onShow : function(){
8965         var ae = this.getActionEl();
8966         
8967         if(ae){
8968             ae.dom.style.display = '';
8969             ae.dom.style.visibility = 'visible';
8970         }
8971     },
8972
8973     // private
8974     
8975     onHide : function(){
8976         var ae = this.getActionEl();
8977         ae.dom.style.display = 'none';
8978     },
8979
8980     /**
8981      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8982      * by an implementing function.
8983      * @method
8984      * @param {EventObject} e
8985      */
8986     onTriggerClick : Roo.emptyFn
8987 });
8988  /*
8989  * Based on:
8990  * Ext JS Library 1.1.1
8991  * Copyright(c) 2006-2007, Ext JS, LLC.
8992  *
8993  * Originally Released Under LGPL - original licence link has changed is not relivant.
8994  *
8995  * Fork - LGPL
8996  * <script type="text/javascript">
8997  */
8998
8999
9000 /**
9001  * @class Roo.data.SortTypes
9002  * @singleton
9003  * Defines the default sorting (casting?) comparison functions used when sorting data.
9004  */
9005 Roo.data.SortTypes = {
9006     /**
9007      * Default sort that does nothing
9008      * @param {Mixed} s The value being converted
9009      * @return {Mixed} The comparison value
9010      */
9011     none : function(s){
9012         return s;
9013     },
9014     
9015     /**
9016      * The regular expression used to strip tags
9017      * @type {RegExp}
9018      * @property
9019      */
9020     stripTagsRE : /<\/?[^>]+>/gi,
9021     
9022     /**
9023      * Strips all HTML tags to sort on text only
9024      * @param {Mixed} s The value being converted
9025      * @return {String} The comparison value
9026      */
9027     asText : function(s){
9028         return String(s).replace(this.stripTagsRE, "");
9029     },
9030     
9031     /**
9032      * Strips all HTML tags to sort on text only - Case insensitive
9033      * @param {Mixed} s The value being converted
9034      * @return {String} The comparison value
9035      */
9036     asUCText : function(s){
9037         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9038     },
9039     
9040     /**
9041      * Case insensitive string
9042      * @param {Mixed} s The value being converted
9043      * @return {String} The comparison value
9044      */
9045     asUCString : function(s) {
9046         return String(s).toUpperCase();
9047     },
9048     
9049     /**
9050      * Date sorting
9051      * @param {Mixed} s The value being converted
9052      * @return {Number} The comparison value
9053      */
9054     asDate : function(s) {
9055         if(!s){
9056             return 0;
9057         }
9058         if(s instanceof Date){
9059             return s.getTime();
9060         }
9061         return Date.parse(String(s));
9062     },
9063     
9064     /**
9065      * Float sorting
9066      * @param {Mixed} s The value being converted
9067      * @return {Float} The comparison value
9068      */
9069     asFloat : function(s) {
9070         var val = parseFloat(String(s).replace(/,/g, ""));
9071         if(isNaN(val)) val = 0;
9072         return val;
9073     },
9074     
9075     /**
9076      * Integer sorting
9077      * @param {Mixed} s The value being converted
9078      * @return {Number} The comparison value
9079      */
9080     asInt : function(s) {
9081         var val = parseInt(String(s).replace(/,/g, ""));
9082         if(isNaN(val)) val = 0;
9083         return val;
9084     }
9085 };/*
9086  * Based on:
9087  * Ext JS Library 1.1.1
9088  * Copyright(c) 2006-2007, Ext JS, LLC.
9089  *
9090  * Originally Released Under LGPL - original licence link has changed is not relivant.
9091  *
9092  * Fork - LGPL
9093  * <script type="text/javascript">
9094  */
9095
9096 /**
9097 * @class Roo.data.Record
9098  * Instances of this class encapsulate both record <em>definition</em> information, and record
9099  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9100  * to access Records cached in an {@link Roo.data.Store} object.<br>
9101  * <p>
9102  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9103  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9104  * objects.<br>
9105  * <p>
9106  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9107  * @constructor
9108  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9109  * {@link #create}. The parameters are the same.
9110  * @param {Array} data An associative Array of data values keyed by the field name.
9111  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9112  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9113  * not specified an integer id is generated.
9114  */
9115 Roo.data.Record = function(data, id){
9116     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9117     this.data = data;
9118 };
9119
9120 /**
9121  * Generate a constructor for a specific record layout.
9122  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9123  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9124  * Each field definition object may contain the following properties: <ul>
9125  * <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,
9126  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9127  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9128  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9129  * is being used, then this is a string containing the javascript expression to reference the data relative to 
9130  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9131  * to the data item relative to the record element. If the mapping expression is the same as the field name,
9132  * this may be omitted.</p></li>
9133  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9134  * <ul><li>auto (Default, implies no conversion)</li>
9135  * <li>string</li>
9136  * <li>int</li>
9137  * <li>float</li>
9138  * <li>boolean</li>
9139  * <li>date</li></ul></p></li>
9140  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9141  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9142  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9143  * by the Reader into an object that will be stored in the Record. It is passed the
9144  * following parameters:<ul>
9145  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9146  * </ul></p></li>
9147  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9148  * </ul>
9149  * <br>usage:<br><pre><code>
9150 var TopicRecord = Roo.data.Record.create(
9151     {name: 'title', mapping: 'topic_title'},
9152     {name: 'author', mapping: 'username'},
9153     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9154     {name: 'lastPost', mapping: 'post_time', type: 'date'},
9155     {name: 'lastPoster', mapping: 'user2'},
9156     {name: 'excerpt', mapping: 'post_text'}
9157 );
9158
9159 var myNewRecord = new TopicRecord({
9160     title: 'Do my job please',
9161     author: 'noobie',
9162     totalPosts: 1,
9163     lastPost: new Date(),
9164     lastPoster: 'Animal',
9165     excerpt: 'No way dude!'
9166 });
9167 myStore.add(myNewRecord);
9168 </code></pre>
9169  * @method create
9170  * @static
9171  */
9172 Roo.data.Record.create = function(o){
9173     var f = function(){
9174         f.superclass.constructor.apply(this, arguments);
9175     };
9176     Roo.extend(f, Roo.data.Record);
9177     var p = f.prototype;
9178     p.fields = new Roo.util.MixedCollection(false, function(field){
9179         return field.name;
9180     });
9181     for(var i = 0, len = o.length; i < len; i++){
9182         p.fields.add(new Roo.data.Field(o[i]));
9183     }
9184     f.getField = function(name){
9185         return p.fields.get(name);  
9186     };
9187     return f;
9188 };
9189
9190 Roo.data.Record.AUTO_ID = 1000;
9191 Roo.data.Record.EDIT = 'edit';
9192 Roo.data.Record.REJECT = 'reject';
9193 Roo.data.Record.COMMIT = 'commit';
9194
9195 Roo.data.Record.prototype = {
9196     /**
9197      * Readonly flag - true if this record has been modified.
9198      * @type Boolean
9199      */
9200     dirty : false,
9201     editing : false,
9202     error: null,
9203     modified: null,
9204
9205     // private
9206     join : function(store){
9207         this.store = store;
9208     },
9209
9210     /**
9211      * Set the named field to the specified value.
9212      * @param {String} name The name of the field to set.
9213      * @param {Object} value The value to set the field to.
9214      */
9215     set : function(name, value){
9216         if(this.data[name] == value){
9217             return;
9218         }
9219         this.dirty = true;
9220         if(!this.modified){
9221             this.modified = {};
9222         }
9223         if(typeof this.modified[name] == 'undefined'){
9224             this.modified[name] = this.data[name];
9225         }
9226         this.data[name] = value;
9227         if(!this.editing && this.store){
9228             this.store.afterEdit(this);
9229         }       
9230     },
9231
9232     /**
9233      * Get the value of the named field.
9234      * @param {String} name The name of the field to get the value of.
9235      * @return {Object} The value of the field.
9236      */
9237     get : function(name){
9238         return this.data[name]; 
9239     },
9240
9241     // private
9242     beginEdit : function(){
9243         this.editing = true;
9244         this.modified = {}; 
9245     },
9246
9247     // private
9248     cancelEdit : function(){
9249         this.editing = false;
9250         delete this.modified;
9251     },
9252
9253     // private
9254     endEdit : function(){
9255         this.editing = false;
9256         if(this.dirty && this.store){
9257             this.store.afterEdit(this);
9258         }
9259     },
9260
9261     /**
9262      * Usually called by the {@link Roo.data.Store} which owns the Record.
9263      * Rejects all changes made to the Record since either creation, or the last commit operation.
9264      * Modified fields are reverted to their original values.
9265      * <p>
9266      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9267      * of reject operations.
9268      */
9269     reject : function(){
9270         var m = this.modified;
9271         for(var n in m){
9272             if(typeof m[n] != "function"){
9273                 this.data[n] = m[n];
9274             }
9275         }
9276         this.dirty = false;
9277         delete this.modified;
9278         this.editing = false;
9279         if(this.store){
9280             this.store.afterReject(this);
9281         }
9282     },
9283
9284     /**
9285      * Usually called by the {@link Roo.data.Store} which owns the Record.
9286      * Commits all changes made to the Record since either creation, or the last commit operation.
9287      * <p>
9288      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9289      * of commit operations.
9290      */
9291     commit : function(){
9292         this.dirty = false;
9293         delete this.modified;
9294         this.editing = false;
9295         if(this.store){
9296             this.store.afterCommit(this);
9297         }
9298     },
9299
9300     // private
9301     hasError : function(){
9302         return this.error != null;
9303     },
9304
9305     // private
9306     clearError : function(){
9307         this.error = null;
9308     },
9309
9310     /**
9311      * Creates a copy of this record.
9312      * @param {String} id (optional) A new record id if you don't want to use this record's id
9313      * @return {Record}
9314      */
9315     copy : function(newId) {
9316         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9317     }
9318 };/*
9319  * Based on:
9320  * Ext JS Library 1.1.1
9321  * Copyright(c) 2006-2007, Ext JS, LLC.
9322  *
9323  * Originally Released Under LGPL - original licence link has changed is not relivant.
9324  *
9325  * Fork - LGPL
9326  * <script type="text/javascript">
9327  */
9328
9329
9330
9331 /**
9332  * @class Roo.data.Store
9333  * @extends Roo.util.Observable
9334  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9335  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9336  * <p>
9337  * 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
9338  * has no knowledge of the format of the data returned by the Proxy.<br>
9339  * <p>
9340  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9341  * instances from the data object. These records are cached and made available through accessor functions.
9342  * @constructor
9343  * Creates a new Store.
9344  * @param {Object} config A config object containing the objects needed for the Store to access data,
9345  * and read the data into Records.
9346  */
9347 Roo.data.Store = function(config){
9348     this.data = new Roo.util.MixedCollection(false);
9349     this.data.getKey = function(o){
9350         return o.id;
9351     };
9352     this.baseParams = {};
9353     // private
9354     this.paramNames = {
9355         "start" : "start",
9356         "limit" : "limit",
9357         "sort" : "sort",
9358         "dir" : "dir",
9359         "multisort" : "_multisort"
9360     };
9361
9362     if(config && config.data){
9363         this.inlineData = config.data;
9364         delete config.data;
9365     }
9366
9367     Roo.apply(this, config);
9368     
9369     if(this.reader){ // reader passed
9370         this.reader = Roo.factory(this.reader, Roo.data);
9371         this.reader.xmodule = this.xmodule || false;
9372         if(!this.recordType){
9373             this.recordType = this.reader.recordType;
9374         }
9375         if(this.reader.onMetaChange){
9376             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9377         }
9378     }
9379
9380     if(this.recordType){
9381         this.fields = this.recordType.prototype.fields;
9382     }
9383     this.modified = [];
9384
9385     this.addEvents({
9386         /**
9387          * @event datachanged
9388          * Fires when the data cache has changed, and a widget which is using this Store
9389          * as a Record cache should refresh its view.
9390          * @param {Store} this
9391          */
9392         datachanged : true,
9393         /**
9394          * @event metachange
9395          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9396          * @param {Store} this
9397          * @param {Object} meta The JSON metadata
9398          */
9399         metachange : true,
9400         /**
9401          * @event add
9402          * Fires when Records have been added to the Store
9403          * @param {Store} this
9404          * @param {Roo.data.Record[]} records The array of Records added
9405          * @param {Number} index The index at which the record(s) were added
9406          */
9407         add : true,
9408         /**
9409          * @event remove
9410          * Fires when a Record has been removed from the Store
9411          * @param {Store} this
9412          * @param {Roo.data.Record} record The Record that was removed
9413          * @param {Number} index The index at which the record was removed
9414          */
9415         remove : true,
9416         /**
9417          * @event update
9418          * Fires when a Record has been updated
9419          * @param {Store} this
9420          * @param {Roo.data.Record} record The Record that was updated
9421          * @param {String} operation The update operation being performed.  Value may be one of:
9422          * <pre><code>
9423  Roo.data.Record.EDIT
9424  Roo.data.Record.REJECT
9425  Roo.data.Record.COMMIT
9426          * </code></pre>
9427          */
9428         update : true,
9429         /**
9430          * @event clear
9431          * Fires when the data cache has been cleared.
9432          * @param {Store} this
9433          */
9434         clear : true,
9435         /**
9436          * @event beforeload
9437          * Fires before a request is made for a new data object.  If the beforeload handler returns false
9438          * the load action will be canceled.
9439          * @param {Store} this
9440          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9441          */
9442         beforeload : true,
9443         /**
9444          * @event beforeloadadd
9445          * Fires after a new set of Records has been loaded.
9446          * @param {Store} this
9447          * @param {Roo.data.Record[]} records The Records that were loaded
9448          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9449          */
9450         beforeloadadd : true,
9451         /**
9452          * @event load
9453          * Fires after a new set of Records has been loaded, before they are added to the store.
9454          * @param {Store} this
9455          * @param {Roo.data.Record[]} records The Records that were loaded
9456          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9457          * @params {Object} return from reader
9458          */
9459         load : true,
9460         /**
9461          * @event loadexception
9462          * Fires if an exception occurs in the Proxy during loading.
9463          * Called with the signature of the Proxy's "loadexception" event.
9464          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9465          * 
9466          * @param {Proxy} 
9467          * @param {Object} return from JsonData.reader() - success, totalRecords, records
9468          * @param {Object} load options 
9469          * @param {Object} jsonData from your request (normally this contains the Exception)
9470          */
9471         loadexception : true
9472     });
9473     
9474     if(this.proxy){
9475         this.proxy = Roo.factory(this.proxy, Roo.data);
9476         this.proxy.xmodule = this.xmodule || false;
9477         this.relayEvents(this.proxy,  ["loadexception"]);
9478     }
9479     this.sortToggle = {};
9480     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9481
9482     Roo.data.Store.superclass.constructor.call(this);
9483
9484     if(this.inlineData){
9485         this.loadData(this.inlineData);
9486         delete this.inlineData;
9487     }
9488 };
9489
9490 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9491      /**
9492     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
9493     * without a remote query - used by combo/forms at present.
9494     */
9495     
9496     /**
9497     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9498     */
9499     /**
9500     * @cfg {Array} data Inline data to be loaded when the store is initialized.
9501     */
9502     /**
9503     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9504     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9505     */
9506     /**
9507     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9508     * on any HTTP request
9509     */
9510     /**
9511     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9512     */
9513     /**
9514     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9515     */
9516     multiSort: false,
9517     /**
9518     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9519     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9520     */
9521     remoteSort : false,
9522
9523     /**
9524     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9525      * loaded or when a record is removed. (defaults to false).
9526     */
9527     pruneModifiedRecords : false,
9528
9529     // private
9530     lastOptions : null,
9531
9532     /**
9533      * Add Records to the Store and fires the add event.
9534      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9535      */
9536     add : function(records){
9537         records = [].concat(records);
9538         for(var i = 0, len = records.length; i < len; i++){
9539             records[i].join(this);
9540         }
9541         var index = this.data.length;
9542         this.data.addAll(records);
9543         this.fireEvent("add", this, records, index);
9544     },
9545
9546     /**
9547      * Remove a Record from the Store and fires the remove event.
9548      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9549      */
9550     remove : function(record){
9551         var index = this.data.indexOf(record);
9552         this.data.removeAt(index);
9553         if(this.pruneModifiedRecords){
9554             this.modified.remove(record);
9555         }
9556         this.fireEvent("remove", this, record, index);
9557     },
9558
9559     /**
9560      * Remove all Records from the Store and fires the clear event.
9561      */
9562     removeAll : function(){
9563         this.data.clear();
9564         if(this.pruneModifiedRecords){
9565             this.modified = [];
9566         }
9567         this.fireEvent("clear", this);
9568     },
9569
9570     /**
9571      * Inserts Records to the Store at the given index and fires the add event.
9572      * @param {Number} index The start index at which to insert the passed Records.
9573      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9574      */
9575     insert : function(index, records){
9576         records = [].concat(records);
9577         for(var i = 0, len = records.length; i < len; i++){
9578             this.data.insert(index, records[i]);
9579             records[i].join(this);
9580         }
9581         this.fireEvent("add", this, records, index);
9582     },
9583
9584     /**
9585      * Get the index within the cache of the passed Record.
9586      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9587      * @return {Number} The index of the passed Record. Returns -1 if not found.
9588      */
9589     indexOf : function(record){
9590         return this.data.indexOf(record);
9591     },
9592
9593     /**
9594      * Get the index within the cache of the Record with the passed id.
9595      * @param {String} id The id of the Record to find.
9596      * @return {Number} The index of the Record. Returns -1 if not found.
9597      */
9598     indexOfId : function(id){
9599         return this.data.indexOfKey(id);
9600     },
9601
9602     /**
9603      * Get the Record with the specified id.
9604      * @param {String} id The id of the Record to find.
9605      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9606      */
9607     getById : function(id){
9608         return this.data.key(id);
9609     },
9610
9611     /**
9612      * Get the Record at the specified index.
9613      * @param {Number} index The index of the Record to find.
9614      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9615      */
9616     getAt : function(index){
9617         return this.data.itemAt(index);
9618     },
9619
9620     /**
9621      * Returns a range of Records between specified indices.
9622      * @param {Number} startIndex (optional) The starting index (defaults to 0)
9623      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9624      * @return {Roo.data.Record[]} An array of Records
9625      */
9626     getRange : function(start, end){
9627         return this.data.getRange(start, end);
9628     },
9629
9630     // private
9631     storeOptions : function(o){
9632         o = Roo.apply({}, o);
9633         delete o.callback;
9634         delete o.scope;
9635         this.lastOptions = o;
9636     },
9637
9638     /**
9639      * Loads the Record cache from the configured Proxy using the configured Reader.
9640      * <p>
9641      * If using remote paging, then the first load call must specify the <em>start</em>
9642      * and <em>limit</em> properties in the options.params property to establish the initial
9643      * position within the dataset, and the number of Records to cache on each read from the Proxy.
9644      * <p>
9645      * <strong>It is important to note that for remote data sources, loading is asynchronous,
9646      * and this call will return before the new data has been loaded. Perform any post-processing
9647      * in a callback function, or in a "load" event handler.</strong>
9648      * <p>
9649      * @param {Object} options An object containing properties which control loading options:<ul>
9650      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9651      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9652      * passed the following arguments:<ul>
9653      * <li>r : Roo.data.Record[]</li>
9654      * <li>options: Options object from the load call</li>
9655      * <li>success: Boolean success indicator</li></ul></li>
9656      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9657      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9658      * </ul>
9659      */
9660     load : function(options){
9661         options = options || {};
9662         if(this.fireEvent("beforeload", this, options) !== false){
9663             this.storeOptions(options);
9664             var p = Roo.apply(options.params || {}, this.baseParams);
9665             // if meta was not loaded from remote source.. try requesting it.
9666             if (!this.reader.metaFromRemote) {
9667                 p._requestMeta = 1;
9668             }
9669             if(this.sortInfo && this.remoteSort){
9670                 var pn = this.paramNames;
9671                 p[pn["sort"]] = this.sortInfo.field;
9672                 p[pn["dir"]] = this.sortInfo.direction;
9673             }
9674             if (this.multiSort) {
9675                 var pn = this.paramNames;
9676                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9677             }
9678             
9679             this.proxy.load(p, this.reader, this.loadRecords, this, options);
9680         }
9681     },
9682
9683     /**
9684      * Reloads the Record cache from the configured Proxy using the configured Reader and
9685      * the options from the last load operation performed.
9686      * @param {Object} options (optional) An object containing properties which may override the options
9687      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9688      * the most recently used options are reused).
9689      */
9690     reload : function(options){
9691         this.load(Roo.applyIf(options||{}, this.lastOptions));
9692     },
9693
9694     // private
9695     // Called as a callback by the Reader during a load operation.
9696     loadRecords : function(o, options, success){
9697         if(!o || success === false){
9698             if(success !== false){
9699                 this.fireEvent("load", this, [], options, o);
9700             }
9701             if(options.callback){
9702                 options.callback.call(options.scope || this, [], options, false);
9703             }
9704             return;
9705         }
9706         // if data returned failure - throw an exception.
9707         if (o.success === false) {
9708             // show a message if no listener is registered.
9709             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9710                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9711             }
9712             // loadmask wil be hooked into this..
9713             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9714             return;
9715         }
9716         var r = o.records, t = o.totalRecords || r.length;
9717         
9718         this.fireEvent("beforeloadadd", this, r, options, o);
9719         
9720         if(!options || options.add !== true){
9721             if(this.pruneModifiedRecords){
9722                 this.modified = [];
9723             }
9724             for(var i = 0, len = r.length; i < len; i++){
9725                 r[i].join(this);
9726             }
9727             if(this.snapshot){
9728                 this.data = this.snapshot;
9729                 delete this.snapshot;
9730             }
9731             this.data.clear();
9732             this.data.addAll(r);
9733             this.totalLength = t;
9734             this.applySort();
9735             this.fireEvent("datachanged", this);
9736         }else{
9737             this.totalLength = Math.max(t, this.data.length+r.length);
9738             this.add(r);
9739         }
9740         this.fireEvent("load", this, r, options, o);
9741         if(options.callback){
9742             options.callback.call(options.scope || this, r, options, true);
9743         }
9744     },
9745
9746
9747     /**
9748      * Loads data from a passed data block. A Reader which understands the format of the data
9749      * must have been configured in the constructor.
9750      * @param {Object} data The data block from which to read the Records.  The format of the data expected
9751      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9752      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9753      */
9754     loadData : function(o, append){
9755         var r = this.reader.readRecords(o);
9756         this.loadRecords(r, {add: append}, true);
9757     },
9758
9759     /**
9760      * Gets the number of cached records.
9761      * <p>
9762      * <em>If using paging, this may not be the total size of the dataset. If the data object
9763      * used by the Reader contains the dataset size, then the getTotalCount() function returns
9764      * the data set size</em>
9765      */
9766     getCount : function(){
9767         return this.data.length || 0;
9768     },
9769
9770     /**
9771      * Gets the total number of records in the dataset as returned by the server.
9772      * <p>
9773      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9774      * the dataset size</em>
9775      */
9776     getTotalCount : function(){
9777         return this.totalLength || 0;
9778     },
9779
9780     /**
9781      * Returns the sort state of the Store as an object with two properties:
9782      * <pre><code>
9783  field {String} The name of the field by which the Records are sorted
9784  direction {String} The sort order, "ASC" or "DESC"
9785      * </code></pre>
9786      */
9787     getSortState : function(){
9788         return this.sortInfo;
9789     },
9790
9791     // private
9792     applySort : function(){
9793         if(this.sortInfo && !this.remoteSort){
9794             var s = this.sortInfo, f = s.field;
9795             var st = this.fields.get(f).sortType;
9796             var fn = function(r1, r2){
9797                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9798                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9799             };
9800             this.data.sort(s.direction, fn);
9801             if(this.snapshot && this.snapshot != this.data){
9802                 this.snapshot.sort(s.direction, fn);
9803             }
9804         }
9805     },
9806
9807     /**
9808      * Sets the default sort column and order to be used by the next load operation.
9809      * @param {String} fieldName The name of the field to sort by.
9810      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9811      */
9812     setDefaultSort : function(field, dir){
9813         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9814     },
9815
9816     /**
9817      * Sort the Records.
9818      * If remote sorting is used, the sort is performed on the server, and the cache is
9819      * reloaded. If local sorting is used, the cache is sorted internally.
9820      * @param {String} fieldName The name of the field to sort by.
9821      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9822      */
9823     sort : function(fieldName, dir){
9824         var f = this.fields.get(fieldName);
9825         if(!dir){
9826             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9827             
9828             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9829                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9830             }else{
9831                 dir = f.sortDir;
9832             }
9833         }
9834         this.sortToggle[f.name] = dir;
9835         this.sortInfo = {field: f.name, direction: dir};
9836         if(!this.remoteSort){
9837             this.applySort();
9838             this.fireEvent("datachanged", this);
9839         }else{
9840             this.load(this.lastOptions);
9841         }
9842     },
9843
9844     /**
9845      * Calls the specified function for each of the Records in the cache.
9846      * @param {Function} fn The function to call. The Record is passed as the first parameter.
9847      * Returning <em>false</em> aborts and exits the iteration.
9848      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9849      */
9850     each : function(fn, scope){
9851         this.data.each(fn, scope);
9852     },
9853
9854     /**
9855      * Gets all records modified since the last commit.  Modified records are persisted across load operations
9856      * (e.g., during paging).
9857      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9858      */
9859     getModifiedRecords : function(){
9860         return this.modified;
9861     },
9862
9863     // private
9864     createFilterFn : function(property, value, anyMatch){
9865         if(!value.exec){ // not a regex
9866             value = String(value);
9867             if(value.length == 0){
9868                 return false;
9869             }
9870             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9871         }
9872         return function(r){
9873             return value.test(r.data[property]);
9874         };
9875     },
9876
9877     /**
9878      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9879      * @param {String} property A field on your records
9880      * @param {Number} start The record index to start at (defaults to 0)
9881      * @param {Number} end The last record index to include (defaults to length - 1)
9882      * @return {Number} The sum
9883      */
9884     sum : function(property, start, end){
9885         var rs = this.data.items, v = 0;
9886         start = start || 0;
9887         end = (end || end === 0) ? end : rs.length-1;
9888
9889         for(var i = start; i <= end; i++){
9890             v += (rs[i].data[property] || 0);
9891         }
9892         return v;
9893     },
9894
9895     /**
9896      * Filter the records by a specified property.
9897      * @param {String} field A field on your records
9898      * @param {String/RegExp} value Either a string that the field
9899      * should start with or a RegExp to test against the field
9900      * @param {Boolean} anyMatch True to match any part not just the beginning
9901      */
9902     filter : function(property, value, anyMatch){
9903         var fn = this.createFilterFn(property, value, anyMatch);
9904         return fn ? this.filterBy(fn) : this.clearFilter();
9905     },
9906
9907     /**
9908      * Filter by a function. The specified function will be called with each
9909      * record in this data source. If the function returns true the record is included,
9910      * otherwise it is filtered.
9911      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9912      * @param {Object} scope (optional) The scope of the function (defaults to this)
9913      */
9914     filterBy : function(fn, scope){
9915         this.snapshot = this.snapshot || this.data;
9916         this.data = this.queryBy(fn, scope||this);
9917         this.fireEvent("datachanged", this);
9918     },
9919
9920     /**
9921      * Query the records by a specified property.
9922      * @param {String} field A field on your records
9923      * @param {String/RegExp} value Either a string that the field
9924      * should start with or a RegExp to test against the field
9925      * @param {Boolean} anyMatch True to match any part not just the beginning
9926      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9927      */
9928     query : function(property, value, anyMatch){
9929         var fn = this.createFilterFn(property, value, anyMatch);
9930         return fn ? this.queryBy(fn) : this.data.clone();
9931     },
9932
9933     /**
9934      * Query by a function. The specified function will be called with each
9935      * record in this data source. If the function returns true the record is included
9936      * in the results.
9937      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9938      * @param {Object} scope (optional) The scope of the function (defaults to this)
9939       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9940      **/
9941     queryBy : function(fn, scope){
9942         var data = this.snapshot || this.data;
9943         return data.filterBy(fn, scope||this);
9944     },
9945
9946     /**
9947      * Collects unique values for a particular dataIndex from this store.
9948      * @param {String} dataIndex The property to collect
9949      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9950      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9951      * @return {Array} An array of the unique values
9952      **/
9953     collect : function(dataIndex, allowNull, bypassFilter){
9954         var d = (bypassFilter === true && this.snapshot) ?
9955                 this.snapshot.items : this.data.items;
9956         var v, sv, r = [], l = {};
9957         for(var i = 0, len = d.length; i < len; i++){
9958             v = d[i].data[dataIndex];
9959             sv = String(v);
9960             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9961                 l[sv] = true;
9962                 r[r.length] = v;
9963             }
9964         }
9965         return r;
9966     },
9967
9968     /**
9969      * Revert to a view of the Record cache with no filtering applied.
9970      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9971      */
9972     clearFilter : function(suppressEvent){
9973         if(this.snapshot && this.snapshot != this.data){
9974             this.data = this.snapshot;
9975             delete this.snapshot;
9976             if(suppressEvent !== true){
9977                 this.fireEvent("datachanged", this);
9978             }
9979         }
9980     },
9981
9982     // private
9983     afterEdit : function(record){
9984         if(this.modified.indexOf(record) == -1){
9985             this.modified.push(record);
9986         }
9987         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9988     },
9989     
9990     // private
9991     afterReject : function(record){
9992         this.modified.remove(record);
9993         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9994     },
9995
9996     // private
9997     afterCommit : function(record){
9998         this.modified.remove(record);
9999         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10000     },
10001
10002     /**
10003      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10004      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10005      */
10006     commitChanges : function(){
10007         var m = this.modified.slice(0);
10008         this.modified = [];
10009         for(var i = 0, len = m.length; i < len; i++){
10010             m[i].commit();
10011         }
10012     },
10013
10014     /**
10015      * Cancel outstanding changes on all changed records.
10016      */
10017     rejectChanges : function(){
10018         var m = this.modified.slice(0);
10019         this.modified = [];
10020         for(var i = 0, len = m.length; i < len; i++){
10021             m[i].reject();
10022         }
10023     },
10024
10025     onMetaChange : function(meta, rtype, o){
10026         this.recordType = rtype;
10027         this.fields = rtype.prototype.fields;
10028         delete this.snapshot;
10029         this.sortInfo = meta.sortInfo || this.sortInfo;
10030         this.modified = [];
10031         this.fireEvent('metachange', this, this.reader.meta);
10032     },
10033     
10034     moveIndex : function(data, type)
10035     {
10036         var index = this.indexOf(data);
10037         
10038         var newIndex = index + type;
10039         
10040         this.remove(data);
10041         
10042         this.insert(newIndex, data);
10043         
10044     }
10045 });/*
10046  * Based on:
10047  * Ext JS Library 1.1.1
10048  * Copyright(c) 2006-2007, Ext JS, LLC.
10049  *
10050  * Originally Released Under LGPL - original licence link has changed is not relivant.
10051  *
10052  * Fork - LGPL
10053  * <script type="text/javascript">
10054  */
10055
10056 /**
10057  * @class Roo.data.SimpleStore
10058  * @extends Roo.data.Store
10059  * Small helper class to make creating Stores from Array data easier.
10060  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10061  * @cfg {Array} fields An array of field definition objects, or field name strings.
10062  * @cfg {Array} data The multi-dimensional array of data
10063  * @constructor
10064  * @param {Object} config
10065  */
10066 Roo.data.SimpleStore = function(config){
10067     Roo.data.SimpleStore.superclass.constructor.call(this, {
10068         isLocal : true,
10069         reader: new Roo.data.ArrayReader({
10070                 id: config.id
10071             },
10072             Roo.data.Record.create(config.fields)
10073         ),
10074         proxy : new Roo.data.MemoryProxy(config.data)
10075     });
10076     this.load();
10077 };
10078 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10079  * Based on:
10080  * Ext JS Library 1.1.1
10081  * Copyright(c) 2006-2007, Ext JS, LLC.
10082  *
10083  * Originally Released Under LGPL - original licence link has changed is not relivant.
10084  *
10085  * Fork - LGPL
10086  * <script type="text/javascript">
10087  */
10088
10089 /**
10090 /**
10091  * @extends Roo.data.Store
10092  * @class Roo.data.JsonStore
10093  * Small helper class to make creating Stores for JSON data easier. <br/>
10094 <pre><code>
10095 var store = new Roo.data.JsonStore({
10096     url: 'get-images.php',
10097     root: 'images',
10098     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10099 });
10100 </code></pre>
10101  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10102  * JsonReader and HttpProxy (unless inline data is provided).</b>
10103  * @cfg {Array} fields An array of field definition objects, or field name strings.
10104  * @constructor
10105  * @param {Object} config
10106  */
10107 Roo.data.JsonStore = function(c){
10108     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10109         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10110         reader: new Roo.data.JsonReader(c, c.fields)
10111     }));
10112 };
10113 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10114  * Based on:
10115  * Ext JS Library 1.1.1
10116  * Copyright(c) 2006-2007, Ext JS, LLC.
10117  *
10118  * Originally Released Under LGPL - original licence link has changed is not relivant.
10119  *
10120  * Fork - LGPL
10121  * <script type="text/javascript">
10122  */
10123
10124  
10125 Roo.data.Field = function(config){
10126     if(typeof config == "string"){
10127         config = {name: config};
10128     }
10129     Roo.apply(this, config);
10130     
10131     if(!this.type){
10132         this.type = "auto";
10133     }
10134     
10135     var st = Roo.data.SortTypes;
10136     // named sortTypes are supported, here we look them up
10137     if(typeof this.sortType == "string"){
10138         this.sortType = st[this.sortType];
10139     }
10140     
10141     // set default sortType for strings and dates
10142     if(!this.sortType){
10143         switch(this.type){
10144             case "string":
10145                 this.sortType = st.asUCString;
10146                 break;
10147             case "date":
10148                 this.sortType = st.asDate;
10149                 break;
10150             default:
10151                 this.sortType = st.none;
10152         }
10153     }
10154
10155     // define once
10156     var stripRe = /[\$,%]/g;
10157
10158     // prebuilt conversion function for this field, instead of
10159     // switching every time we're reading a value
10160     if(!this.convert){
10161         var cv, dateFormat = this.dateFormat;
10162         switch(this.type){
10163             case "":
10164             case "auto":
10165             case undefined:
10166                 cv = function(v){ return v; };
10167                 break;
10168             case "string":
10169                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10170                 break;
10171             case "int":
10172                 cv = function(v){
10173                     return v !== undefined && v !== null && v !== '' ?
10174                            parseInt(String(v).replace(stripRe, ""), 10) : '';
10175                     };
10176                 break;
10177             case "float":
10178                 cv = function(v){
10179                     return v !== undefined && v !== null && v !== '' ?
10180                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
10181                     };
10182                 break;
10183             case "bool":
10184             case "boolean":
10185                 cv = function(v){ return v === true || v === "true" || v == 1; };
10186                 break;
10187             case "date":
10188                 cv = function(v){
10189                     if(!v){
10190                         return '';
10191                     }
10192                     if(v instanceof Date){
10193                         return v;
10194                     }
10195                     if(dateFormat){
10196                         if(dateFormat == "timestamp"){
10197                             return new Date(v*1000);
10198                         }
10199                         return Date.parseDate(v, dateFormat);
10200                     }
10201                     var parsed = Date.parse(v);
10202                     return parsed ? new Date(parsed) : null;
10203                 };
10204              break;
10205             
10206         }
10207         this.convert = cv;
10208     }
10209 };
10210
10211 Roo.data.Field.prototype = {
10212     dateFormat: null,
10213     defaultValue: "",
10214     mapping: null,
10215     sortType : null,
10216     sortDir : "ASC"
10217 };/*
10218  * Based on:
10219  * Ext JS Library 1.1.1
10220  * Copyright(c) 2006-2007, Ext JS, LLC.
10221  *
10222  * Originally Released Under LGPL - original licence link has changed is not relivant.
10223  *
10224  * Fork - LGPL
10225  * <script type="text/javascript">
10226  */
10227  
10228 // Base class for reading structured data from a data source.  This class is intended to be
10229 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10230
10231 /**
10232  * @class Roo.data.DataReader
10233  * Base class for reading structured data from a data source.  This class is intended to be
10234  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10235  */
10236
10237 Roo.data.DataReader = function(meta, recordType){
10238     
10239     this.meta = meta;
10240     
10241     this.recordType = recordType instanceof Array ? 
10242         Roo.data.Record.create(recordType) : recordType;
10243 };
10244
10245 Roo.data.DataReader.prototype = {
10246      /**
10247      * Create an empty record
10248      * @param {Object} data (optional) - overlay some values
10249      * @return {Roo.data.Record} record created.
10250      */
10251     newRow :  function(d) {
10252         var da =  {};
10253         this.recordType.prototype.fields.each(function(c) {
10254             switch( c.type) {
10255                 case 'int' : da[c.name] = 0; break;
10256                 case 'date' : da[c.name] = new Date(); break;
10257                 case 'float' : da[c.name] = 0.0; break;
10258                 case 'boolean' : da[c.name] = false; break;
10259                 default : da[c.name] = ""; break;
10260             }
10261             
10262         });
10263         return new this.recordType(Roo.apply(da, d));
10264     }
10265     
10266 };/*
10267  * Based on:
10268  * Ext JS Library 1.1.1
10269  * Copyright(c) 2006-2007, Ext JS, LLC.
10270  *
10271  * Originally Released Under LGPL - original licence link has changed is not relivant.
10272  *
10273  * Fork - LGPL
10274  * <script type="text/javascript">
10275  */
10276
10277 /**
10278  * @class Roo.data.DataProxy
10279  * @extends Roo.data.Observable
10280  * This class is an abstract base class for implementations which provide retrieval of
10281  * unformatted data objects.<br>
10282  * <p>
10283  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10284  * (of the appropriate type which knows how to parse the data object) to provide a block of
10285  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10286  * <p>
10287  * Custom implementations must implement the load method as described in
10288  * {@link Roo.data.HttpProxy#load}.
10289  */
10290 Roo.data.DataProxy = function(){
10291     this.addEvents({
10292         /**
10293          * @event beforeload
10294          * Fires before a network request is made to retrieve a data object.
10295          * @param {Object} This DataProxy object.
10296          * @param {Object} params The params parameter to the load function.
10297          */
10298         beforeload : true,
10299         /**
10300          * @event load
10301          * Fires before the load method's callback is called.
10302          * @param {Object} This DataProxy object.
10303          * @param {Object} o The data object.
10304          * @param {Object} arg The callback argument object passed to the load function.
10305          */
10306         load : true,
10307         /**
10308          * @event loadexception
10309          * Fires if an Exception occurs during data retrieval.
10310          * @param {Object} This DataProxy object.
10311          * @param {Object} o The data object.
10312          * @param {Object} arg The callback argument object passed to the load function.
10313          * @param {Object} e The Exception.
10314          */
10315         loadexception : true
10316     });
10317     Roo.data.DataProxy.superclass.constructor.call(this);
10318 };
10319
10320 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10321
10322     /**
10323      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10324      */
10325 /*
10326  * Based on:
10327  * Ext JS Library 1.1.1
10328  * Copyright(c) 2006-2007, Ext JS, LLC.
10329  *
10330  * Originally Released Under LGPL - original licence link has changed is not relivant.
10331  *
10332  * Fork - LGPL
10333  * <script type="text/javascript">
10334  */
10335 /**
10336  * @class Roo.data.MemoryProxy
10337  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10338  * to the Reader when its load method is called.
10339  * @constructor
10340  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10341  */
10342 Roo.data.MemoryProxy = function(data){
10343     if (data.data) {
10344         data = data.data;
10345     }
10346     Roo.data.MemoryProxy.superclass.constructor.call(this);
10347     this.data = data;
10348 };
10349
10350 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10351     /**
10352      * Load data from the requested source (in this case an in-memory
10353      * data object passed to the constructor), read the data object into
10354      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10355      * process that block using the passed callback.
10356      * @param {Object} params This parameter is not used by the MemoryProxy class.
10357      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10358      * object into a block of Roo.data.Records.
10359      * @param {Function} callback The function into which to pass the block of Roo.data.records.
10360      * The function must be passed <ul>
10361      * <li>The Record block object</li>
10362      * <li>The "arg" argument from the load function</li>
10363      * <li>A boolean success indicator</li>
10364      * </ul>
10365      * @param {Object} scope The scope in which to call the callback
10366      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10367      */
10368     load : function(params, reader, callback, scope, arg){
10369         params = params || {};
10370         var result;
10371         try {
10372             result = reader.readRecords(this.data);
10373         }catch(e){
10374             this.fireEvent("loadexception", this, arg, null, e);
10375             callback.call(scope, null, arg, false);
10376             return;
10377         }
10378         callback.call(scope, result, arg, true);
10379     },
10380     
10381     // private
10382     update : function(params, records){
10383         
10384     }
10385 });/*
10386  * Based on:
10387  * Ext JS Library 1.1.1
10388  * Copyright(c) 2006-2007, Ext JS, LLC.
10389  *
10390  * Originally Released Under LGPL - original licence link has changed is not relivant.
10391  *
10392  * Fork - LGPL
10393  * <script type="text/javascript">
10394  */
10395 /**
10396  * @class Roo.data.HttpProxy
10397  * @extends Roo.data.DataProxy
10398  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10399  * configured to reference a certain URL.<br><br>
10400  * <p>
10401  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10402  * from which the running page was served.<br><br>
10403  * <p>
10404  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10405  * <p>
10406  * Be aware that to enable the browser to parse an XML document, the server must set
10407  * the Content-Type header in the HTTP response to "text/xml".
10408  * @constructor
10409  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10410  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
10411  * will be used to make the request.
10412  */
10413 Roo.data.HttpProxy = function(conn){
10414     Roo.data.HttpProxy.superclass.constructor.call(this);
10415     // is conn a conn config or a real conn?
10416     this.conn = conn;
10417     this.useAjax = !conn || !conn.events;
10418   
10419 };
10420
10421 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10422     // thse are take from connection...
10423     
10424     /**
10425      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10426      */
10427     /**
10428      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10429      * extra parameters to each request made by this object. (defaults to undefined)
10430      */
10431     /**
10432      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10433      *  to each request made by this object. (defaults to undefined)
10434      */
10435     /**
10436      * @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)
10437      */
10438     /**
10439      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10440      */
10441      /**
10442      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10443      * @type Boolean
10444      */
10445   
10446
10447     /**
10448      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10449      * @type Boolean
10450      */
10451     /**
10452      * Return the {@link Roo.data.Connection} object being used by this Proxy.
10453      * @return {Connection} The Connection object. This object may be used to subscribe to events on
10454      * a finer-grained basis than the DataProxy events.
10455      */
10456     getConnection : function(){
10457         return this.useAjax ? Roo.Ajax : this.conn;
10458     },
10459
10460     /**
10461      * Load data from the configured {@link Roo.data.Connection}, read the data object into
10462      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10463      * process that block using the passed callback.
10464      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10465      * for the request to the remote server.
10466      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10467      * object into a block of Roo.data.Records.
10468      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10469      * The function must be passed <ul>
10470      * <li>The Record block object</li>
10471      * <li>The "arg" argument from the load function</li>
10472      * <li>A boolean success indicator</li>
10473      * </ul>
10474      * @param {Object} scope The scope in which to call the callback
10475      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10476      */
10477     load : function(params, reader, callback, scope, arg){
10478         if(this.fireEvent("beforeload", this, params) !== false){
10479             var  o = {
10480                 params : params || {},
10481                 request: {
10482                     callback : callback,
10483                     scope : scope,
10484                     arg : arg
10485                 },
10486                 reader: reader,
10487                 callback : this.loadResponse,
10488                 scope: this
10489             };
10490             if(this.useAjax){
10491                 Roo.applyIf(o, this.conn);
10492                 if(this.activeRequest){
10493                     Roo.Ajax.abort(this.activeRequest);
10494                 }
10495                 this.activeRequest = Roo.Ajax.request(o);
10496             }else{
10497                 this.conn.request(o);
10498             }
10499         }else{
10500             callback.call(scope||this, null, arg, false);
10501         }
10502     },
10503
10504     // private
10505     loadResponse : function(o, success, response){
10506         delete this.activeRequest;
10507         if(!success){
10508             this.fireEvent("loadexception", this, o, response);
10509             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10510             return;
10511         }
10512         var result;
10513         try {
10514             result = o.reader.read(response);
10515         }catch(e){
10516             this.fireEvent("loadexception", this, o, response, e);
10517             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10518             return;
10519         }
10520         
10521         this.fireEvent("load", this, o, o.request.arg);
10522         o.request.callback.call(o.request.scope, result, o.request.arg, true);
10523     },
10524
10525     // private
10526     update : function(dataSet){
10527
10528     },
10529
10530     // private
10531     updateResponse : function(dataSet){
10532
10533     }
10534 });/*
10535  * Based on:
10536  * Ext JS Library 1.1.1
10537  * Copyright(c) 2006-2007, Ext JS, LLC.
10538  *
10539  * Originally Released Under LGPL - original licence link has changed is not relivant.
10540  *
10541  * Fork - LGPL
10542  * <script type="text/javascript">
10543  */
10544
10545 /**
10546  * @class Roo.data.ScriptTagProxy
10547  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10548  * other than the originating domain of the running page.<br><br>
10549  * <p>
10550  * <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
10551  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10552  * <p>
10553  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10554  * source code that is used as the source inside a &lt;script> tag.<br><br>
10555  * <p>
10556  * In order for the browser to process the returned data, the server must wrap the data object
10557  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10558  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10559  * depending on whether the callback name was passed:
10560  * <p>
10561  * <pre><code>
10562 boolean scriptTag = false;
10563 String cb = request.getParameter("callback");
10564 if (cb != null) {
10565     scriptTag = true;
10566     response.setContentType("text/javascript");
10567 } else {
10568     response.setContentType("application/x-json");
10569 }
10570 Writer out = response.getWriter();
10571 if (scriptTag) {
10572     out.write(cb + "(");
10573 }
10574 out.print(dataBlock.toJsonString());
10575 if (scriptTag) {
10576     out.write(");");
10577 }
10578 </pre></code>
10579  *
10580  * @constructor
10581  * @param {Object} config A configuration object.
10582  */
10583 Roo.data.ScriptTagProxy = function(config){
10584     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10585     Roo.apply(this, config);
10586     this.head = document.getElementsByTagName("head")[0];
10587 };
10588
10589 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10590
10591 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10592     /**
10593      * @cfg {String} url The URL from which to request the data object.
10594      */
10595     /**
10596      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10597      */
10598     timeout : 30000,
10599     /**
10600      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10601      * the server the name of the callback function set up by the load call to process the returned data object.
10602      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10603      * javascript output which calls this named function passing the data object as its only parameter.
10604      */
10605     callbackParam : "callback",
10606     /**
10607      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10608      * name to the request.
10609      */
10610     nocache : true,
10611
10612     /**
10613      * Load data from the configured URL, read the data object into
10614      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10615      * process that block using the passed callback.
10616      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10617      * for the request to the remote server.
10618      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10619      * object into a block of Roo.data.Records.
10620      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10621      * The function must be passed <ul>
10622      * <li>The Record block object</li>
10623      * <li>The "arg" argument from the load function</li>
10624      * <li>A boolean success indicator</li>
10625      * </ul>
10626      * @param {Object} scope The scope in which to call the callback
10627      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10628      */
10629     load : function(params, reader, callback, scope, arg){
10630         if(this.fireEvent("beforeload", this, params) !== false){
10631
10632             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10633
10634             var url = this.url;
10635             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10636             if(this.nocache){
10637                 url += "&_dc=" + (new Date().getTime());
10638             }
10639             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10640             var trans = {
10641                 id : transId,
10642                 cb : "stcCallback"+transId,
10643                 scriptId : "stcScript"+transId,
10644                 params : params,
10645                 arg : arg,
10646                 url : url,
10647                 callback : callback,
10648                 scope : scope,
10649                 reader : reader
10650             };
10651             var conn = this;
10652
10653             window[trans.cb] = function(o){
10654                 conn.handleResponse(o, trans);
10655             };
10656
10657             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10658
10659             if(this.autoAbort !== false){
10660                 this.abort();
10661             }
10662
10663             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10664
10665             var script = document.createElement("script");
10666             script.setAttribute("src", url);
10667             script.setAttribute("type", "text/javascript");
10668             script.setAttribute("id", trans.scriptId);
10669             this.head.appendChild(script);
10670
10671             this.trans = trans;
10672         }else{
10673             callback.call(scope||this, null, arg, false);
10674         }
10675     },
10676
10677     // private
10678     isLoading : function(){
10679         return this.trans ? true : false;
10680     },
10681
10682     /**
10683      * Abort the current server request.
10684      */
10685     abort : function(){
10686         if(this.isLoading()){
10687             this.destroyTrans(this.trans);
10688         }
10689     },
10690
10691     // private
10692     destroyTrans : function(trans, isLoaded){
10693         this.head.removeChild(document.getElementById(trans.scriptId));
10694         clearTimeout(trans.timeoutId);
10695         if(isLoaded){
10696             window[trans.cb] = undefined;
10697             try{
10698                 delete window[trans.cb];
10699             }catch(e){}
10700         }else{
10701             // if hasn't been loaded, wait for load to remove it to prevent script error
10702             window[trans.cb] = function(){
10703                 window[trans.cb] = undefined;
10704                 try{
10705                     delete window[trans.cb];
10706                 }catch(e){}
10707             };
10708         }
10709     },
10710
10711     // private
10712     handleResponse : function(o, trans){
10713         this.trans = false;
10714         this.destroyTrans(trans, true);
10715         var result;
10716         try {
10717             result = trans.reader.readRecords(o);
10718         }catch(e){
10719             this.fireEvent("loadexception", this, o, trans.arg, e);
10720             trans.callback.call(trans.scope||window, null, trans.arg, false);
10721             return;
10722         }
10723         this.fireEvent("load", this, o, trans.arg);
10724         trans.callback.call(trans.scope||window, result, trans.arg, true);
10725     },
10726
10727     // private
10728     handleFailure : function(trans){
10729         this.trans = false;
10730         this.destroyTrans(trans, false);
10731         this.fireEvent("loadexception", this, null, trans.arg);
10732         trans.callback.call(trans.scope||window, null, trans.arg, false);
10733     }
10734 });/*
10735  * Based on:
10736  * Ext JS Library 1.1.1
10737  * Copyright(c) 2006-2007, Ext JS, LLC.
10738  *
10739  * Originally Released Under LGPL - original licence link has changed is not relivant.
10740  *
10741  * Fork - LGPL
10742  * <script type="text/javascript">
10743  */
10744
10745 /**
10746  * @class Roo.data.JsonReader
10747  * @extends Roo.data.DataReader
10748  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10749  * based on mappings in a provided Roo.data.Record constructor.
10750  * 
10751  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10752  * in the reply previously. 
10753  * 
10754  * <p>
10755  * Example code:
10756  * <pre><code>
10757 var RecordDef = Roo.data.Record.create([
10758     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
10759     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
10760 ]);
10761 var myReader = new Roo.data.JsonReader({
10762     totalProperty: "results",    // The property which contains the total dataset size (optional)
10763     root: "rows",                // The property which contains an Array of row objects
10764     id: "id"                     // The property within each row object that provides an ID for the record (optional)
10765 }, RecordDef);
10766 </code></pre>
10767  * <p>
10768  * This would consume a JSON file like this:
10769  * <pre><code>
10770 { 'results': 2, 'rows': [
10771     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10772     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10773 }
10774 </code></pre>
10775  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10776  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10777  * paged from the remote server.
10778  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10779  * @cfg {String} root name of the property which contains the Array of row objects.
10780  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10781  * @cfg {Array} fields Array of field definition objects
10782  * @constructor
10783  * Create a new JsonReader
10784  * @param {Object} meta Metadata configuration options
10785  * @param {Object} recordType Either an Array of field definition objects,
10786  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10787  */
10788 Roo.data.JsonReader = function(meta, recordType){
10789     
10790     meta = meta || {};
10791     // set some defaults:
10792     Roo.applyIf(meta, {
10793         totalProperty: 'total',
10794         successProperty : 'success',
10795         root : 'data',
10796         id : 'id'
10797     });
10798     
10799     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10800 };
10801 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10802     
10803     /**
10804      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
10805      * Used by Store query builder to append _requestMeta to params.
10806      * 
10807      */
10808     metaFromRemote : false,
10809     /**
10810      * This method is only used by a DataProxy which has retrieved data from a remote server.
10811      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10812      * @return {Object} data A data block which is used by an Roo.data.Store object as
10813      * a cache of Roo.data.Records.
10814      */
10815     read : function(response){
10816         var json = response.responseText;
10817        
10818         var o = /* eval:var:o */ eval("("+json+")");
10819         if(!o) {
10820             throw {message: "JsonReader.read: Json object not found"};
10821         }
10822         
10823         if(o.metaData){
10824             
10825             delete this.ef;
10826             this.metaFromRemote = true;
10827             this.meta = o.metaData;
10828             this.recordType = Roo.data.Record.create(o.metaData.fields);
10829             this.onMetaChange(this.meta, this.recordType, o);
10830         }
10831         return this.readRecords(o);
10832     },
10833
10834     // private function a store will implement
10835     onMetaChange : function(meta, recordType, o){
10836
10837     },
10838
10839     /**
10840          * @ignore
10841          */
10842     simpleAccess: function(obj, subsc) {
10843         return obj[subsc];
10844     },
10845
10846         /**
10847          * @ignore
10848          */
10849     getJsonAccessor: function(){
10850         var re = /[\[\.]/;
10851         return function(expr) {
10852             try {
10853                 return(re.test(expr))
10854                     ? new Function("obj", "return obj." + expr)
10855                     : function(obj){
10856                         return obj[expr];
10857                     };
10858             } catch(e){}
10859             return Roo.emptyFn;
10860         };
10861     }(),
10862
10863     /**
10864      * Create a data block containing Roo.data.Records from an XML document.
10865      * @param {Object} o An object which contains an Array of row objects in the property specified
10866      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10867      * which contains the total size of the dataset.
10868      * @return {Object} data A data block which is used by an Roo.data.Store object as
10869      * a cache of Roo.data.Records.
10870      */
10871     readRecords : function(o){
10872         /**
10873          * After any data loads, the raw JSON data is available for further custom processing.
10874          * @type Object
10875          */
10876         this.o = o;
10877         var s = this.meta, Record = this.recordType,
10878             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
10879
10880 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10881         if (!this.ef) {
10882             if(s.totalProperty) {
10883                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10884                 }
10885                 if(s.successProperty) {
10886                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10887                 }
10888                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10889                 if (s.id) {
10890                         var g = this.getJsonAccessor(s.id);
10891                         this.getId = function(rec) {
10892                                 var r = g(rec);  
10893                                 return (r === undefined || r === "") ? null : r;
10894                         };
10895                 } else {
10896                         this.getId = function(){return null;};
10897                 }
10898             this.ef = [];
10899             for(var jj = 0; jj < fl; jj++){
10900                 f = fi[jj];
10901                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10902                 this.ef[jj] = this.getJsonAccessor(map);
10903             }
10904         }
10905
10906         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10907         if(s.totalProperty){
10908             var vt = parseInt(this.getTotal(o), 10);
10909             if(!isNaN(vt)){
10910                 totalRecords = vt;
10911             }
10912         }
10913         if(s.successProperty){
10914             var vs = this.getSuccess(o);
10915             if(vs === false || vs === 'false'){
10916                 success = false;
10917             }
10918         }
10919         var records = [];
10920         for(var i = 0; i < c; i++){
10921                 var n = root[i];
10922             var values = {};
10923             var id = this.getId(n);
10924             for(var j = 0; j < fl; j++){
10925                 f = fi[j];
10926             var v = this.ef[j](n);
10927             if (!f.convert) {
10928                 Roo.log('missing convert for ' + f.name);
10929                 Roo.log(f);
10930                 continue;
10931             }
10932             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10933             }
10934             var record = new Record(values, id);
10935             record.json = n;
10936             records[i] = record;
10937         }
10938         return {
10939             raw : o,
10940             success : success,
10941             records : records,
10942             totalRecords : totalRecords
10943         };
10944     }
10945 });/*
10946  * Based on:
10947  * Ext JS Library 1.1.1
10948  * Copyright(c) 2006-2007, Ext JS, LLC.
10949  *
10950  * Originally Released Under LGPL - original licence link has changed is not relivant.
10951  *
10952  * Fork - LGPL
10953  * <script type="text/javascript">
10954  */
10955
10956 /**
10957  * @class Roo.data.ArrayReader
10958  * @extends Roo.data.DataReader
10959  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10960  * Each element of that Array represents a row of data fields. The
10961  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10962  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10963  * <p>
10964  * Example code:.
10965  * <pre><code>
10966 var RecordDef = Roo.data.Record.create([
10967     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10968     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10969 ]);
10970 var myReader = new Roo.data.ArrayReader({
10971     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10972 }, RecordDef);
10973 </code></pre>
10974  * <p>
10975  * This would consume an Array like this:
10976  * <pre><code>
10977 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10978   </code></pre>
10979  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10980  * @constructor
10981  * Create a new JsonReader
10982  * @param {Object} meta Metadata configuration options.
10983  * @param {Object} recordType Either an Array of field definition objects
10984  * as specified to {@link Roo.data.Record#create},
10985  * or an {@link Roo.data.Record} object
10986  * created using {@link Roo.data.Record#create}.
10987  */
10988 Roo.data.ArrayReader = function(meta, recordType){
10989     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10990 };
10991
10992 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10993     /**
10994      * Create a data block containing Roo.data.Records from an XML document.
10995      * @param {Object} o An Array of row objects which represents the dataset.
10996      * @return {Object} data A data block which is used by an Roo.data.Store object as
10997      * a cache of Roo.data.Records.
10998      */
10999     readRecords : function(o){
11000         var sid = this.meta ? this.meta.id : null;
11001         var recordType = this.recordType, fields = recordType.prototype.fields;
11002         var records = [];
11003         var root = o;
11004             for(var i = 0; i < root.length; i++){
11005                     var n = root[i];
11006                 var values = {};
11007                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11008                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11009                 var f = fields.items[j];
11010                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11011                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11012                 v = f.convert(v);
11013                 values[f.name] = v;
11014             }
11015                 var record = new recordType(values, id);
11016                 record.json = n;
11017                 records[records.length] = record;
11018             }
11019             return {
11020                 records : records,
11021                 totalRecords : records.length
11022             };
11023     }
11024 });/*
11025  * - LGPL
11026  * * 
11027  */
11028
11029 /**
11030  * @class Roo.bootstrap.ComboBox
11031  * @extends Roo.bootstrap.TriggerField
11032  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11033  * @cfg {Boolean} append (true|false) default false
11034  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11035  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11036  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11037  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11038  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11039  * @constructor
11040  * Create a new ComboBox.
11041  * @param {Object} config Configuration options
11042  */
11043 Roo.bootstrap.ComboBox = function(config){
11044     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11045     this.addEvents({
11046         /**
11047          * @event expand
11048          * Fires when the dropdown list is expanded
11049              * @param {Roo.bootstrap.ComboBox} combo This combo box
11050              */
11051         'expand' : true,
11052         /**
11053          * @event collapse
11054          * Fires when the dropdown list is collapsed
11055              * @param {Roo.bootstrap.ComboBox} combo This combo box
11056              */
11057         'collapse' : true,
11058         /**
11059          * @event beforeselect
11060          * Fires before a list item is selected. Return false to cancel the selection.
11061              * @param {Roo.bootstrap.ComboBox} combo This combo box
11062              * @param {Roo.data.Record} record The data record returned from the underlying store
11063              * @param {Number} index The index of the selected item in the dropdown list
11064              */
11065         'beforeselect' : true,
11066         /**
11067          * @event select
11068          * Fires when a list item is selected
11069              * @param {Roo.bootstrap.ComboBox} combo This combo box
11070              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11071              * @param {Number} index The index of the selected item in the dropdown list
11072              */
11073         'select' : true,
11074         /**
11075          * @event beforequery
11076          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11077          * The event object passed has these properties:
11078              * @param {Roo.bootstrap.ComboBox} combo This combo box
11079              * @param {String} query The query
11080              * @param {Boolean} forceAll true to force "all" query
11081              * @param {Boolean} cancel true to cancel the query
11082              * @param {Object} e The query event object
11083              */
11084         'beforequery': true,
11085          /**
11086          * @event add
11087          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11088              * @param {Roo.bootstrap.ComboBox} combo This combo box
11089              */
11090         'add' : true,
11091         /**
11092          * @event edit
11093          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11094              * @param {Roo.bootstrap.ComboBox} combo This combo box
11095              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11096              */
11097         'edit' : true,
11098         /**
11099          * @event remove
11100          * Fires when the remove value from the combobox array
11101              * @param {Roo.bootstrap.ComboBox} combo This combo box
11102              */
11103         'remove' : true,
11104         /**
11105          * @event specialfilter
11106          * Fires when specialfilter
11107             * @param {Roo.bootstrap.ComboBox} combo This combo box
11108             */
11109         'specialfilter' : true
11110         
11111     });
11112     
11113     this.item = [];
11114     this.tickItems = [];
11115     
11116     this.selectedIndex = -1;
11117     if(this.mode == 'local'){
11118         if(config.queryDelay === undefined){
11119             this.queryDelay = 10;
11120         }
11121         if(config.minChars === undefined){
11122             this.minChars = 0;
11123         }
11124     }
11125 };
11126
11127 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11128      
11129     /**
11130      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11131      * rendering into an Roo.Editor, defaults to false)
11132      */
11133     /**
11134      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11135      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11136      */
11137     /**
11138      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11139      */
11140     /**
11141      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11142      * the dropdown list (defaults to undefined, with no header element)
11143      */
11144
11145      /**
11146      * @cfg {String/Roo.Template} tpl The template to use to render the output
11147      */
11148      
11149      /**
11150      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11151      */
11152     listWidth: undefined,
11153     /**
11154      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11155      * mode = 'remote' or 'text' if mode = 'local')
11156      */
11157     displayField: undefined,
11158     
11159     /**
11160      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11161      * mode = 'remote' or 'value' if mode = 'local'). 
11162      * Note: use of a valueField requires the user make a selection
11163      * in order for a value to be mapped.
11164      */
11165     valueField: undefined,
11166     
11167     
11168     /**
11169      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11170      * field's data value (defaults to the underlying DOM element's name)
11171      */
11172     hiddenName: undefined,
11173     /**
11174      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11175      */
11176     listClass: '',
11177     /**
11178      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11179      */
11180     selectedClass: 'active',
11181     
11182     /**
11183      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11184      */
11185     shadow:'sides',
11186     /**
11187      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11188      * anchor positions (defaults to 'tl-bl')
11189      */
11190     listAlign: 'tl-bl?',
11191     /**
11192      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11193      */
11194     maxHeight: 300,
11195     /**
11196      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
11197      * query specified by the allQuery config option (defaults to 'query')
11198      */
11199     triggerAction: 'query',
11200     /**
11201      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11202      * (defaults to 4, does not apply if editable = false)
11203      */
11204     minChars : 4,
11205     /**
11206      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11207      * delay (typeAheadDelay) if it matches a known value (defaults to false)
11208      */
11209     typeAhead: false,
11210     /**
11211      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11212      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11213      */
11214     queryDelay: 500,
11215     /**
11216      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11217      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
11218      */
11219     pageSize: 0,
11220     /**
11221      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
11222      * when editable = true (defaults to false)
11223      */
11224     selectOnFocus:false,
11225     /**
11226      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11227      */
11228     queryParam: 'query',
11229     /**
11230      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
11231      * when mode = 'remote' (defaults to 'Loading...')
11232      */
11233     loadingText: 'Loading...',
11234     /**
11235      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11236      */
11237     resizable: false,
11238     /**
11239      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11240      */
11241     handleHeight : 8,
11242     /**
11243      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11244      * traditional select (defaults to true)
11245      */
11246     editable: true,
11247     /**
11248      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11249      */
11250     allQuery: '',
11251     /**
11252      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11253      */
11254     mode: 'remote',
11255     /**
11256      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11257      * listWidth has a higher value)
11258      */
11259     minListWidth : 70,
11260     /**
11261      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11262      * allow the user to set arbitrary text into the field (defaults to false)
11263      */
11264     forceSelection:false,
11265     /**
11266      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11267      * if typeAhead = true (defaults to 250)
11268      */
11269     typeAheadDelay : 250,
11270     /**
11271      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11272      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11273      */
11274     valueNotFoundText : undefined,
11275     /**
11276      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11277      */
11278     blockFocus : false,
11279     
11280     /**
11281      * @cfg {Boolean} disableClear Disable showing of clear button.
11282      */
11283     disableClear : false,
11284     /**
11285      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
11286      */
11287     alwaysQuery : false,
11288     
11289     /**
11290      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
11291      */
11292     multiple : false,
11293     
11294     /**
11295      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11296      */
11297     invalidClass : "has-warning",
11298     
11299     /**
11300      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11301      */
11302     validClass : "has-success",
11303     
11304     /**
11305      * @cfg {Boolean} specialFilter (true|false) special filter default false
11306      */
11307     specialFilter : false,
11308     
11309     //private
11310     addicon : false,
11311     editicon: false,
11312     
11313     page: 0,
11314     hasQuery: false,
11315     append: false,
11316     loadNext: false,
11317     autoFocus : true,
11318     tickable : false,
11319     btnPosition : 'right',
11320     triggerList : true,
11321     showToggleBtn : true,
11322     // element that contains real text value.. (when hidden is used..)
11323     
11324     getAutoCreate : function()
11325     {
11326         var cfg = false;
11327         
11328         /*
11329          *  Normal ComboBox
11330          */
11331         if(!this.tickable){
11332             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11333             return cfg;
11334         }
11335         
11336         /*
11337          *  ComboBox with tickable selections
11338          */
11339              
11340         var align = this.labelAlign || this.parentLabelAlign();
11341         
11342         cfg = {
11343             cls : 'form-group roo-combobox-tickable' //input-group
11344         };
11345         
11346         var buttons = {
11347             tag : 'div',
11348             cls : 'tickable-buttons',
11349             cn : [
11350                 {
11351                     tag : 'button',
11352                     type : 'button',
11353                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11354                     html : 'Edit'
11355                 },
11356                 {
11357                     tag : 'button',
11358                     type : 'button',
11359                     name : 'ok',
11360                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11361                     html : 'Done'
11362                 },
11363                 {
11364                     tag : 'button',
11365                     type : 'button',
11366                     name : 'cancel',
11367                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11368                     html : 'Cancel'
11369                 }
11370             ]
11371         };
11372         
11373         if(this.editable){
11374             buttons.cn.unshift({
11375                 tag: 'input',
11376                 cls: 'select2-search-field-input'
11377             });
11378         }
11379         
11380         var _this = this;
11381         
11382         Roo.each(buttons.cn, function(c){
11383             if (_this.size) {
11384                 c.cls += ' btn-' + _this.size;
11385             }
11386
11387             if (_this.disabled) {
11388                 c.disabled = true;
11389             }
11390         });
11391         
11392         var box = {
11393             tag: 'div',
11394             cn: [
11395                 {
11396                     tag: 'input',
11397                     type : 'hidden',
11398                     cls: 'form-hidden-field'
11399                 },
11400                 {
11401                     tag: 'ul',
11402                     cls: 'select2-choices',
11403                     cn:[
11404                         {
11405                             tag: 'li',
11406                             cls: 'select2-search-field',
11407                             cn: [
11408
11409                                 buttons
11410                             ]
11411                         }
11412                     ]
11413                 }
11414             ]
11415         }
11416         
11417         var combobox = {
11418             cls: 'select2-container input-group select2-container-multi',
11419             cn: [
11420                 box
11421 //                {
11422 //                    tag: 'ul',
11423 //                    cls: 'typeahead typeahead-long dropdown-menu',
11424 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
11425 //                }
11426             ]
11427         };
11428         
11429         if(this.hasFeedback && !this.allowBlank){
11430             
11431             var feedback = {
11432                 tag: 'span',
11433                 cls: 'glyphicon form-control-feedback'
11434             };
11435
11436             combobox.cn.push(feedback);
11437         }
11438         
11439         if (align ==='left' && this.fieldLabel.length) {
11440             
11441                 Roo.log("left and has label");
11442                 cfg.cn = [
11443                     
11444                     {
11445                         tag: 'label',
11446                         'for' :  id,
11447                         cls : 'control-label col-sm-' + this.labelWidth,
11448                         html : this.fieldLabel
11449                         
11450                     },
11451                     {
11452                         cls : "col-sm-" + (12 - this.labelWidth), 
11453                         cn: [
11454                             combobox
11455                         ]
11456                     }
11457                     
11458                 ];
11459         } else if ( this.fieldLabel.length) {
11460                 Roo.log(" label");
11461                  cfg.cn = [
11462                    
11463                     {
11464                         tag: 'label',
11465                         //cls : 'input-group-addon',
11466                         html : this.fieldLabel
11467                         
11468                     },
11469                     
11470                     combobox
11471                     
11472                 ];
11473
11474         } else {
11475             
11476                 Roo.log(" no label && no align");
11477                 cfg = combobox
11478                      
11479                 
11480         }
11481          
11482         var settings=this;
11483         ['xs','sm','md','lg'].map(function(size){
11484             if (settings[size]) {
11485                 cfg.cls += ' col-' + size + '-' + settings[size];
11486             }
11487         });
11488         
11489         return cfg;
11490         
11491     },
11492     
11493     // private
11494     initEvents: function()
11495     {
11496         
11497         if (!this.store) {
11498             throw "can not find store for combo";
11499         }
11500         this.store = Roo.factory(this.store, Roo.data);
11501         
11502         if(this.tickable){
11503             this.initTickableEvents();
11504             return;
11505         }
11506         
11507         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11508         
11509         if(this.hiddenName){
11510             
11511             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11512             
11513             this.hiddenField.dom.value =
11514                 this.hiddenValue !== undefined ? this.hiddenValue :
11515                 this.value !== undefined ? this.value : '';
11516
11517             // prevent input submission
11518             this.el.dom.removeAttribute('name');
11519             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11520              
11521              
11522         }
11523         //if(Roo.isGecko){
11524         //    this.el.dom.setAttribute('autocomplete', 'off');
11525         //}
11526         
11527         var cls = 'x-combo-list';
11528         
11529         //this.list = new Roo.Layer({
11530         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11531         //});
11532         
11533         var _this = this;
11534         
11535         (function(){
11536             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11537             _this.list.setWidth(lw);
11538         }).defer(100);
11539         
11540         this.list.on('mouseover', this.onViewOver, this);
11541         this.list.on('mousemove', this.onViewMove, this);
11542         
11543         this.list.on('scroll', this.onViewScroll, this);
11544         
11545         /*
11546         this.list.swallowEvent('mousewheel');
11547         this.assetHeight = 0;
11548
11549         if(this.title){
11550             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
11551             this.assetHeight += this.header.getHeight();
11552         }
11553
11554         this.innerList = this.list.createChild({cls:cls+'-inner'});
11555         this.innerList.on('mouseover', this.onViewOver, this);
11556         this.innerList.on('mousemove', this.onViewMove, this);
11557         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11558         
11559         if(this.allowBlank && !this.pageSize && !this.disableClear){
11560             this.footer = this.list.createChild({cls:cls+'-ft'});
11561             this.pageTb = new Roo.Toolbar(this.footer);
11562            
11563         }
11564         if(this.pageSize){
11565             this.footer = this.list.createChild({cls:cls+'-ft'});
11566             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
11567                     {pageSize: this.pageSize});
11568             
11569         }
11570         
11571         if (this.pageTb && this.allowBlank && !this.disableClear) {
11572             var _this = this;
11573             this.pageTb.add(new Roo.Toolbar.Fill(), {
11574                 cls: 'x-btn-icon x-btn-clear',
11575                 text: '&#160;',
11576                 handler: function()
11577                 {
11578                     _this.collapse();
11579                     _this.clearValue();
11580                     _this.onSelect(false, -1);
11581                 }
11582             });
11583         }
11584         if (this.footer) {
11585             this.assetHeight += this.footer.getHeight();
11586         }
11587         */
11588             
11589         if(!this.tpl){
11590             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11591         }
11592
11593         this.view = new Roo.View(this.list, this.tpl, {
11594             singleSelect:true, store: this.store, selectedClass: this.selectedClass
11595         });
11596         //this.view.wrapEl.setDisplayed(false);
11597         this.view.on('click', this.onViewClick, this);
11598         
11599         
11600         
11601         this.store.on('beforeload', this.onBeforeLoad, this);
11602         this.store.on('load', this.onLoad, this);
11603         this.store.on('loadexception', this.onLoadException, this);
11604         /*
11605         if(this.resizable){
11606             this.resizer = new Roo.Resizable(this.list,  {
11607                pinned:true, handles:'se'
11608             });
11609             this.resizer.on('resize', function(r, w, h){
11610                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11611                 this.listWidth = w;
11612                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11613                 this.restrictHeight();
11614             }, this);
11615             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11616         }
11617         */
11618         if(!this.editable){
11619             this.editable = true;
11620             this.setEditable(false);
11621         }
11622         
11623         /*
11624         
11625         if (typeof(this.events.add.listeners) != 'undefined') {
11626             
11627             this.addicon = this.wrap.createChild(
11628                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
11629        
11630             this.addicon.on('click', function(e) {
11631                 this.fireEvent('add', this);
11632             }, this);
11633         }
11634         if (typeof(this.events.edit.listeners) != 'undefined') {
11635             
11636             this.editicon = this.wrap.createChild(
11637                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
11638             if (this.addicon) {
11639                 this.editicon.setStyle('margin-left', '40px');
11640             }
11641             this.editicon.on('click', function(e) {
11642                 
11643                 // we fire even  if inothing is selected..
11644                 this.fireEvent('edit', this, this.lastData );
11645                 
11646             }, this);
11647         }
11648         */
11649         
11650         this.keyNav = new Roo.KeyNav(this.inputEl(), {
11651             "up" : function(e){
11652                 this.inKeyMode = true;
11653                 this.selectPrev();
11654             },
11655
11656             "down" : function(e){
11657                 if(!this.isExpanded()){
11658                     this.onTriggerClick();
11659                 }else{
11660                     this.inKeyMode = true;
11661                     this.selectNext();
11662                 }
11663             },
11664
11665             "enter" : function(e){
11666 //                this.onViewClick();
11667                 //return true;
11668                 this.collapse();
11669                 
11670                 if(this.fireEvent("specialkey", this, e)){
11671                     this.onViewClick(false);
11672                 }
11673                 
11674                 return true;
11675             },
11676
11677             "esc" : function(e){
11678                 this.collapse();
11679             },
11680
11681             "tab" : function(e){
11682                 this.collapse();
11683                 
11684                 if(this.fireEvent("specialkey", this, e)){
11685                     this.onViewClick(false);
11686                 }
11687                 
11688                 return true;
11689             },
11690
11691             scope : this,
11692
11693             doRelay : function(foo, bar, hname){
11694                 if(hname == 'down' || this.scope.isExpanded()){
11695                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11696                 }
11697                 return true;
11698             },
11699
11700             forceKeyDown: true
11701         });
11702         
11703         
11704         this.queryDelay = Math.max(this.queryDelay || 10,
11705                 this.mode == 'local' ? 10 : 250);
11706         
11707         
11708         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11709         
11710         if(this.typeAhead){
11711             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11712         }
11713         if(this.editable !== false){
11714             this.inputEl().on("keyup", this.onKeyUp, this);
11715         }
11716         if(this.forceSelection){
11717             this.inputEl().on('blur', this.doForce, this);
11718         }
11719         
11720         if(this.multiple){
11721             this.choices = this.el.select('ul.select2-choices', true).first();
11722             this.searchField = this.el.select('ul li.select2-search-field', true).first();
11723         }
11724     },
11725     
11726     initTickableEvents: function()
11727     {   
11728         this.createList();
11729         
11730         if(this.hiddenName){
11731             
11732             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11733             
11734             this.hiddenField.dom.value =
11735                 this.hiddenValue !== undefined ? this.hiddenValue :
11736                 this.value !== undefined ? this.value : '';
11737
11738             // prevent input submission
11739             this.el.dom.removeAttribute('name');
11740             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11741              
11742              
11743         }
11744         
11745 //        this.list = this.el.select('ul.dropdown-menu',true).first();
11746         
11747         this.choices = this.el.select('ul.select2-choices', true).first();
11748         this.searchField = this.el.select('ul li.select2-search-field', true).first();
11749         if(this.triggerList){
11750             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
11751         }
11752          
11753         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
11754         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
11755         
11756         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
11757         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
11758         
11759         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
11760         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
11761         
11762         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
11763         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
11764         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
11765         
11766         this.okBtn.hide();
11767         this.cancelBtn.hide();
11768         
11769         var _this = this;
11770         
11771         (function(){
11772             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11773             _this.list.setWidth(lw);
11774         }).defer(100);
11775         
11776         this.list.on('mouseover', this.onViewOver, this);
11777         this.list.on('mousemove', this.onViewMove, this);
11778         
11779         this.list.on('scroll', this.onViewScroll, this);
11780         
11781         if(!this.tpl){
11782             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>';
11783         }
11784
11785         this.view = new Roo.View(this.list, this.tpl, {
11786             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
11787         });
11788         
11789         //this.view.wrapEl.setDisplayed(false);
11790         this.view.on('click', this.onViewClick, this);
11791         
11792         
11793         
11794         this.store.on('beforeload', this.onBeforeLoad, this);
11795         this.store.on('load', this.onLoad, this);
11796         this.store.on('loadexception', this.onLoadException, this);
11797         
11798         if(this.editable){
11799             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
11800                 "up" : function(e){
11801                     this.inKeyMode = true;
11802                     this.selectPrev();
11803                 },
11804
11805                 "down" : function(e){
11806                     this.inKeyMode = true;
11807                     this.selectNext();
11808                 },
11809
11810                 "enter" : function(e){
11811                     if(this.fireEvent("specialkey", this, e)){
11812                         this.onViewClick(false);
11813                     }
11814                     
11815                     return true;
11816                 },
11817
11818                 "esc" : function(e){
11819                     this.onTickableFooterButtonClick(e, false, false);
11820                 },
11821
11822                 "tab" : function(e){
11823                     this.fireEvent("specialkey", this, e);
11824                     
11825                     this.onTickableFooterButtonClick(e, false, false);
11826                     
11827                     return true;
11828                 },
11829
11830                 scope : this,
11831
11832                 doRelay : function(e, fn, key){
11833                     if(this.scope.isExpanded()){
11834                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11835                     }
11836                     return true;
11837                 },
11838
11839                 forceKeyDown: true
11840             });
11841         }
11842         
11843         this.queryDelay = Math.max(this.queryDelay || 10,
11844                 this.mode == 'local' ? 10 : 250);
11845         
11846         
11847         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11848         
11849         if(this.typeAhead){
11850             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11851         }
11852         
11853         if(this.editable !== false){
11854             this.tickableInputEl().on("keyup", this.onKeyUp, this);
11855         }
11856         
11857     },
11858
11859     onDestroy : function(){
11860         if(this.view){
11861             this.view.setStore(null);
11862             this.view.el.removeAllListeners();
11863             this.view.el.remove();
11864             this.view.purgeListeners();
11865         }
11866         if(this.list){
11867             this.list.dom.innerHTML  = '';
11868         }
11869         
11870         if(this.store){
11871             this.store.un('beforeload', this.onBeforeLoad, this);
11872             this.store.un('load', this.onLoad, this);
11873             this.store.un('loadexception', this.onLoadException, this);
11874         }
11875         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11876     },
11877
11878     // private
11879     fireKey : function(e){
11880         if(e.isNavKeyPress() && !this.list.isVisible()){
11881             this.fireEvent("specialkey", this, e);
11882         }
11883     },
11884
11885     // private
11886     onResize: function(w, h){
11887 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11888 //        
11889 //        if(typeof w != 'number'){
11890 //            // we do not handle it!?!?
11891 //            return;
11892 //        }
11893 //        var tw = this.trigger.getWidth();
11894 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11895 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11896 //        var x = w - tw;
11897 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11898 //            
11899 //        //this.trigger.setStyle('left', x+'px');
11900 //        
11901 //        if(this.list && this.listWidth === undefined){
11902 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11903 //            this.list.setWidth(lw);
11904 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11905 //        }
11906         
11907     
11908         
11909     },
11910
11911     /**
11912      * Allow or prevent the user from directly editing the field text.  If false is passed,
11913      * the user will only be able to select from the items defined in the dropdown list.  This method
11914      * is the runtime equivalent of setting the 'editable' config option at config time.
11915      * @param {Boolean} value True to allow the user to directly edit the field text
11916      */
11917     setEditable : function(value){
11918         if(value == this.editable){
11919             return;
11920         }
11921         this.editable = value;
11922         if(!value){
11923             this.inputEl().dom.setAttribute('readOnly', true);
11924             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11925             this.inputEl().addClass('x-combo-noedit');
11926         }else{
11927             this.inputEl().dom.setAttribute('readOnly', false);
11928             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11929             this.inputEl().removeClass('x-combo-noedit');
11930         }
11931     },
11932
11933     // private
11934     
11935     onBeforeLoad : function(combo,opts){
11936         if(!this.hasFocus){
11937             return;
11938         }
11939          if (!opts.add) {
11940             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11941          }
11942         this.restrictHeight();
11943         this.selectedIndex = -1;
11944     },
11945
11946     // private
11947     onLoad : function(){
11948         
11949         this.hasQuery = false;
11950         
11951         if(!this.hasFocus){
11952             return;
11953         }
11954         
11955         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11956             this.loading.hide();
11957         }
11958              
11959         if(this.store.getCount() > 0){
11960             this.expand();
11961             this.restrictHeight();
11962             if(this.lastQuery == this.allQuery){
11963                 if(this.editable && !this.tickable){
11964                     this.inputEl().dom.select();
11965                 }
11966                 
11967                 if(
11968                     !this.selectByValue(this.value, true) &&
11969                     this.autoFocus && 
11970                     (
11971                         !this.store.lastOptions ||
11972                         typeof(this.store.lastOptions.add) == 'undefined' || 
11973                         this.store.lastOptions.add != true
11974                     )
11975                 ){
11976                     this.select(0, true);
11977                 }
11978             }else{
11979                 if(this.autoFocus){
11980                     this.selectNext();
11981                 }
11982                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11983                     this.taTask.delay(this.typeAheadDelay);
11984                 }
11985             }
11986         }else{
11987             this.onEmptyResults();
11988         }
11989         
11990         //this.el.focus();
11991     },
11992     // private
11993     onLoadException : function()
11994     {
11995         this.hasQuery = false;
11996         
11997         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11998             this.loading.hide();
11999         }
12000         
12001         if(this.tickable && this.editable){
12002             return;
12003         }
12004         
12005         this.collapse();
12006         
12007         Roo.log(this.store.reader.jsonData);
12008         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12009             // fixme
12010             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12011         }
12012         
12013         
12014     },
12015     // private
12016     onTypeAhead : function(){
12017         if(this.store.getCount() > 0){
12018             var r = this.store.getAt(0);
12019             var newValue = r.data[this.displayField];
12020             var len = newValue.length;
12021             var selStart = this.getRawValue().length;
12022             
12023             if(selStart != len){
12024                 this.setRawValue(newValue);
12025                 this.selectText(selStart, newValue.length);
12026             }
12027         }
12028     },
12029
12030     // private
12031     onSelect : function(record, index){
12032         
12033         if(this.fireEvent('beforeselect', this, record, index) !== false){
12034         
12035             this.setFromData(index > -1 ? record.data : false);
12036             
12037             this.collapse();
12038             this.fireEvent('select', this, record, index);
12039         }
12040     },
12041
12042     /**
12043      * Returns the currently selected field value or empty string if no value is set.
12044      * @return {String} value The selected value
12045      */
12046     getValue : function(){
12047         
12048         if(this.multiple){
12049             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12050         }
12051         
12052         if(this.valueField){
12053             return typeof this.value != 'undefined' ? this.value : '';
12054         }else{
12055             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12056         }
12057     },
12058
12059     /**
12060      * Clears any text/value currently set in the field
12061      */
12062     clearValue : function(){
12063         if(this.hiddenField){
12064             this.hiddenField.dom.value = '';
12065         }
12066         this.value = '';
12067         this.setRawValue('');
12068         this.lastSelectionText = '';
12069         this.lastData = false;
12070         
12071         var close = this.closeTriggerEl();
12072         
12073         if(close){
12074             close.hide();
12075         }
12076         
12077     },
12078
12079     /**
12080      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
12081      * will be displayed in the field.  If the value does not match the data value of an existing item,
12082      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12083      * Otherwise the field will be blank (although the value will still be set).
12084      * @param {String} value The value to match
12085      */
12086     setValue : function(v){
12087         if(this.multiple){
12088             this.syncValue();
12089             return;
12090         }
12091         
12092         var text = v;
12093         if(this.valueField){
12094             var r = this.findRecord(this.valueField, v);
12095             if(r){
12096                 text = r.data[this.displayField];
12097             }else if(this.valueNotFoundText !== undefined){
12098                 text = this.valueNotFoundText;
12099             }
12100         }
12101         this.lastSelectionText = text;
12102         if(this.hiddenField){
12103             this.hiddenField.dom.value = v;
12104         }
12105         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12106         this.value = v;
12107         
12108         var close = this.closeTriggerEl();
12109         
12110         if(close){
12111             (v.length || v * 1 > 0) ? close.show() : close.hide();
12112         }
12113     },
12114     /**
12115      * @property {Object} the last set data for the element
12116      */
12117     
12118     lastData : false,
12119     /**
12120      * Sets the value of the field based on a object which is related to the record format for the store.
12121      * @param {Object} value the value to set as. or false on reset?
12122      */
12123     setFromData : function(o){
12124         
12125         if(this.multiple){
12126             this.addItem(o);
12127             return;
12128         }
12129             
12130         var dv = ''; // display value
12131         var vv = ''; // value value..
12132         this.lastData = o;
12133         if (this.displayField) {
12134             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12135         } else {
12136             // this is an error condition!!!
12137             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12138         }
12139         
12140         if(this.valueField){
12141             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12142         }
12143         
12144         var close = this.closeTriggerEl();
12145         
12146         if(close){
12147             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12148         }
12149         
12150         if(this.hiddenField){
12151             this.hiddenField.dom.value = vv;
12152             
12153             this.lastSelectionText = dv;
12154             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12155             this.value = vv;
12156             return;
12157         }
12158         // no hidden field.. - we store the value in 'value', but still display
12159         // display field!!!!
12160         this.lastSelectionText = dv;
12161         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12162         this.value = vv;
12163         
12164         
12165         
12166     },
12167     // private
12168     reset : function(){
12169         // overridden so that last data is reset..
12170         
12171         if(this.multiple){
12172             this.clearItem();
12173             return;
12174         }
12175         
12176         this.setValue(this.originalValue);
12177         this.clearInvalid();
12178         this.lastData = false;
12179         if (this.view) {
12180             this.view.clearSelections();
12181         }
12182     },
12183     // private
12184     findRecord : function(prop, value){
12185         var record;
12186         if(this.store.getCount() > 0){
12187             this.store.each(function(r){
12188                 if(r.data[prop] == value){
12189                     record = r;
12190                     return false;
12191                 }
12192                 return true;
12193             });
12194         }
12195         return record;
12196     },
12197     
12198     getName: function()
12199     {
12200         // returns hidden if it's set..
12201         if (!this.rendered) {return ''};
12202         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
12203         
12204     },
12205     // private
12206     onViewMove : function(e, t){
12207         this.inKeyMode = false;
12208     },
12209
12210     // private
12211     onViewOver : function(e, t){
12212         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12213             return;
12214         }
12215         var item = this.view.findItemFromChild(t);
12216         
12217         if(item){
12218             var index = this.view.indexOf(item);
12219             this.select(index, false);
12220         }
12221     },
12222
12223     // private
12224     onViewClick : function(view, doFocus, el, e)
12225     {
12226         var index = this.view.getSelectedIndexes()[0];
12227         
12228         var r = this.store.getAt(index);
12229         
12230         if(this.tickable){
12231             
12232             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12233                 return;
12234             }
12235             
12236             var rm = false;
12237             var _this = this;
12238             
12239             Roo.each(this.tickItems, function(v,k){
12240                 
12241                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12242                     _this.tickItems.splice(k, 1);
12243                     
12244                     if(typeof(e) == 'undefined' && view == false){
12245                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12246                     }
12247                     
12248                     rm = true;
12249                     return;
12250                 }
12251             });
12252             
12253             if(rm){
12254                 return;
12255             }
12256             
12257             this.tickItems.push(r.data);
12258             
12259             if(typeof(e) == 'undefined' && view == false){
12260                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12261             }
12262                     
12263             return;
12264         }
12265         
12266         if(r){
12267             this.onSelect(r, index);
12268         }
12269         if(doFocus !== false && !this.blockFocus){
12270             this.inputEl().focus();
12271         }
12272     },
12273
12274     // private
12275     restrictHeight : function(){
12276         //this.innerList.dom.style.height = '';
12277         //var inner = this.innerList.dom;
12278         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12279         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12280         //this.list.beginUpdate();
12281         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12282         this.list.alignTo(this.inputEl(), this.listAlign);
12283         this.list.alignTo(this.inputEl(), this.listAlign);
12284         //this.list.endUpdate();
12285     },
12286
12287     // private
12288     onEmptyResults : function(){
12289         
12290         if(this.tickable && this.editable){
12291             this.restrictHeight();
12292             return;
12293         }
12294         
12295         this.collapse();
12296     },
12297
12298     /**
12299      * Returns true if the dropdown list is expanded, else false.
12300      */
12301     isExpanded : function(){
12302         return this.list.isVisible();
12303     },
12304
12305     /**
12306      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12307      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12308      * @param {String} value The data value of the item to select
12309      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12310      * selected item if it is not currently in view (defaults to true)
12311      * @return {Boolean} True if the value matched an item in the list, else false
12312      */
12313     selectByValue : function(v, scrollIntoView){
12314         if(v !== undefined && v !== null){
12315             var r = this.findRecord(this.valueField || this.displayField, v);
12316             if(r){
12317                 this.select(this.store.indexOf(r), scrollIntoView);
12318                 return true;
12319             }
12320         }
12321         return false;
12322     },
12323
12324     /**
12325      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12326      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12327      * @param {Number} index The zero-based index of the list item to select
12328      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12329      * selected item if it is not currently in view (defaults to true)
12330      */
12331     select : function(index, scrollIntoView){
12332         this.selectedIndex = index;
12333         this.view.select(index);
12334         if(scrollIntoView !== false){
12335             var el = this.view.getNode(index);
12336             /*
12337              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12338              */
12339             if(el){
12340                 this.list.scrollChildIntoView(el, false);
12341             }
12342         }
12343     },
12344
12345     // private
12346     selectNext : function(){
12347         var ct = this.store.getCount();
12348         if(ct > 0){
12349             if(this.selectedIndex == -1){
12350                 this.select(0);
12351             }else if(this.selectedIndex < ct-1){
12352                 this.select(this.selectedIndex+1);
12353             }
12354         }
12355     },
12356
12357     // private
12358     selectPrev : function(){
12359         var ct = this.store.getCount();
12360         if(ct > 0){
12361             if(this.selectedIndex == -1){
12362                 this.select(0);
12363             }else if(this.selectedIndex != 0){
12364                 this.select(this.selectedIndex-1);
12365             }
12366         }
12367     },
12368
12369     // private
12370     onKeyUp : function(e){
12371         if(this.editable !== false && !e.isSpecialKey()){
12372             this.lastKey = e.getKey();
12373             this.dqTask.delay(this.queryDelay);
12374         }
12375     },
12376
12377     // private
12378     validateBlur : function(){
12379         return !this.list || !this.list.isVisible();   
12380     },
12381
12382     // private
12383     initQuery : function(){
12384         
12385         var v = this.getRawValue();
12386         
12387         if(this.tickable && this.editable){
12388             v = this.tickableInputEl().getValue();
12389         }
12390         
12391         this.doQuery(v);
12392     },
12393
12394     // private
12395     doForce : function(){
12396         if(this.inputEl().dom.value.length > 0){
12397             this.inputEl().dom.value =
12398                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12399              
12400         }
12401     },
12402
12403     /**
12404      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
12405      * query allowing the query action to be canceled if needed.
12406      * @param {String} query The SQL query to execute
12407      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12408      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
12409      * saved in the current store (defaults to false)
12410      */
12411     doQuery : function(q, forceAll){
12412         
12413         if(q === undefined || q === null){
12414             q = '';
12415         }
12416         var qe = {
12417             query: q,
12418             forceAll: forceAll,
12419             combo: this,
12420             cancel:false
12421         };
12422         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12423             return false;
12424         }
12425         q = qe.query;
12426         
12427         forceAll = qe.forceAll;
12428         if(forceAll === true || (q.length >= this.minChars)){
12429             
12430             this.hasQuery = true;
12431             
12432             if(this.lastQuery != q || this.alwaysQuery){
12433                 this.lastQuery = q;
12434                 if(this.mode == 'local'){
12435                     this.selectedIndex = -1;
12436                     if(forceAll){
12437                         this.store.clearFilter();
12438                     }else{
12439                         
12440                         if(this.specialFilter){
12441                             this.fireEvent('specialfilter', this);
12442                             this.onLoad();
12443                             return;
12444                         }
12445                         
12446                         this.store.filter(this.displayField, q);
12447                     }
12448                     
12449                     this.store.fireEvent("datachanged", this.store);
12450                     
12451                     this.onLoad();
12452                     
12453                     
12454                 }else{
12455                     
12456                     this.store.baseParams[this.queryParam] = q;
12457                     
12458                     var options = {params : this.getParams(q)};
12459                     
12460                     if(this.loadNext){
12461                         options.add = true;
12462                         options.params.start = this.page * this.pageSize;
12463                     }
12464                     
12465                     this.store.load(options);
12466                     
12467                     /*
12468                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
12469                      *  we should expand the list on onLoad
12470                      *  so command out it
12471                      */
12472 //                    this.expand();
12473                 }
12474             }else{
12475                 this.selectedIndex = -1;
12476                 this.onLoad();   
12477             }
12478         }
12479         
12480         this.loadNext = false;
12481     },
12482     
12483     // private
12484     getParams : function(q){
12485         var p = {};
12486         //p[this.queryParam] = q;
12487         
12488         if(this.pageSize){
12489             p.start = 0;
12490             p.limit = this.pageSize;
12491         }
12492         return p;
12493     },
12494
12495     /**
12496      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12497      */
12498     collapse : function(){
12499         if(!this.isExpanded()){
12500             return;
12501         }
12502         
12503         this.list.hide();
12504         
12505         if(this.tickable){
12506             this.hasFocus = false;
12507             this.okBtn.hide();
12508             this.cancelBtn.hide();
12509             this.trigger.show();
12510             
12511             if(this.editable){
12512                 this.tickableInputEl().dom.value = '';
12513                 this.tickableInputEl().blur();
12514             }
12515             
12516         }
12517         
12518         Roo.get(document).un('mousedown', this.collapseIf, this);
12519         Roo.get(document).un('mousewheel', this.collapseIf, this);
12520         if (!this.editable) {
12521             Roo.get(document).un('keydown', this.listKeyPress, this);
12522         }
12523         this.fireEvent('collapse', this);
12524     },
12525
12526     // private
12527     collapseIf : function(e){
12528         var in_combo  = e.within(this.el);
12529         var in_list =  e.within(this.list);
12530         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12531         
12532         if (in_combo || in_list || is_list) {
12533             //e.stopPropagation();
12534             return;
12535         }
12536         
12537         if(this.tickable){
12538             this.onTickableFooterButtonClick(e, false, false);
12539         }
12540
12541         this.collapse();
12542         
12543     },
12544
12545     /**
12546      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
12547      */
12548     expand : function(){
12549        
12550         if(this.isExpanded() || !this.hasFocus){
12551             return;
12552         }
12553         
12554         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
12555         this.list.setWidth(lw);
12556         
12557         
12558          Roo.log('expand');
12559         
12560         this.list.show();
12561         
12562         this.restrictHeight();
12563         
12564         if(this.tickable){
12565             
12566             this.tickItems = Roo.apply([], this.item);
12567             
12568             this.okBtn.show();
12569             this.cancelBtn.show();
12570             this.trigger.hide();
12571             
12572             if(this.editable){
12573                 this.tickableInputEl().focus();
12574             }
12575             
12576         }
12577         
12578         Roo.get(document).on('mousedown', this.collapseIf, this);
12579         Roo.get(document).on('mousewheel', this.collapseIf, this);
12580         if (!this.editable) {
12581             Roo.get(document).on('keydown', this.listKeyPress, this);
12582         }
12583         
12584         this.fireEvent('expand', this);
12585     },
12586
12587     // private
12588     // Implements the default empty TriggerField.onTriggerClick function
12589     onTriggerClick : function(e)
12590     {
12591         Roo.log('trigger click');
12592         
12593         if(this.disabled || !this.triggerList){
12594             return;
12595         }
12596         
12597         this.page = 0;
12598         this.loadNext = false;
12599         
12600         if(this.isExpanded()){
12601             this.collapse();
12602             if (!this.blockFocus) {
12603                 this.inputEl().focus();
12604             }
12605             
12606         }else {
12607             this.hasFocus = true;
12608             if(this.triggerAction == 'all') {
12609                 this.doQuery(this.allQuery, true);
12610             } else {
12611                 this.doQuery(this.getRawValue());
12612             }
12613             if (!this.blockFocus) {
12614                 this.inputEl().focus();
12615             }
12616         }
12617     },
12618     
12619     onTickableTriggerClick : function(e)
12620     {
12621         if(this.disabled){
12622             return;
12623         }
12624         
12625         this.page = 0;
12626         this.loadNext = false;
12627         this.hasFocus = true;
12628         
12629         if(this.triggerAction == 'all') {
12630             this.doQuery(this.allQuery, true);
12631         } else {
12632             this.doQuery(this.getRawValue());
12633         }
12634     },
12635     
12636     onSearchFieldClick : function(e)
12637     {
12638         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
12639             this.onTickableFooterButtonClick(e, false, false);
12640             return;
12641         }
12642         
12643         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
12644             return;
12645         }
12646         
12647         this.page = 0;
12648         this.loadNext = false;
12649         this.hasFocus = true;
12650         
12651         if(this.triggerAction == 'all') {
12652             this.doQuery(this.allQuery, true);
12653         } else {
12654             this.doQuery(this.getRawValue());
12655         }
12656     },
12657     
12658     listKeyPress : function(e)
12659     {
12660         //Roo.log('listkeypress');
12661         // scroll to first matching element based on key pres..
12662         if (e.isSpecialKey()) {
12663             return false;
12664         }
12665         var k = String.fromCharCode(e.getKey()).toUpperCase();
12666         //Roo.log(k);
12667         var match  = false;
12668         var csel = this.view.getSelectedNodes();
12669         var cselitem = false;
12670         if (csel.length) {
12671             var ix = this.view.indexOf(csel[0]);
12672             cselitem  = this.store.getAt(ix);
12673             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
12674                 cselitem = false;
12675             }
12676             
12677         }
12678         
12679         this.store.each(function(v) { 
12680             if (cselitem) {
12681                 // start at existing selection.
12682                 if (cselitem.id == v.id) {
12683                     cselitem = false;
12684                 }
12685                 return true;
12686             }
12687                 
12688             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
12689                 match = this.store.indexOf(v);
12690                 return false;
12691             }
12692             return true;
12693         }, this);
12694         
12695         if (match === false) {
12696             return true; // no more action?
12697         }
12698         // scroll to?
12699         this.view.select(match);
12700         var sn = Roo.get(this.view.getSelectedNodes()[0])
12701         sn.scrollIntoView(sn.dom.parentNode, false);
12702     },
12703     
12704     onViewScroll : function(e, t){
12705         
12706         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){
12707             return;
12708         }
12709         
12710         this.hasQuery = true;
12711         
12712         this.loading = this.list.select('.loading', true).first();
12713         
12714         if(this.loading === null){
12715             this.list.createChild({
12716                 tag: 'div',
12717                 cls: 'loading select2-more-results select2-active',
12718                 html: 'Loading more results...'
12719             })
12720             
12721             this.loading = this.list.select('.loading', true).first();
12722             
12723             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
12724             
12725             this.loading.hide();
12726         }
12727         
12728         this.loading.show();
12729         
12730         var _combo = this;
12731         
12732         this.page++;
12733         this.loadNext = true;
12734         
12735         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
12736         
12737         return;
12738     },
12739     
12740     addItem : function(o)
12741     {   
12742         var dv = ''; // display value
12743         
12744         if (this.displayField) {
12745             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12746         } else {
12747             // this is an error condition!!!
12748             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12749         }
12750         
12751         if(!dv.length){
12752             return;
12753         }
12754         
12755         var choice = this.choices.createChild({
12756             tag: 'li',
12757             cls: 'select2-search-choice',
12758             cn: [
12759                 {
12760                     tag: 'div',
12761                     html: dv
12762                 },
12763                 {
12764                     tag: 'a',
12765                     href: '#',
12766                     cls: 'select2-search-choice-close',
12767                     tabindex: '-1'
12768                 }
12769             ]
12770             
12771         }, this.searchField);
12772         
12773         var close = choice.select('a.select2-search-choice-close', true).first()
12774         
12775         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
12776         
12777         this.item.push(o);
12778         
12779         this.lastData = o;
12780         
12781         this.syncValue();
12782         
12783         this.inputEl().dom.value = '';
12784         
12785         this.validate();
12786     },
12787     
12788     onRemoveItem : function(e, _self, o)
12789     {
12790         e.preventDefault();
12791         
12792         this.lastItem = Roo.apply([], this.item);
12793         
12794         var index = this.item.indexOf(o.data) * 1;
12795         
12796         if( index < 0){
12797             Roo.log('not this item?!');
12798             return;
12799         }
12800         
12801         this.item.splice(index, 1);
12802         o.item.remove();
12803         
12804         this.syncValue();
12805         
12806         this.fireEvent('remove', this, e);
12807         
12808         this.validate();
12809         
12810     },
12811     
12812     syncValue : function()
12813     {
12814         if(!this.item.length){
12815             this.clearValue();
12816             return;
12817         }
12818             
12819         var value = [];
12820         var _this = this;
12821         Roo.each(this.item, function(i){
12822             if(_this.valueField){
12823                 value.push(i[_this.valueField]);
12824                 return;
12825             }
12826
12827             value.push(i);
12828         });
12829
12830         this.value = value.join(',');
12831
12832         if(this.hiddenField){
12833             this.hiddenField.dom.value = this.value;
12834         }
12835         
12836         this.store.fireEvent("datachanged", this.store);
12837     },
12838     
12839     clearItem : function()
12840     {
12841         if(!this.multiple){
12842             return;
12843         }
12844         
12845         this.item = [];
12846         
12847         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
12848            c.remove();
12849         });
12850         
12851         this.syncValue();
12852         
12853         this.validate();
12854     },
12855     
12856     inputEl: function ()
12857     {
12858         if(this.tickable){
12859             return this.searchField;
12860         }
12861         return this.el.select('input.form-control',true).first();
12862     },
12863     
12864     
12865     onTickableFooterButtonClick : function(e, btn, el)
12866     {
12867         e.preventDefault();
12868         
12869         this.lastItem = Roo.apply([], this.item);
12870         
12871         if(btn && btn.name == 'cancel'){
12872             this.tickItems = Roo.apply([], this.item);
12873             this.collapse();
12874             return;
12875         }
12876         
12877         this.clearItem();
12878         
12879         var _this = this;
12880         
12881         Roo.each(this.tickItems, function(o){
12882             _this.addItem(o);
12883         });
12884         
12885         this.collapse();
12886         
12887     },
12888     
12889     validate : function()
12890     {
12891         var v = this.getRawValue();
12892         
12893         if(this.multiple){
12894             v = this.getValue();
12895         }
12896         
12897         if(this.disabled || this.allowBlank || v.length){
12898             this.markValid();
12899             return true;
12900         }
12901         
12902         this.markInvalid();
12903         return false;
12904     },
12905     
12906     tickableInputEl : function()
12907     {
12908         if(!this.tickable || !this.editable){
12909             return this.inputEl();
12910         }
12911         
12912         return this.inputEl().select('.select2-search-field-input', true).first();
12913     }
12914     
12915     
12916
12917     /** 
12918     * @cfg {Boolean} grow 
12919     * @hide 
12920     */
12921     /** 
12922     * @cfg {Number} growMin 
12923     * @hide 
12924     */
12925     /** 
12926     * @cfg {Number} growMax 
12927     * @hide 
12928     */
12929     /**
12930      * @hide
12931      * @method autoSize
12932      */
12933 });
12934 /*
12935  * Based on:
12936  * Ext JS Library 1.1.1
12937  * Copyright(c) 2006-2007, Ext JS, LLC.
12938  *
12939  * Originally Released Under LGPL - original licence link has changed is not relivant.
12940  *
12941  * Fork - LGPL
12942  * <script type="text/javascript">
12943  */
12944
12945 /**
12946  * @class Roo.View
12947  * @extends Roo.util.Observable
12948  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
12949  * This class also supports single and multi selection modes. <br>
12950  * Create a data model bound view:
12951  <pre><code>
12952  var store = new Roo.data.Store(...);
12953
12954  var view = new Roo.View({
12955     el : "my-element",
12956     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
12957  
12958     singleSelect: true,
12959     selectedClass: "ydataview-selected",
12960     store: store
12961  });
12962
12963  // listen for node click?
12964  view.on("click", function(vw, index, node, e){
12965  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
12966  });
12967
12968  // load XML data
12969  dataModel.load("foobar.xml");
12970  </code></pre>
12971  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
12972  * <br><br>
12973  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
12974  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
12975  * 
12976  * Note: old style constructor is still suported (container, template, config)
12977  * 
12978  * @constructor
12979  * Create a new View
12980  * @param {Object} config The config object
12981  * 
12982  */
12983 Roo.View = function(config, depreciated_tpl, depreciated_config){
12984     
12985     this.parent = false;
12986     
12987     if (typeof(depreciated_tpl) == 'undefined') {
12988         // new way.. - universal constructor.
12989         Roo.apply(this, config);
12990         this.el  = Roo.get(this.el);
12991     } else {
12992         // old format..
12993         this.el  = Roo.get(config);
12994         this.tpl = depreciated_tpl;
12995         Roo.apply(this, depreciated_config);
12996     }
12997     this.wrapEl  = this.el.wrap().wrap();
12998     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
12999     
13000     
13001     if(typeof(this.tpl) == "string"){
13002         this.tpl = new Roo.Template(this.tpl);
13003     } else {
13004         // support xtype ctors..
13005         this.tpl = new Roo.factory(this.tpl, Roo);
13006     }
13007     
13008     
13009     this.tpl.compile();
13010     
13011     /** @private */
13012     this.addEvents({
13013         /**
13014          * @event beforeclick
13015          * Fires before a click is processed. Returns false to cancel the default action.
13016          * @param {Roo.View} this
13017          * @param {Number} index The index of the target node
13018          * @param {HTMLElement} node The target node
13019          * @param {Roo.EventObject} e The raw event object
13020          */
13021             "beforeclick" : true,
13022         /**
13023          * @event click
13024          * Fires when a template node is clicked.
13025          * @param {Roo.View} this
13026          * @param {Number} index The index of the target node
13027          * @param {HTMLElement} node The target node
13028          * @param {Roo.EventObject} e The raw event object
13029          */
13030             "click" : true,
13031         /**
13032          * @event dblclick
13033          * Fires when a template node is double clicked.
13034          * @param {Roo.View} this
13035          * @param {Number} index The index of the target node
13036          * @param {HTMLElement} node The target node
13037          * @param {Roo.EventObject} e The raw event object
13038          */
13039             "dblclick" : true,
13040         /**
13041          * @event contextmenu
13042          * Fires when a template node is right clicked.
13043          * @param {Roo.View} this
13044          * @param {Number} index The index of the target node
13045          * @param {HTMLElement} node The target node
13046          * @param {Roo.EventObject} e The raw event object
13047          */
13048             "contextmenu" : true,
13049         /**
13050          * @event selectionchange
13051          * Fires when the selected nodes change.
13052          * @param {Roo.View} this
13053          * @param {Array} selections Array of the selected nodes
13054          */
13055             "selectionchange" : true,
13056     
13057         /**
13058          * @event beforeselect
13059          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
13060          * @param {Roo.View} this
13061          * @param {HTMLElement} node The node to be selected
13062          * @param {Array} selections Array of currently selected nodes
13063          */
13064             "beforeselect" : true,
13065         /**
13066          * @event preparedata
13067          * Fires on every row to render, to allow you to change the data.
13068          * @param {Roo.View} this
13069          * @param {Object} data to be rendered (change this)
13070          */
13071           "preparedata" : true
13072           
13073           
13074         });
13075
13076
13077
13078     this.el.on({
13079         "click": this.onClick,
13080         "dblclick": this.onDblClick,
13081         "contextmenu": this.onContextMenu,
13082         scope:this
13083     });
13084
13085     this.selections = [];
13086     this.nodes = [];
13087     this.cmp = new Roo.CompositeElementLite([]);
13088     if(this.store){
13089         this.store = Roo.factory(this.store, Roo.data);
13090         this.setStore(this.store, true);
13091     }
13092     
13093     if ( this.footer && this.footer.xtype) {
13094            
13095          var fctr = this.wrapEl.appendChild(document.createElement("div"));
13096         
13097         this.footer.dataSource = this.store
13098         this.footer.container = fctr;
13099         this.footer = Roo.factory(this.footer, Roo);
13100         fctr.insertFirst(this.el);
13101         
13102         // this is a bit insane - as the paging toolbar seems to detach the el..
13103 //        dom.parentNode.parentNode.parentNode
13104          // they get detached?
13105     }
13106     
13107     
13108     Roo.View.superclass.constructor.call(this);
13109     
13110     
13111 };
13112
13113 Roo.extend(Roo.View, Roo.util.Observable, {
13114     
13115      /**
13116      * @cfg {Roo.data.Store} store Data store to load data from.
13117      */
13118     store : false,
13119     
13120     /**
13121      * @cfg {String|Roo.Element} el The container element.
13122      */
13123     el : '',
13124     
13125     /**
13126      * @cfg {String|Roo.Template} tpl The template used by this View 
13127      */
13128     tpl : false,
13129     /**
13130      * @cfg {String} dataName the named area of the template to use as the data area
13131      *                          Works with domtemplates roo-name="name"
13132      */
13133     dataName: false,
13134     /**
13135      * @cfg {String} selectedClass The css class to add to selected nodes
13136      */
13137     selectedClass : "x-view-selected",
13138      /**
13139      * @cfg {String} emptyText The empty text to show when nothing is loaded.
13140      */
13141     emptyText : "",
13142     
13143     /**
13144      * @cfg {String} text to display on mask (default Loading)
13145      */
13146     mask : false,
13147     /**
13148      * @cfg {Boolean} multiSelect Allow multiple selection
13149      */
13150     multiSelect : false,
13151     /**
13152      * @cfg {Boolean} singleSelect Allow single selection
13153      */
13154     singleSelect:  false,
13155     
13156     /**
13157      * @cfg {Boolean} toggleSelect - selecting 
13158      */
13159     toggleSelect : false,
13160     
13161     /**
13162      * @cfg {Boolean} tickable - selecting 
13163      */
13164     tickable : false,
13165     
13166     /**
13167      * Returns the element this view is bound to.
13168      * @return {Roo.Element}
13169      */
13170     getEl : function(){
13171         return this.wrapEl;
13172     },
13173     
13174     
13175
13176     /**
13177      * Refreshes the view. - called by datachanged on the store. - do not call directly.
13178      */
13179     refresh : function(){
13180         //Roo.log('refresh');
13181         var t = this.tpl;
13182         
13183         // if we are using something like 'domtemplate', then
13184         // the what gets used is:
13185         // t.applySubtemplate(NAME, data, wrapping data..)
13186         // the outer template then get' applied with
13187         //     the store 'extra data'
13188         // and the body get's added to the
13189         //      roo-name="data" node?
13190         //      <span class='roo-tpl-{name}'></span> ?????
13191         
13192         
13193         
13194         this.clearSelections();
13195         this.el.update("");
13196         var html = [];
13197         var records = this.store.getRange();
13198         if(records.length < 1) {
13199             
13200             // is this valid??  = should it render a template??
13201             
13202             this.el.update(this.emptyText);
13203             return;
13204         }
13205         var el = this.el;
13206         if (this.dataName) {
13207             this.el.update(t.apply(this.store.meta)); //????
13208             el = this.el.child('.roo-tpl-' + this.dataName);
13209         }
13210         
13211         for(var i = 0, len = records.length; i < len; i++){
13212             var data = this.prepareData(records[i].data, i, records[i]);
13213             this.fireEvent("preparedata", this, data, i, records[i]);
13214             
13215             var d = Roo.apply({}, data);
13216             
13217             if(this.tickable){
13218                 Roo.apply(d, {'roo-id' : Roo.id()});
13219                 
13220                 var _this = this;
13221             
13222                 Roo.each(this.parent.item, function(item){
13223                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
13224                         return;
13225                     }
13226                     Roo.apply(d, {'roo-data-checked' : 'checked'});
13227                 });
13228             }
13229             
13230             html[html.length] = Roo.util.Format.trim(
13231                 this.dataName ?
13232                     t.applySubtemplate(this.dataName, d, this.store.meta) :
13233                     t.apply(d)
13234             );
13235         }
13236         
13237         
13238         
13239         el.update(html.join(""));
13240         this.nodes = el.dom.childNodes;
13241         this.updateIndexes(0);
13242     },
13243     
13244
13245     /**
13246      * Function to override to reformat the data that is sent to
13247      * the template for each node.
13248      * DEPRICATED - use the preparedata event handler.
13249      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
13250      * a JSON object for an UpdateManager bound view).
13251      */
13252     prepareData : function(data, index, record)
13253     {
13254         this.fireEvent("preparedata", this, data, index, record);
13255         return data;
13256     },
13257
13258     onUpdate : function(ds, record){
13259         // Roo.log('on update');   
13260         this.clearSelections();
13261         var index = this.store.indexOf(record);
13262         var n = this.nodes[index];
13263         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
13264         n.parentNode.removeChild(n);
13265         this.updateIndexes(index, index);
13266     },
13267
13268     
13269     
13270 // --------- FIXME     
13271     onAdd : function(ds, records, index)
13272     {
13273         //Roo.log(['on Add', ds, records, index] );        
13274         this.clearSelections();
13275         if(this.nodes.length == 0){
13276             this.refresh();
13277             return;
13278         }
13279         var n = this.nodes[index];
13280         for(var i = 0, len = records.length; i < len; i++){
13281             var d = this.prepareData(records[i].data, i, records[i]);
13282             if(n){
13283                 this.tpl.insertBefore(n, d);
13284             }else{
13285                 
13286                 this.tpl.append(this.el, d);
13287             }
13288         }
13289         this.updateIndexes(index);
13290     },
13291
13292     onRemove : function(ds, record, index){
13293        // Roo.log('onRemove');
13294         this.clearSelections();
13295         var el = this.dataName  ?
13296             this.el.child('.roo-tpl-' + this.dataName) :
13297             this.el; 
13298         
13299         el.dom.removeChild(this.nodes[index]);
13300         this.updateIndexes(index);
13301     },
13302
13303     /**
13304      * Refresh an individual node.
13305      * @param {Number} index
13306      */
13307     refreshNode : function(index){
13308         this.onUpdate(this.store, this.store.getAt(index));
13309     },
13310
13311     updateIndexes : function(startIndex, endIndex){
13312         var ns = this.nodes;
13313         startIndex = startIndex || 0;
13314         endIndex = endIndex || ns.length - 1;
13315         for(var i = startIndex; i <= endIndex; i++){
13316             ns[i].nodeIndex = i;
13317         }
13318     },
13319
13320     /**
13321      * Changes the data store this view uses and refresh the view.
13322      * @param {Store} store
13323      */
13324     setStore : function(store, initial){
13325         if(!initial && this.store){
13326             this.store.un("datachanged", this.refresh);
13327             this.store.un("add", this.onAdd);
13328             this.store.un("remove", this.onRemove);
13329             this.store.un("update", this.onUpdate);
13330             this.store.un("clear", this.refresh);
13331             this.store.un("beforeload", this.onBeforeLoad);
13332             this.store.un("load", this.onLoad);
13333             this.store.un("loadexception", this.onLoad);
13334         }
13335         if(store){
13336           
13337             store.on("datachanged", this.refresh, this);
13338             store.on("add", this.onAdd, this);
13339             store.on("remove", this.onRemove, this);
13340             store.on("update", this.onUpdate, this);
13341             store.on("clear", this.refresh, this);
13342             store.on("beforeload", this.onBeforeLoad, this);
13343             store.on("load", this.onLoad, this);
13344             store.on("loadexception", this.onLoad, this);
13345         }
13346         
13347         if(store){
13348             this.refresh();
13349         }
13350     },
13351     /**
13352      * onbeforeLoad - masks the loading area.
13353      *
13354      */
13355     onBeforeLoad : function(store,opts)
13356     {
13357          //Roo.log('onBeforeLoad');   
13358         if (!opts.add) {
13359             this.el.update("");
13360         }
13361         this.el.mask(this.mask ? this.mask : "Loading" ); 
13362     },
13363     onLoad : function ()
13364     {
13365         this.el.unmask();
13366     },
13367     
13368
13369     /**
13370      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
13371      * @param {HTMLElement} node
13372      * @return {HTMLElement} The template node
13373      */
13374     findItemFromChild : function(node){
13375         var el = this.dataName  ?
13376             this.el.child('.roo-tpl-' + this.dataName,true) :
13377             this.el.dom; 
13378         
13379         if(!node || node.parentNode == el){
13380                     return node;
13381             }
13382             var p = node.parentNode;
13383             while(p && p != el){
13384             if(p.parentNode == el){
13385                 return p;
13386             }
13387             p = p.parentNode;
13388         }
13389             return null;
13390     },
13391
13392     /** @ignore */
13393     onClick : function(e){
13394         var item = this.findItemFromChild(e.getTarget());
13395         if(item){
13396             var index = this.indexOf(item);
13397             if(this.onItemClick(item, index, e) !== false){
13398                 this.fireEvent("click", this, index, item, e);
13399             }
13400         }else{
13401             this.clearSelections();
13402         }
13403     },
13404
13405     /** @ignore */
13406     onContextMenu : function(e){
13407         var item = this.findItemFromChild(e.getTarget());
13408         if(item){
13409             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
13410         }
13411     },
13412
13413     /** @ignore */
13414     onDblClick : function(e){
13415         var item = this.findItemFromChild(e.getTarget());
13416         if(item){
13417             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
13418         }
13419     },
13420
13421     onItemClick : function(item, index, e)
13422     {
13423         if(this.fireEvent("beforeclick", this, index, item, e) === false){
13424             return false;
13425         }
13426         if (this.toggleSelect) {
13427             var m = this.isSelected(item) ? 'unselect' : 'select';
13428             //Roo.log(m);
13429             var _t = this;
13430             _t[m](item, true, false);
13431             return true;
13432         }
13433         if(this.multiSelect || this.singleSelect){
13434             if(this.multiSelect && e.shiftKey && this.lastSelection){
13435                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
13436             }else{
13437                 this.select(item, this.multiSelect && e.ctrlKey);
13438                 this.lastSelection = item;
13439             }
13440             
13441             if(!this.tickable){
13442                 e.preventDefault();
13443             }
13444             
13445         }
13446         return true;
13447     },
13448
13449     /**
13450      * Get the number of selected nodes.
13451      * @return {Number}
13452      */
13453     getSelectionCount : function(){
13454         return this.selections.length;
13455     },
13456
13457     /**
13458      * Get the currently selected nodes.
13459      * @return {Array} An array of HTMLElements
13460      */
13461     getSelectedNodes : function(){
13462         return this.selections;
13463     },
13464
13465     /**
13466      * Get the indexes of the selected nodes.
13467      * @return {Array}
13468      */
13469     getSelectedIndexes : function(){
13470         var indexes = [], s = this.selections;
13471         for(var i = 0, len = s.length; i < len; i++){
13472             indexes.push(s[i].nodeIndex);
13473         }
13474         return indexes;
13475     },
13476
13477     /**
13478      * Clear all selections
13479      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
13480      */
13481     clearSelections : function(suppressEvent){
13482         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
13483             this.cmp.elements = this.selections;
13484             this.cmp.removeClass(this.selectedClass);
13485             this.selections = [];
13486             if(!suppressEvent){
13487                 this.fireEvent("selectionchange", this, this.selections);
13488             }
13489         }
13490     },
13491
13492     /**
13493      * Returns true if the passed node is selected
13494      * @param {HTMLElement/Number} node The node or node index
13495      * @return {Boolean}
13496      */
13497     isSelected : function(node){
13498         var s = this.selections;
13499         if(s.length < 1){
13500             return false;
13501         }
13502         node = this.getNode(node);
13503         return s.indexOf(node) !== -1;
13504     },
13505
13506     /**
13507      * Selects nodes.
13508      * @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
13509      * @param {Boolean} keepExisting (optional) true to keep existing selections
13510      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
13511      */
13512     select : function(nodeInfo, keepExisting, suppressEvent){
13513         if(nodeInfo instanceof Array){
13514             if(!keepExisting){
13515                 this.clearSelections(true);
13516             }
13517             for(var i = 0, len = nodeInfo.length; i < len; i++){
13518                 this.select(nodeInfo[i], true, true);
13519             }
13520             return;
13521         } 
13522         var node = this.getNode(nodeInfo);
13523         if(!node || this.isSelected(node)){
13524             return; // already selected.
13525         }
13526         if(!keepExisting){
13527             this.clearSelections(true);
13528         }
13529         
13530         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
13531             Roo.fly(node).addClass(this.selectedClass);
13532             this.selections.push(node);
13533             if(!suppressEvent){
13534                 this.fireEvent("selectionchange", this, this.selections);
13535             }
13536         }
13537         
13538         
13539     },
13540       /**
13541      * Unselects nodes.
13542      * @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
13543      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
13544      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
13545      */
13546     unselect : function(nodeInfo, keepExisting, suppressEvent)
13547     {
13548         if(nodeInfo instanceof Array){
13549             Roo.each(this.selections, function(s) {
13550                 this.unselect(s, nodeInfo);
13551             }, this);
13552             return;
13553         }
13554         var node = this.getNode(nodeInfo);
13555         if(!node || !this.isSelected(node)){
13556             //Roo.log("not selected");
13557             return; // not selected.
13558         }
13559         // fireevent???
13560         var ns = [];
13561         Roo.each(this.selections, function(s) {
13562             if (s == node ) {
13563                 Roo.fly(node).removeClass(this.selectedClass);
13564
13565                 return;
13566             }
13567             ns.push(s);
13568         },this);
13569         
13570         this.selections= ns;
13571         this.fireEvent("selectionchange", this, this.selections);
13572     },
13573
13574     /**
13575      * Gets a template node.
13576      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
13577      * @return {HTMLElement} The node or null if it wasn't found
13578      */
13579     getNode : function(nodeInfo){
13580         if(typeof nodeInfo == "string"){
13581             return document.getElementById(nodeInfo);
13582         }else if(typeof nodeInfo == "number"){
13583             return this.nodes[nodeInfo];
13584         }
13585         return nodeInfo;
13586     },
13587
13588     /**
13589      * Gets a range template nodes.
13590      * @param {Number} startIndex
13591      * @param {Number} endIndex
13592      * @return {Array} An array of nodes
13593      */
13594     getNodes : function(start, end){
13595         var ns = this.nodes;
13596         start = start || 0;
13597         end = typeof end == "undefined" ? ns.length - 1 : end;
13598         var nodes = [];
13599         if(start <= end){
13600             for(var i = start; i <= end; i++){
13601                 nodes.push(ns[i]);
13602             }
13603         } else{
13604             for(var i = start; i >= end; i--){
13605                 nodes.push(ns[i]);
13606             }
13607         }
13608         return nodes;
13609     },
13610
13611     /**
13612      * Finds the index of the passed node
13613      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
13614      * @return {Number} The index of the node or -1
13615      */
13616     indexOf : function(node){
13617         node = this.getNode(node);
13618         if(typeof node.nodeIndex == "number"){
13619             return node.nodeIndex;
13620         }
13621         var ns = this.nodes;
13622         for(var i = 0, len = ns.length; i < len; i++){
13623             if(ns[i] == node){
13624                 return i;
13625             }
13626         }
13627         return -1;
13628     }
13629 });
13630 /*
13631  * - LGPL
13632  *
13633  * based on jquery fullcalendar
13634  * 
13635  */
13636
13637 Roo.bootstrap = Roo.bootstrap || {};
13638 /**
13639  * @class Roo.bootstrap.Calendar
13640  * @extends Roo.bootstrap.Component
13641  * Bootstrap Calendar class
13642  * @cfg {Boolean} loadMask (true|false) default false
13643  * @cfg {Object} header generate the user specific header of the calendar, default false
13644
13645  * @constructor
13646  * Create a new Container
13647  * @param {Object} config The config object
13648  */
13649
13650
13651
13652 Roo.bootstrap.Calendar = function(config){
13653     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
13654      this.addEvents({
13655         /**
13656              * @event select
13657              * Fires when a date is selected
13658              * @param {DatePicker} this
13659              * @param {Date} date The selected date
13660              */
13661         'select': true,
13662         /**
13663              * @event monthchange
13664              * Fires when the displayed month changes 
13665              * @param {DatePicker} this
13666              * @param {Date} date The selected month
13667              */
13668         'monthchange': true,
13669         /**
13670              * @event evententer
13671              * Fires when mouse over an event
13672              * @param {Calendar} this
13673              * @param {event} Event
13674              */
13675         'evententer': true,
13676         /**
13677              * @event eventleave
13678              * Fires when the mouse leaves an
13679              * @param {Calendar} this
13680              * @param {event}
13681              */
13682         'eventleave': true,
13683         /**
13684              * @event eventclick
13685              * Fires when the mouse click an
13686              * @param {Calendar} this
13687              * @param {event}
13688              */
13689         'eventclick': true
13690         
13691     });
13692
13693 };
13694
13695 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
13696     
13697      /**
13698      * @cfg {Number} startDay
13699      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
13700      */
13701     startDay : 0,
13702     
13703     loadMask : false,
13704     
13705     header : false,
13706       
13707     getAutoCreate : function(){
13708         
13709         
13710         var fc_button = function(name, corner, style, content ) {
13711             return Roo.apply({},{
13712                 tag : 'span',
13713                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
13714                          (corner.length ?
13715                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
13716                             ''
13717                         ),
13718                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
13719                 unselectable: 'on'
13720             });
13721         };
13722         
13723         var header = {};
13724         
13725         if(!this.header){
13726             header = {
13727                 tag : 'table',
13728                 cls : 'fc-header',
13729                 style : 'width:100%',
13730                 cn : [
13731                     {
13732                         tag: 'tr',
13733                         cn : [
13734                             {
13735                                 tag : 'td',
13736                                 cls : 'fc-header-left',
13737                                 cn : [
13738                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
13739                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
13740                                     { tag: 'span', cls: 'fc-header-space' },
13741                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
13742
13743
13744                                 ]
13745                             },
13746
13747                             {
13748                                 tag : 'td',
13749                                 cls : 'fc-header-center',
13750                                 cn : [
13751                                     {
13752                                         tag: 'span',
13753                                         cls: 'fc-header-title',
13754                                         cn : {
13755                                             tag: 'H2',
13756                                             html : 'month / year'
13757                                         }
13758                                     }
13759
13760                                 ]
13761                             },
13762                             {
13763                                 tag : 'td',
13764                                 cls : 'fc-header-right',
13765                                 cn : [
13766                               /*      fc_button('month', 'left', '', 'month' ),
13767                                     fc_button('week', '', '', 'week' ),
13768                                     fc_button('day', 'right', '', 'day' )
13769                                 */    
13770
13771                                 ]
13772                             }
13773
13774                         ]
13775                     }
13776                 ]
13777             };
13778         }
13779         
13780         header = this.header;
13781         
13782        
13783         var cal_heads = function() {
13784             var ret = [];
13785             // fixme - handle this.
13786             
13787             for (var i =0; i < Date.dayNames.length; i++) {
13788                 var d = Date.dayNames[i];
13789                 ret.push({
13790                     tag: 'th',
13791                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
13792                     html : d.substring(0,3)
13793                 });
13794                 
13795             }
13796             ret[0].cls += ' fc-first';
13797             ret[6].cls += ' fc-last';
13798             return ret;
13799         };
13800         var cal_cell = function(n) {
13801             return  {
13802                 tag: 'td',
13803                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
13804                 cn : [
13805                     {
13806                         cn : [
13807                             {
13808                                 cls: 'fc-day-number',
13809                                 html: 'D'
13810                             },
13811                             {
13812                                 cls: 'fc-day-content',
13813                              
13814                                 cn : [
13815                                      {
13816                                         style: 'position: relative;' // height: 17px;
13817                                     }
13818                                 ]
13819                             }
13820                             
13821                             
13822                         ]
13823                     }
13824                 ]
13825                 
13826             }
13827         };
13828         var cal_rows = function() {
13829             
13830             var ret = [];
13831             for (var r = 0; r < 6; r++) {
13832                 var row= {
13833                     tag : 'tr',
13834                     cls : 'fc-week',
13835                     cn : []
13836                 };
13837                 
13838                 for (var i =0; i < Date.dayNames.length; i++) {
13839                     var d = Date.dayNames[i];
13840                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
13841
13842                 }
13843                 row.cn[0].cls+=' fc-first';
13844                 row.cn[0].cn[0].style = 'min-height:90px';
13845                 row.cn[6].cls+=' fc-last';
13846                 ret.push(row);
13847                 
13848             }
13849             ret[0].cls += ' fc-first';
13850             ret[4].cls += ' fc-prev-last';
13851             ret[5].cls += ' fc-last';
13852             return ret;
13853             
13854         };
13855         
13856         var cal_table = {
13857             tag: 'table',
13858             cls: 'fc-border-separate',
13859             style : 'width:100%',
13860             cellspacing  : 0,
13861             cn : [
13862                 { 
13863                     tag: 'thead',
13864                     cn : [
13865                         { 
13866                             tag: 'tr',
13867                             cls : 'fc-first fc-last',
13868                             cn : cal_heads()
13869                         }
13870                     ]
13871                 },
13872                 { 
13873                     tag: 'tbody',
13874                     cn : cal_rows()
13875                 }
13876                   
13877             ]
13878         };
13879          
13880          var cfg = {
13881             cls : 'fc fc-ltr',
13882             cn : [
13883                 header,
13884                 {
13885                     cls : 'fc-content',
13886                     style : "position: relative;",
13887                     cn : [
13888                         {
13889                             cls : 'fc-view fc-view-month fc-grid',
13890                             style : 'position: relative',
13891                             unselectable : 'on',
13892                             cn : [
13893                                 {
13894                                     cls : 'fc-event-container',
13895                                     style : 'position:absolute;z-index:8;top:0;left:0;'
13896                                 },
13897                                 cal_table
13898                             ]
13899                         }
13900                     ]
13901     
13902                 }
13903            ] 
13904             
13905         };
13906         
13907          
13908         
13909         return cfg;
13910     },
13911     
13912     
13913     initEvents : function()
13914     {
13915         if(!this.store){
13916             throw "can not find store for calendar";
13917         }
13918         
13919         var mark = {
13920             tag: "div",
13921             cls:"x-dlg-mask",
13922             style: "text-align:center",
13923             cn: [
13924                 {
13925                     tag: "div",
13926                     style: "background-color:white;width:50%;margin:250 auto",
13927                     cn: [
13928                         {
13929                             tag: "img",
13930                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
13931                         },
13932                         {
13933                             tag: "span",
13934                             html: "Loading"
13935                         }
13936                         
13937                     ]
13938                 }
13939             ]
13940         }
13941         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
13942         
13943         var size = this.el.select('.fc-content', true).first().getSize();
13944         this.maskEl.setSize(size.width, size.height);
13945         this.maskEl.enableDisplayMode("block");
13946         if(!this.loadMask){
13947             this.maskEl.hide();
13948         }
13949         
13950         this.store = Roo.factory(this.store, Roo.data);
13951         this.store.on('load', this.onLoad, this);
13952         this.store.on('beforeload', this.onBeforeLoad, this);
13953         
13954         this.resize();
13955         
13956         this.cells = this.el.select('.fc-day',true);
13957         //Roo.log(this.cells);
13958         this.textNodes = this.el.query('.fc-day-number');
13959         this.cells.addClassOnOver('fc-state-hover');
13960         
13961         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
13962         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
13963         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
13964         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
13965         
13966         this.on('monthchange', this.onMonthChange, this);
13967         
13968         this.update(new Date().clearTime());
13969     },
13970     
13971     resize : function() {
13972         var sz  = this.el.getSize();
13973         
13974         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
13975         this.el.select('.fc-day-content div',true).setHeight(34);
13976     },
13977     
13978     
13979     // private
13980     showPrevMonth : function(e){
13981         this.update(this.activeDate.add("mo", -1));
13982     },
13983     showToday : function(e){
13984         this.update(new Date().clearTime());
13985     },
13986     // private
13987     showNextMonth : function(e){
13988         this.update(this.activeDate.add("mo", 1));
13989     },
13990
13991     // private
13992     showPrevYear : function(){
13993         this.update(this.activeDate.add("y", -1));
13994     },
13995
13996     // private
13997     showNextYear : function(){
13998         this.update(this.activeDate.add("y", 1));
13999     },
14000
14001     
14002    // private
14003     update : function(date)
14004     {
14005         var vd = this.activeDate;
14006         this.activeDate = date;
14007 //        if(vd && this.el){
14008 //            var t = date.getTime();
14009 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
14010 //                Roo.log('using add remove');
14011 //                
14012 //                this.fireEvent('monthchange', this, date);
14013 //                
14014 //                this.cells.removeClass("fc-state-highlight");
14015 //                this.cells.each(function(c){
14016 //                   if(c.dateValue == t){
14017 //                       c.addClass("fc-state-highlight");
14018 //                       setTimeout(function(){
14019 //                            try{c.dom.firstChild.focus();}catch(e){}
14020 //                       }, 50);
14021 //                       return false;
14022 //                   }
14023 //                   return true;
14024 //                });
14025 //                return;
14026 //            }
14027 //        }
14028         
14029         var days = date.getDaysInMonth();
14030         
14031         var firstOfMonth = date.getFirstDateOfMonth();
14032         var startingPos = firstOfMonth.getDay()-this.startDay;
14033         
14034         if(startingPos < this.startDay){
14035             startingPos += 7;
14036         }
14037         
14038         var pm = date.add(Date.MONTH, -1);
14039         var prevStart = pm.getDaysInMonth()-startingPos;
14040 //        
14041         this.cells = this.el.select('.fc-day',true);
14042         this.textNodes = this.el.query('.fc-day-number');
14043         this.cells.addClassOnOver('fc-state-hover');
14044         
14045         var cells = this.cells.elements;
14046         var textEls = this.textNodes;
14047         
14048         Roo.each(cells, function(cell){
14049             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
14050         });
14051         
14052         days += startingPos;
14053
14054         // convert everything to numbers so it's fast
14055         var day = 86400000;
14056         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
14057         //Roo.log(d);
14058         //Roo.log(pm);
14059         //Roo.log(prevStart);
14060         
14061         var today = new Date().clearTime().getTime();
14062         var sel = date.clearTime().getTime();
14063         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
14064         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
14065         var ddMatch = this.disabledDatesRE;
14066         var ddText = this.disabledDatesText;
14067         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
14068         var ddaysText = this.disabledDaysText;
14069         var format = this.format;
14070         
14071         var setCellClass = function(cal, cell){
14072             cell.row = 0;
14073             cell.events = [];
14074             cell.more = [];
14075             //Roo.log('set Cell Class');
14076             cell.title = "";
14077             var t = d.getTime();
14078             
14079             //Roo.log(d);
14080             
14081             cell.dateValue = t;
14082             if(t == today){
14083                 cell.className += " fc-today";
14084                 cell.className += " fc-state-highlight";
14085                 cell.title = cal.todayText;
14086             }
14087             if(t == sel){
14088                 // disable highlight in other month..
14089                 //cell.className += " fc-state-highlight";
14090                 
14091             }
14092             // disabling
14093             if(t < min) {
14094                 cell.className = " fc-state-disabled";
14095                 cell.title = cal.minText;
14096                 return;
14097             }
14098             if(t > max) {
14099                 cell.className = " fc-state-disabled";
14100                 cell.title = cal.maxText;
14101                 return;
14102             }
14103             if(ddays){
14104                 if(ddays.indexOf(d.getDay()) != -1){
14105                     cell.title = ddaysText;
14106                     cell.className = " fc-state-disabled";
14107                 }
14108             }
14109             if(ddMatch && format){
14110                 var fvalue = d.dateFormat(format);
14111                 if(ddMatch.test(fvalue)){
14112                     cell.title = ddText.replace("%0", fvalue);
14113                     cell.className = " fc-state-disabled";
14114                 }
14115             }
14116             
14117             if (!cell.initialClassName) {
14118                 cell.initialClassName = cell.dom.className;
14119             }
14120             
14121             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
14122         };
14123
14124         var i = 0;
14125         
14126         for(; i < startingPos; i++) {
14127             textEls[i].innerHTML = (++prevStart);
14128             d.setDate(d.getDate()+1);
14129             
14130             cells[i].className = "fc-past fc-other-month";
14131             setCellClass(this, cells[i]);
14132         }
14133         
14134         var intDay = 0;
14135         
14136         for(; i < days; i++){
14137             intDay = i - startingPos + 1;
14138             textEls[i].innerHTML = (intDay);
14139             d.setDate(d.getDate()+1);
14140             
14141             cells[i].className = ''; // "x-date-active";
14142             setCellClass(this, cells[i]);
14143         }
14144         var extraDays = 0;
14145         
14146         for(; i < 42; i++) {
14147             textEls[i].innerHTML = (++extraDays);
14148             d.setDate(d.getDate()+1);
14149             
14150             cells[i].className = "fc-future fc-other-month";
14151             setCellClass(this, cells[i]);
14152         }
14153         
14154         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
14155         
14156         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
14157         
14158         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
14159         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
14160         
14161         if(totalRows != 6){
14162             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
14163             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
14164         }
14165         
14166         this.fireEvent('monthchange', this, date);
14167         
14168         
14169         /*
14170         if(!this.internalRender){
14171             var main = this.el.dom.firstChild;
14172             var w = main.offsetWidth;
14173             this.el.setWidth(w + this.el.getBorderWidth("lr"));
14174             Roo.fly(main).setWidth(w);
14175             this.internalRender = true;
14176             // opera does not respect the auto grow header center column
14177             // then, after it gets a width opera refuses to recalculate
14178             // without a second pass
14179             if(Roo.isOpera && !this.secondPass){
14180                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
14181                 this.secondPass = true;
14182                 this.update.defer(10, this, [date]);
14183             }
14184         }
14185         */
14186         
14187     },
14188     
14189     findCell : function(dt) {
14190         dt = dt.clearTime().getTime();
14191         var ret = false;
14192         this.cells.each(function(c){
14193             //Roo.log("check " +c.dateValue + '?=' + dt);
14194             if(c.dateValue == dt){
14195                 ret = c;
14196                 return false;
14197             }
14198             return true;
14199         });
14200         
14201         return ret;
14202     },
14203     
14204     findCells : function(ev) {
14205         var s = ev.start.clone().clearTime().getTime();
14206        // Roo.log(s);
14207         var e= ev.end.clone().clearTime().getTime();
14208        // Roo.log(e);
14209         var ret = [];
14210         this.cells.each(function(c){
14211              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
14212             
14213             if(c.dateValue > e){
14214                 return ;
14215             }
14216             if(c.dateValue < s){
14217                 return ;
14218             }
14219             ret.push(c);
14220         });
14221         
14222         return ret;    
14223     },
14224     
14225 //    findBestRow: function(cells)
14226 //    {
14227 //        var ret = 0;
14228 //        
14229 //        for (var i =0 ; i < cells.length;i++) {
14230 //            ret  = Math.max(cells[i].rows || 0,ret);
14231 //        }
14232 //        return ret;
14233 //        
14234 //    },
14235     
14236     
14237     addItem : function(ev)
14238     {
14239         // look for vertical location slot in
14240         var cells = this.findCells(ev);
14241         
14242 //        ev.row = this.findBestRow(cells);
14243         
14244         // work out the location.
14245         
14246         var crow = false;
14247         var rows = [];
14248         for(var i =0; i < cells.length; i++) {
14249             
14250             cells[i].row = cells[0].row;
14251             
14252             if(i == 0){
14253                 cells[i].row = cells[i].row + 1;
14254             }
14255             
14256             if (!crow) {
14257                 crow = {
14258                     start : cells[i],
14259                     end :  cells[i]
14260                 };
14261                 continue;
14262             }
14263             if (crow.start.getY() == cells[i].getY()) {
14264                 // on same row.
14265                 crow.end = cells[i];
14266                 continue;
14267             }
14268             // different row.
14269             rows.push(crow);
14270             crow = {
14271                 start: cells[i],
14272                 end : cells[i]
14273             };
14274             
14275         }
14276         
14277         rows.push(crow);
14278         ev.els = [];
14279         ev.rows = rows;
14280         ev.cells = cells;
14281         
14282         cells[0].events.push(ev);
14283         
14284         this.calevents.push(ev);
14285     },
14286     
14287     clearEvents: function() {
14288         
14289         if(!this.calevents){
14290             return;
14291         }
14292         
14293         Roo.each(this.cells.elements, function(c){
14294             c.row = 0;
14295             c.events = [];
14296             c.more = [];
14297         });
14298         
14299         Roo.each(this.calevents, function(e) {
14300             Roo.each(e.els, function(el) {
14301                 el.un('mouseenter' ,this.onEventEnter, this);
14302                 el.un('mouseleave' ,this.onEventLeave, this);
14303                 el.remove();
14304             },this);
14305         },this);
14306         
14307         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
14308             e.remove();
14309         });
14310         
14311     },
14312     
14313     renderEvents: function()
14314     {   
14315         var _this = this;
14316         
14317         this.cells.each(function(c) {
14318             
14319             if(c.row < 5){
14320                 return;
14321             }
14322             
14323             var ev = c.events;
14324             
14325             var r = 4;
14326             if(c.row != c.events.length){
14327                 r = 4 - (4 - (c.row - c.events.length));
14328             }
14329             
14330             c.events = ev.slice(0, r);
14331             c.more = ev.slice(r);
14332             
14333             if(c.more.length && c.more.length == 1){
14334                 c.events.push(c.more.pop());
14335             }
14336             
14337             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
14338             
14339         });
14340             
14341         this.cells.each(function(c) {
14342             
14343             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
14344             
14345             
14346             for (var e = 0; e < c.events.length; e++){
14347                 var ev = c.events[e];
14348                 var rows = ev.rows;
14349                 
14350                 for(var i = 0; i < rows.length; i++) {
14351                 
14352                     // how many rows should it span..
14353
14354                     var  cfg = {
14355                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
14356                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
14357
14358                         unselectable : "on",
14359                         cn : [
14360                             {
14361                                 cls: 'fc-event-inner',
14362                                 cn : [
14363     //                                {
14364     //                                  tag:'span',
14365     //                                  cls: 'fc-event-time',
14366     //                                  html : cells.length > 1 ? '' : ev.time
14367     //                                },
14368                                     {
14369                                       tag:'span',
14370                                       cls: 'fc-event-title',
14371                                       html : String.format('{0}', ev.title)
14372                                     }
14373
14374
14375                                 ]
14376                             },
14377                             {
14378                                 cls: 'ui-resizable-handle ui-resizable-e',
14379                                 html : '&nbsp;&nbsp;&nbsp'
14380                             }
14381
14382                         ]
14383                     };
14384
14385                     if (i == 0) {
14386                         cfg.cls += ' fc-event-start';
14387                     }
14388                     if ((i+1) == rows.length) {
14389                         cfg.cls += ' fc-event-end';
14390                     }
14391
14392                     var ctr = _this.el.select('.fc-event-container',true).first();
14393                     var cg = ctr.createChild(cfg);
14394
14395                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
14396                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
14397
14398                     var r = (c.more.length) ? 1 : 0;
14399                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
14400                     cg.setWidth(ebox.right - sbox.x -2);
14401
14402                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
14403                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
14404                     cg.on('click', _this.onEventClick, _this, ev);
14405
14406                     ev.els.push(cg);
14407                     
14408                 }
14409                 
14410             }
14411             
14412             
14413             if(c.more.length){
14414                 var  cfg = {
14415                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
14416                     style : 'position: absolute',
14417                     unselectable : "on",
14418                     cn : [
14419                         {
14420                             cls: 'fc-event-inner',
14421                             cn : [
14422                                 {
14423                                   tag:'span',
14424                                   cls: 'fc-event-title',
14425                                   html : 'More'
14426                                 }
14427
14428
14429                             ]
14430                         },
14431                         {
14432                             cls: 'ui-resizable-handle ui-resizable-e',
14433                             html : '&nbsp;&nbsp;&nbsp'
14434                         }
14435
14436                     ]
14437                 };
14438
14439                 var ctr = _this.el.select('.fc-event-container',true).first();
14440                 var cg = ctr.createChild(cfg);
14441
14442                 var sbox = c.select('.fc-day-content',true).first().getBox();
14443                 var ebox = c.select('.fc-day-content',true).first().getBox();
14444                 //Roo.log(cg);
14445                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
14446                 cg.setWidth(ebox.right - sbox.x -2);
14447
14448                 cg.on('click', _this.onMoreEventClick, _this, c.more);
14449                 
14450             }
14451             
14452         });
14453         
14454         
14455         
14456     },
14457     
14458     onEventEnter: function (e, el,event,d) {
14459         this.fireEvent('evententer', this, el, event);
14460     },
14461     
14462     onEventLeave: function (e, el,event,d) {
14463         this.fireEvent('eventleave', this, el, event);
14464     },
14465     
14466     onEventClick: function (e, el,event,d) {
14467         this.fireEvent('eventclick', this, el, event);
14468     },
14469     
14470     onMonthChange: function () {
14471         this.store.load();
14472     },
14473     
14474     onMoreEventClick: function(e, el, more)
14475     {
14476         var _this = this;
14477         
14478         this.calpopover.placement = 'right';
14479         this.calpopover.setTitle('More');
14480         
14481         this.calpopover.setContent('');
14482         
14483         var ctr = this.calpopover.el.select('.popover-content', true).first();
14484         
14485         Roo.each(more, function(m){
14486             var cfg = {
14487                 cls : 'fc-event-hori fc-event-draggable',
14488                 html : m.title
14489             }
14490             var cg = ctr.createChild(cfg);
14491             
14492             cg.on('click', _this.onEventClick, _this, m);
14493         });
14494         
14495         this.calpopover.show(el);
14496         
14497         
14498     },
14499     
14500     onLoad: function () 
14501     {   
14502         this.calevents = [];
14503         var cal = this;
14504         
14505         if(this.store.getCount() > 0){
14506             this.store.data.each(function(d){
14507                cal.addItem({
14508                     id : d.data.id,
14509                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
14510                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
14511                     time : d.data.start_time,
14512                     title : d.data.title,
14513                     description : d.data.description,
14514                     venue : d.data.venue
14515                 });
14516             });
14517         }
14518         
14519         this.renderEvents();
14520         
14521         if(this.calevents.length && this.loadMask){
14522             this.maskEl.hide();
14523         }
14524     },
14525     
14526     onBeforeLoad: function()
14527     {
14528         this.clearEvents();
14529         if(this.loadMask){
14530             this.maskEl.show();
14531         }
14532     }
14533 });
14534
14535  
14536  /*
14537  * - LGPL
14538  *
14539  * element
14540  * 
14541  */
14542
14543 /**
14544  * @class Roo.bootstrap.Popover
14545  * @extends Roo.bootstrap.Component
14546  * Bootstrap Popover class
14547  * @cfg {String} html contents of the popover   (or false to use children..)
14548  * @cfg {String} title of popover (or false to hide)
14549  * @cfg {String} placement how it is placed
14550  * @cfg {String} trigger click || hover (or false to trigger manually)
14551  * @cfg {String} over what (parent or false to trigger manually.)
14552  * @cfg {Number} delay - delay before showing
14553  
14554  * @constructor
14555  * Create a new Popover
14556  * @param {Object} config The config object
14557  */
14558
14559 Roo.bootstrap.Popover = function(config){
14560     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
14561 };
14562
14563 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
14564     
14565     title: 'Fill in a title',
14566     html: false,
14567     
14568     placement : 'right',
14569     trigger : 'hover', // hover
14570     
14571     delay : 0,
14572     
14573     over: 'parent',
14574     
14575     can_build_overlaid : false,
14576     
14577     getChildContainer : function()
14578     {
14579         return this.el.select('.popover-content',true).first();
14580     },
14581     
14582     getAutoCreate : function(){
14583          Roo.log('make popover?');
14584         var cfg = {
14585            cls : 'popover roo-dynamic',
14586            style: 'display:block',
14587            cn : [
14588                 {
14589                     cls : 'arrow'
14590                 },
14591                 {
14592                     cls : 'popover-inner',
14593                     cn : [
14594                         {
14595                             tag: 'h3',
14596                             cls: 'popover-title',
14597                             html : this.title
14598                         },
14599                         {
14600                             cls : 'popover-content',
14601                             html : this.html
14602                         }
14603                     ]
14604                     
14605                 }
14606            ]
14607         };
14608         
14609         return cfg;
14610     },
14611     setTitle: function(str)
14612     {
14613         this.el.select('.popover-title',true).first().dom.innerHTML = str;
14614     },
14615     setContent: function(str)
14616     {
14617         this.el.select('.popover-content',true).first().dom.innerHTML = str;
14618     },
14619     // as it get's added to the bottom of the page.
14620     onRender : function(ct, position)
14621     {
14622         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
14623         if(!this.el){
14624             var cfg = Roo.apply({},  this.getAutoCreate());
14625             cfg.id = Roo.id();
14626             
14627             if (this.cls) {
14628                 cfg.cls += ' ' + this.cls;
14629             }
14630             if (this.style) {
14631                 cfg.style = this.style;
14632             }
14633             Roo.log("adding to ")
14634             this.el = Roo.get(document.body).createChild(cfg, position);
14635             Roo.log(this.el);
14636         }
14637         this.initEvents();
14638     },
14639     
14640     initEvents : function()
14641     {
14642         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
14643         this.el.enableDisplayMode('block');
14644         this.el.hide();
14645         if (this.over === false) {
14646             return; 
14647         }
14648         if (this.triggers === false) {
14649             return;
14650         }
14651         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
14652         var triggers = this.trigger ? this.trigger.split(' ') : [];
14653         Roo.each(triggers, function(trigger) {
14654         
14655             if (trigger == 'click') {
14656                 on_el.on('click', this.toggle, this);
14657             } else if (trigger != 'manual') {
14658                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
14659                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
14660       
14661                 on_el.on(eventIn  ,this.enter, this);
14662                 on_el.on(eventOut, this.leave, this);
14663             }
14664         }, this);
14665         
14666     },
14667     
14668     
14669     // private
14670     timeout : null,
14671     hoverState : null,
14672     
14673     toggle : function () {
14674         this.hoverState == 'in' ? this.leave() : this.enter();
14675     },
14676     
14677     enter : function () {
14678        
14679     
14680         clearTimeout(this.timeout);
14681     
14682         this.hoverState = 'in';
14683     
14684         if (!this.delay || !this.delay.show) {
14685             this.show();
14686             return;
14687         }
14688         var _t = this;
14689         this.timeout = setTimeout(function () {
14690             if (_t.hoverState == 'in') {
14691                 _t.show();
14692             }
14693         }, this.delay.show)
14694     },
14695     leave : function() {
14696         clearTimeout(this.timeout);
14697     
14698         this.hoverState = 'out';
14699     
14700         if (!this.delay || !this.delay.hide) {
14701             this.hide();
14702             return;
14703         }
14704         var _t = this;
14705         this.timeout = setTimeout(function () {
14706             if (_t.hoverState == 'out') {
14707                 _t.hide();
14708             }
14709         }, this.delay.hide)
14710     },
14711     
14712     show : function (on_el)
14713     {
14714         if (!on_el) {
14715             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
14716         }
14717         // set content.
14718         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
14719         if (this.html !== false) {
14720             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
14721         }
14722         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
14723         if (!this.title.length) {
14724             this.el.select('.popover-title',true).hide();
14725         }
14726         
14727         var placement = typeof this.placement == 'function' ?
14728             this.placement.call(this, this.el, on_el) :
14729             this.placement;
14730             
14731         var autoToken = /\s?auto?\s?/i;
14732         var autoPlace = autoToken.test(placement);
14733         if (autoPlace) {
14734             placement = placement.replace(autoToken, '') || 'top';
14735         }
14736         
14737         //this.el.detach()
14738         //this.el.setXY([0,0]);
14739         this.el.show();
14740         this.el.dom.style.display='block';
14741         this.el.addClass(placement);
14742         
14743         //this.el.appendTo(on_el);
14744         
14745         var p = this.getPosition();
14746         var box = this.el.getBox();
14747         
14748         if (autoPlace) {
14749             // fixme..
14750         }
14751         var align = Roo.bootstrap.Popover.alignment[placement];
14752         this.el.alignTo(on_el, align[0],align[1]);
14753         //var arrow = this.el.select('.arrow',true).first();
14754         //arrow.set(align[2], 
14755         
14756         this.el.addClass('in');
14757         this.hoverState = null;
14758         
14759         if (this.el.hasClass('fade')) {
14760             // fade it?
14761         }
14762         
14763     },
14764     hide : function()
14765     {
14766         this.el.setXY([0,0]);
14767         this.el.removeClass('in');
14768         this.el.hide();
14769         
14770     }
14771     
14772 });
14773
14774 Roo.bootstrap.Popover.alignment = {
14775     'left' : ['r-l', [-10,0], 'right'],
14776     'right' : ['l-r', [10,0], 'left'],
14777     'bottom' : ['t-b', [0,10], 'top'],
14778     'top' : [ 'b-t', [0,-10], 'bottom']
14779 };
14780
14781  /*
14782  * - LGPL
14783  *
14784  * Progress
14785  * 
14786  */
14787
14788 /**
14789  * @class Roo.bootstrap.Progress
14790  * @extends Roo.bootstrap.Component
14791  * Bootstrap Progress class
14792  * @cfg {Boolean} striped striped of the progress bar
14793  * @cfg {Boolean} active animated of the progress bar
14794  * 
14795  * 
14796  * @constructor
14797  * Create a new Progress
14798  * @param {Object} config The config object
14799  */
14800
14801 Roo.bootstrap.Progress = function(config){
14802     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
14803 };
14804
14805 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
14806     
14807     striped : false,
14808     active: false,
14809     
14810     getAutoCreate : function(){
14811         var cfg = {
14812             tag: 'div',
14813             cls: 'progress'
14814         };
14815         
14816         
14817         if(this.striped){
14818             cfg.cls += ' progress-striped';
14819         }
14820       
14821         if(this.active){
14822             cfg.cls += ' active';
14823         }
14824         
14825         
14826         return cfg;
14827     }
14828    
14829 });
14830
14831  
14832
14833  /*
14834  * - LGPL
14835  *
14836  * ProgressBar
14837  * 
14838  */
14839
14840 /**
14841  * @class Roo.bootstrap.ProgressBar
14842  * @extends Roo.bootstrap.Component
14843  * Bootstrap ProgressBar class
14844  * @cfg {Number} aria_valuenow aria-value now
14845  * @cfg {Number} aria_valuemin aria-value min
14846  * @cfg {Number} aria_valuemax aria-value max
14847  * @cfg {String} label label for the progress bar
14848  * @cfg {String} panel (success | info | warning | danger )
14849  * @cfg {String} role role of the progress bar
14850  * @cfg {String} sr_only text
14851  * 
14852  * 
14853  * @constructor
14854  * Create a new ProgressBar
14855  * @param {Object} config The config object
14856  */
14857
14858 Roo.bootstrap.ProgressBar = function(config){
14859     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
14860 };
14861
14862 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
14863     
14864     aria_valuenow : 0,
14865     aria_valuemin : 0,
14866     aria_valuemax : 100,
14867     label : false,
14868     panel : false,
14869     role : false,
14870     sr_only: false,
14871     
14872     getAutoCreate : function()
14873     {
14874         
14875         var cfg = {
14876             tag: 'div',
14877             cls: 'progress-bar',
14878             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
14879         };
14880         
14881         if(this.sr_only){
14882             cfg.cn = {
14883                 tag: 'span',
14884                 cls: 'sr-only',
14885                 html: this.sr_only
14886             }
14887         }
14888         
14889         if(this.role){
14890             cfg.role = this.role;
14891         }
14892         
14893         if(this.aria_valuenow){
14894             cfg['aria-valuenow'] = this.aria_valuenow;
14895         }
14896         
14897         if(this.aria_valuemin){
14898             cfg['aria-valuemin'] = this.aria_valuemin;
14899         }
14900         
14901         if(this.aria_valuemax){
14902             cfg['aria-valuemax'] = this.aria_valuemax;
14903         }
14904         
14905         if(this.label && !this.sr_only){
14906             cfg.html = this.label;
14907         }
14908         
14909         if(this.panel){
14910             cfg.cls += ' progress-bar-' + this.panel;
14911         }
14912         
14913         return cfg;
14914     },
14915     
14916     update : function(aria_valuenow)
14917     {
14918         this.aria_valuenow = aria_valuenow;
14919         
14920         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
14921     }
14922    
14923 });
14924
14925  
14926
14927  /*
14928  * - LGPL
14929  *
14930  * column
14931  * 
14932  */
14933
14934 /**
14935  * @class Roo.bootstrap.TabGroup
14936  * @extends Roo.bootstrap.Column
14937  * Bootstrap Column class
14938  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
14939  * @cfg {Boolean} carousel true to make the group behave like a carousel
14940  * @cfg {Number} bullets show the panel pointer.. default 0
14941  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
14942  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
14943  * @cfg {Number} timer auto slide timer .. default 0 millisecond
14944  * 
14945  * @constructor
14946  * Create a new TabGroup
14947  * @param {Object} config The config object
14948  */
14949
14950 Roo.bootstrap.TabGroup = function(config){
14951     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
14952     if (!this.navId) {
14953         this.navId = Roo.id();
14954     }
14955     this.tabs = [];
14956     Roo.bootstrap.TabGroup.register(this);
14957     
14958 };
14959
14960 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
14961     
14962     carousel : false,
14963     transition : false,
14964     bullets : 0,
14965     timer : 0,
14966     autoslide : false,
14967     slideFn : false,
14968     slideOnTouch : false,
14969     
14970     getAutoCreate : function()
14971     {
14972         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
14973         
14974         cfg.cls += ' tab-content';
14975         
14976         Roo.log('get auto create...............');
14977         
14978         if (this.carousel) {
14979             cfg.cls += ' carousel slide';
14980             
14981             cfg.cn = [{
14982                cls : 'carousel-inner'
14983             }];
14984         
14985             if(this.bullets > 0 && !Roo.isTouch){
14986                 
14987                 var bullets = {
14988                     cls : 'carousel-bullets',
14989                     cn : []
14990                 };
14991                 
14992                 if(this.bullets_cls){
14993                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
14994                 }
14995                 
14996                 for (var i = 0; i < this.bullets; i++){
14997                     bullets.cn.push({
14998                         cls : 'bullet bullet-' + i
14999                     });
15000                 }
15001                 
15002                 bullets.cn.push({
15003                     cls : 'clear'
15004                 });
15005                 
15006                 cfg.cn[0].cn = bullets;
15007             }
15008         }
15009         
15010         return cfg;
15011     },
15012     
15013     initEvents:  function()
15014     {
15015         Roo.log('-------- init events on tab group ---------');
15016         
15017         if(this.bullets > 0 && !Roo.isTouch){
15018             this.initBullet();
15019         }
15020         
15021         Roo.log(this);
15022         
15023         if(Roo.isTouch && this.slideOnTouch){
15024             this.el.on("touchstart", this.onTouchStart, this);
15025         }
15026         
15027         if(this.autoslide){
15028             var _this = this;
15029             
15030             this.slideFn = window.setInterval(function() {
15031                 _this.showPanelNext();
15032             }, this.timer);
15033         }
15034         
15035     },
15036     
15037     onTouchStart : function(e, el, o)
15038     {
15039         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
15040             return;
15041         }
15042         
15043         this.showPanelNext();
15044     },
15045     
15046     getChildContainer : function()
15047     {
15048         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
15049     },
15050     
15051     /**
15052     * register a Navigation item
15053     * @param {Roo.bootstrap.NavItem} the navitem to add
15054     */
15055     register : function(item)
15056     {
15057         this.tabs.push( item);
15058         item.navId = this.navId; // not really needed..
15059     
15060     },
15061     
15062     getActivePanel : function()
15063     {
15064         var r = false;
15065         Roo.each(this.tabs, function(t) {
15066             if (t.active) {
15067                 r = t;
15068                 return false;
15069             }
15070             return null;
15071         });
15072         return r;
15073         
15074     },
15075     getPanelByName : function(n)
15076     {
15077         var r = false;
15078         Roo.each(this.tabs, function(t) {
15079             if (t.tabId == n) {
15080                 r = t;
15081                 return false;
15082             }
15083             return null;
15084         });
15085         return r;
15086     },
15087     indexOfPanel : function(p)
15088     {
15089         var r = false;
15090         Roo.each(this.tabs, function(t,i) {
15091             if (t.tabId == p.tabId) {
15092                 r = i;
15093                 return false;
15094             }
15095             return null;
15096         });
15097         return r;
15098     },
15099     /**
15100      * show a specific panel
15101      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
15102      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
15103      */
15104     showPanel : function (pan)
15105     {
15106         if(this.transition){
15107             Roo.log("waiting for the transitionend");
15108             return;
15109         }
15110         
15111         if (typeof(pan) == 'number') {
15112             pan = this.tabs[pan];
15113         }
15114         if (typeof(pan) == 'string') {
15115             pan = this.getPanelByName(pan);
15116         }
15117         if (pan.tabId == this.getActivePanel().tabId) {
15118             return true;
15119         }
15120         var cur = this.getActivePanel();
15121         
15122         if (false === cur.fireEvent('beforedeactivate')) {
15123             return false;
15124         }
15125         
15126         if(this.bullets > 0 && !Roo.isTouch){
15127             this.setActiveBullet(this.indexOfPanel(pan));
15128         }
15129         
15130         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
15131             
15132             this.transition = true;
15133             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
15134             var lr = dir == 'next' ? 'left' : 'right';
15135             pan.el.addClass(dir); // or prev
15136             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
15137             cur.el.addClass(lr); // or right
15138             pan.el.addClass(lr);
15139             
15140             var _this = this;
15141             cur.el.on('transitionend', function() {
15142                 Roo.log("trans end?");
15143                 
15144                 pan.el.removeClass([lr,dir]);
15145                 pan.setActive(true);
15146                 
15147                 cur.el.removeClass([lr]);
15148                 cur.setActive(false);
15149                 
15150                 _this.transition = false;
15151                 
15152             }, this, { single:  true } );
15153             
15154             return true;
15155         }
15156         
15157         cur.setActive(false);
15158         pan.setActive(true);
15159         
15160         return true;
15161         
15162     },
15163     showPanelNext : function()
15164     {
15165         var i = this.indexOfPanel(this.getActivePanel());
15166         
15167         if (i >= this.tabs.length - 1 && !this.autoslide) {
15168             return;
15169         }
15170         
15171         if (i >= this.tabs.length - 1 && this.autoslide) {
15172             i = -1;
15173         }
15174         
15175         this.showPanel(this.tabs[i+1]);
15176     },
15177     
15178     showPanelPrev : function()
15179     {
15180         var i = this.indexOfPanel(this.getActivePanel());
15181         
15182         if (i  < 1 && !this.autoslide) {
15183             return;
15184         }
15185         
15186         if (i < 1 && this.autoslide) {
15187             i = this.tabs.length;
15188         }
15189         
15190         this.showPanel(this.tabs[i-1]);
15191     },
15192     
15193     initBullet : function()
15194     {
15195         if(Roo.isTouch){
15196             return;
15197         }
15198         
15199         var _this = this;
15200         
15201         for (var i = 0; i < this.bullets; i++){
15202             var bullet = this.el.select('.bullet-' + i, true).first();
15203
15204             if(!bullet){
15205                 continue;
15206             }
15207
15208             bullet.on('click', (function(e, el, o, ii, t){
15209
15210                 e.preventDefault();
15211
15212                 _this.showPanel(ii);
15213
15214                 if(_this.autoslide && _this.slideFn){
15215                     clearInterval(_this.slideFn);
15216                     _this.slideFn = window.setInterval(function() {
15217                         _this.showPanelNext();
15218                     }, _this.timer);
15219                 }
15220
15221             }).createDelegate(this, [i, bullet], true));
15222         }
15223     },
15224     
15225     setActiveBullet : function(i)
15226     {
15227         if(Roo.isTouch){
15228             return;
15229         }
15230         
15231         Roo.each(this.el.select('.bullet', true).elements, function(el){
15232             el.removeClass('selected');
15233         });
15234
15235         var bullet = this.el.select('.bullet-' + i, true).first();
15236         
15237         if(!bullet){
15238             return;
15239         }
15240         
15241         bullet.addClass('selected');
15242     }
15243     
15244     
15245   
15246 });
15247
15248  
15249
15250  
15251  
15252 Roo.apply(Roo.bootstrap.TabGroup, {
15253     
15254     groups: {},
15255      /**
15256     * register a Navigation Group
15257     * @param {Roo.bootstrap.NavGroup} the navgroup to add
15258     */
15259     register : function(navgrp)
15260     {
15261         this.groups[navgrp.navId] = navgrp;
15262         
15263     },
15264     /**
15265     * fetch a Navigation Group based on the navigation ID
15266     * if one does not exist , it will get created.
15267     * @param {string} the navgroup to add
15268     * @returns {Roo.bootstrap.NavGroup} the navgroup 
15269     */
15270     get: function(navId) {
15271         if (typeof(this.groups[navId]) == 'undefined') {
15272             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
15273         }
15274         return this.groups[navId] ;
15275     }
15276     
15277     
15278     
15279 });
15280
15281  /*
15282  * - LGPL
15283  *
15284  * TabPanel
15285  * 
15286  */
15287
15288 /**
15289  * @class Roo.bootstrap.TabPanel
15290  * @extends Roo.bootstrap.Component
15291  * Bootstrap TabPanel class
15292  * @cfg {Boolean} active panel active
15293  * @cfg {String} html panel content
15294  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
15295  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
15296  * 
15297  * 
15298  * @constructor
15299  * Create a new TabPanel
15300  * @param {Object} config The config object
15301  */
15302
15303 Roo.bootstrap.TabPanel = function(config){
15304     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
15305     this.addEvents({
15306         /**
15307              * @event changed
15308              * Fires when the active status changes
15309              * @param {Roo.bootstrap.TabPanel} this
15310              * @param {Boolean} state the new state
15311             
15312          */
15313         'changed': true,
15314         /**
15315              * @event beforedeactivate
15316              * Fires before a tab is de-activated - can be used to do validation on a form.
15317              * @param {Roo.bootstrap.TabPanel} this
15318              * @return {Boolean} false if there is an error
15319             
15320          */
15321         'beforedeactivate': true
15322      });
15323     
15324     this.tabId = this.tabId || Roo.id();
15325   
15326 };
15327
15328 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
15329     
15330     active: false,
15331     html: false,
15332     tabId: false,
15333     navId : false,
15334     
15335     getAutoCreate : function(){
15336         var cfg = {
15337             tag: 'div',
15338             // item is needed for carousel - not sure if it has any effect otherwise
15339             cls: 'tab-pane item',
15340             html: this.html || ''
15341         };
15342         
15343         if(this.active){
15344             cfg.cls += ' active';
15345         }
15346         
15347         if(this.tabId){
15348             cfg.tabId = this.tabId;
15349         }
15350         
15351         
15352         return cfg;
15353     },
15354     
15355     initEvents:  function()
15356     {
15357         Roo.log('-------- init events on tab panel ---------');
15358         
15359         var p = this.parent();
15360         this.navId = this.navId || p.navId;
15361         
15362         if (typeof(this.navId) != 'undefined') {
15363             // not really needed.. but just in case.. parent should be a NavGroup.
15364             var tg = Roo.bootstrap.TabGroup.get(this.navId);
15365             Roo.log(['register', tg, this]);
15366             tg.register(this);
15367             
15368             var i = tg.tabs.length - 1;
15369             
15370             if(this.active && tg.bullets > 0 && i < tg.bullets){
15371                 tg.setActiveBullet(i);
15372             }
15373         }
15374         
15375     },
15376     
15377     
15378     onRender : function(ct, position)
15379     {
15380        // Roo.log("Call onRender: " + this.xtype);
15381         
15382         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
15383         
15384         
15385         
15386         
15387         
15388     },
15389     
15390     setActive: function(state)
15391     {
15392         Roo.log("panel - set active " + this.tabId + "=" + state);
15393         
15394         this.active = state;
15395         if (!state) {
15396             this.el.removeClass('active');
15397             
15398         } else  if (!this.el.hasClass('active')) {
15399             this.el.addClass('active');
15400         }
15401         
15402         this.fireEvent('changed', this, state);
15403     }
15404     
15405     
15406 });
15407  
15408
15409  
15410
15411  /*
15412  * - LGPL
15413  *
15414  * DateField
15415  * 
15416  */
15417
15418 /**
15419  * @class Roo.bootstrap.DateField
15420  * @extends Roo.bootstrap.Input
15421  * Bootstrap DateField class
15422  * @cfg {Number} weekStart default 0
15423  * @cfg {String} viewMode default empty, (months|years)
15424  * @cfg {String} minViewMode default empty, (months|years)
15425  * @cfg {Number} startDate default -Infinity
15426  * @cfg {Number} endDate default Infinity
15427  * @cfg {Boolean} todayHighlight default false
15428  * @cfg {Boolean} todayBtn default false
15429  * @cfg {Boolean} calendarWeeks default false
15430  * @cfg {Object} daysOfWeekDisabled default empty
15431  * @cfg {Boolean} singleMode default false (true | false)
15432  * 
15433  * @cfg {Boolean} keyboardNavigation default true
15434  * @cfg {String} language default en
15435  * 
15436  * @constructor
15437  * Create a new DateField
15438  * @param {Object} config The config object
15439  */
15440
15441 Roo.bootstrap.DateField = function(config){
15442     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
15443      this.addEvents({
15444             /**
15445              * @event show
15446              * Fires when this field show.
15447              * @param {Roo.bootstrap.DateField} this
15448              * @param {Mixed} date The date value
15449              */
15450             show : true,
15451             /**
15452              * @event show
15453              * Fires when this field hide.
15454              * @param {Roo.bootstrap.DateField} this
15455              * @param {Mixed} date The date value
15456              */
15457             hide : true,
15458             /**
15459              * @event select
15460              * Fires when select a date.
15461              * @param {Roo.bootstrap.DateField} this
15462              * @param {Mixed} date The date value
15463              */
15464             select : true
15465         });
15466 };
15467
15468 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
15469     
15470     /**
15471      * @cfg {String} format
15472      * The default date format string which can be overriden for localization support.  The format must be
15473      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
15474      */
15475     format : "m/d/y",
15476     /**
15477      * @cfg {String} altFormats
15478      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
15479      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
15480      */
15481     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
15482     
15483     weekStart : 0,
15484     
15485     viewMode : '',
15486     
15487     minViewMode : '',
15488     
15489     todayHighlight : false,
15490     
15491     todayBtn: false,
15492     
15493     language: 'en',
15494     
15495     keyboardNavigation: true,
15496     
15497     calendarWeeks: false,
15498     
15499     startDate: -Infinity,
15500     
15501     endDate: Infinity,
15502     
15503     daysOfWeekDisabled: [],
15504     
15505     _events: [],
15506     
15507     singleMode : false,
15508     
15509     UTCDate: function()
15510     {
15511         return new Date(Date.UTC.apply(Date, arguments));
15512     },
15513     
15514     UTCToday: function()
15515     {
15516         var today = new Date();
15517         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
15518     },
15519     
15520     getDate: function() {
15521             var d = this.getUTCDate();
15522             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
15523     },
15524     
15525     getUTCDate: function() {
15526             return this.date;
15527     },
15528     
15529     setDate: function(d) {
15530             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
15531     },
15532     
15533     setUTCDate: function(d) {
15534             this.date = d;
15535             this.setValue(this.formatDate(this.date));
15536     },
15537         
15538     onRender: function(ct, position)
15539     {
15540         
15541         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
15542         
15543         this.language = this.language || 'en';
15544         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
15545         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
15546         
15547         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
15548         this.format = this.format || 'm/d/y';
15549         this.isInline = false;
15550         this.isInput = true;
15551         this.component = this.el.select('.add-on', true).first() || false;
15552         this.component = (this.component && this.component.length === 0) ? false : this.component;
15553         this.hasInput = this.component && this.inputEL().length;
15554         
15555         if (typeof(this.minViewMode === 'string')) {
15556             switch (this.minViewMode) {
15557                 case 'months':
15558                     this.minViewMode = 1;
15559                     break;
15560                 case 'years':
15561                     this.minViewMode = 2;
15562                     break;
15563                 default:
15564                     this.minViewMode = 0;
15565                     break;
15566             }
15567         }
15568         
15569         if (typeof(this.viewMode === 'string')) {
15570             switch (this.viewMode) {
15571                 case 'months':
15572                     this.viewMode = 1;
15573                     break;
15574                 case 'years':
15575                     this.viewMode = 2;
15576                     break;
15577                 default:
15578                     this.viewMode = 0;
15579                     break;
15580             }
15581         }
15582                 
15583         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
15584         
15585 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
15586         
15587         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15588         
15589         this.picker().on('mousedown', this.onMousedown, this);
15590         this.picker().on('click', this.onClick, this);
15591         
15592         this.picker().addClass('datepicker-dropdown');
15593         
15594         this.startViewMode = this.viewMode;
15595         
15596         if(this.singleMode){
15597             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
15598                 v.setVisibilityMode(Roo.Element.DISPLAY)
15599                 v.hide();
15600             });
15601             
15602             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
15603                 v.setStyle('width', '189px');
15604             });
15605         }
15606         
15607         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
15608             if(!this.calendarWeeks){
15609                 v.remove();
15610                 return;
15611             }
15612             
15613             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
15614             v.attr('colspan', function(i, val){
15615                 return parseInt(val) + 1;
15616             });
15617         })
15618                         
15619         
15620         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
15621         
15622         this.setStartDate(this.startDate);
15623         this.setEndDate(this.endDate);
15624         
15625         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
15626         
15627         this.fillDow();
15628         this.fillMonths();
15629         this.update();
15630         this.showMode();
15631         
15632         if(this.isInline) {
15633             this.show();
15634         }
15635     },
15636     
15637     picker : function()
15638     {
15639         return this.pickerEl;
15640 //        return this.el.select('.datepicker', true).first();
15641     },
15642     
15643     fillDow: function()
15644     {
15645         var dowCnt = this.weekStart;
15646         
15647         var dow = {
15648             tag: 'tr',
15649             cn: [
15650                 
15651             ]
15652         };
15653         
15654         if(this.calendarWeeks){
15655             dow.cn.push({
15656                 tag: 'th',
15657                 cls: 'cw',
15658                 html: '&nbsp;'
15659             })
15660         }
15661         
15662         while (dowCnt < this.weekStart + 7) {
15663             dow.cn.push({
15664                 tag: 'th',
15665                 cls: 'dow',
15666                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
15667             });
15668         }
15669         
15670         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
15671     },
15672     
15673     fillMonths: function()
15674     {    
15675         var i = 0;
15676         var months = this.picker().select('>.datepicker-months td', true).first();
15677         
15678         months.dom.innerHTML = '';
15679         
15680         while (i < 12) {
15681             var month = {
15682                 tag: 'span',
15683                 cls: 'month',
15684                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
15685             }
15686             
15687             months.createChild(month);
15688         }
15689         
15690     },
15691     
15692     update: function()
15693     {
15694         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;
15695         
15696         if (this.date < this.startDate) {
15697             this.viewDate = new Date(this.startDate);
15698         } else if (this.date > this.endDate) {
15699             this.viewDate = new Date(this.endDate);
15700         } else {
15701             this.viewDate = new Date(this.date);
15702         }
15703         
15704         this.fill();
15705     },
15706     
15707     fill: function() 
15708     {
15709         var d = new Date(this.viewDate),
15710                 year = d.getUTCFullYear(),
15711                 month = d.getUTCMonth(),
15712                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
15713                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
15714                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
15715                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
15716                 currentDate = this.date && this.date.valueOf(),
15717                 today = this.UTCToday();
15718         
15719         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
15720         
15721 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
15722         
15723 //        this.picker.select('>tfoot th.today').
15724 //                                              .text(dates[this.language].today)
15725 //                                              .toggle(this.todayBtn !== false);
15726     
15727         this.updateNavArrows();
15728         this.fillMonths();
15729                                                 
15730         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
15731         
15732         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
15733          
15734         prevMonth.setUTCDate(day);
15735         
15736         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
15737         
15738         var nextMonth = new Date(prevMonth);
15739         
15740         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
15741         
15742         nextMonth = nextMonth.valueOf();
15743         
15744         var fillMonths = false;
15745         
15746         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
15747         
15748         while(prevMonth.valueOf() < nextMonth) {
15749             var clsName = '';
15750             
15751             if (prevMonth.getUTCDay() === this.weekStart) {
15752                 if(fillMonths){
15753                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
15754                 }
15755                     
15756                 fillMonths = {
15757                     tag: 'tr',
15758                     cn: []
15759                 };
15760                 
15761                 if(this.calendarWeeks){
15762                     // ISO 8601: First week contains first thursday.
15763                     // ISO also states week starts on Monday, but we can be more abstract here.
15764                     var
15765                     // Start of current week: based on weekstart/current date
15766                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
15767                     // Thursday of this week
15768                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
15769                     // First Thursday of year, year from thursday
15770                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
15771                     // Calendar week: ms between thursdays, div ms per day, div 7 days
15772                     calWeek =  (th - yth) / 864e5 / 7 + 1;
15773                     
15774                     fillMonths.cn.push({
15775                         tag: 'td',
15776                         cls: 'cw',
15777                         html: calWeek
15778                     });
15779                 }
15780             }
15781             
15782             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
15783                 clsName += ' old';
15784             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
15785                 clsName += ' new';
15786             }
15787             if (this.todayHighlight &&
15788                 prevMonth.getUTCFullYear() == today.getFullYear() &&
15789                 prevMonth.getUTCMonth() == today.getMonth() &&
15790                 prevMonth.getUTCDate() == today.getDate()) {
15791                 clsName += ' today';
15792             }
15793             
15794             if (currentDate && prevMonth.valueOf() === currentDate) {
15795                 clsName += ' active';
15796             }
15797             
15798             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
15799                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
15800                     clsName += ' disabled';
15801             }
15802             
15803             fillMonths.cn.push({
15804                 tag: 'td',
15805                 cls: 'day ' + clsName,
15806                 html: prevMonth.getDate()
15807             })
15808             
15809             prevMonth.setDate(prevMonth.getDate()+1);
15810         }
15811           
15812         var currentYear = this.date && this.date.getUTCFullYear();
15813         var currentMonth = this.date && this.date.getUTCMonth();
15814         
15815         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
15816         
15817         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
15818             v.removeClass('active');
15819             
15820             if(currentYear === year && k === currentMonth){
15821                 v.addClass('active');
15822             }
15823             
15824             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
15825                 v.addClass('disabled');
15826             }
15827             
15828         });
15829         
15830         
15831         year = parseInt(year/10, 10) * 10;
15832         
15833         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
15834         
15835         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
15836         
15837         year -= 1;
15838         for (var i = -1; i < 11; i++) {
15839             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
15840                 tag: 'span',
15841                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
15842                 html: year
15843             })
15844             
15845             year += 1;
15846         }
15847     },
15848     
15849     showMode: function(dir) 
15850     {
15851         if (dir) {
15852             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
15853         }
15854         
15855         Roo.each(this.picker().select('>div',true).elements, function(v){
15856             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15857             v.hide();
15858         });
15859         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
15860     },
15861     
15862     place: function()
15863     {
15864         if(this.isInline) return;
15865         
15866         this.picker().removeClass(['bottom', 'top']);
15867         
15868         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
15869             /*
15870              * place to the top of element!
15871              *
15872              */
15873             
15874             this.picker().addClass('top');
15875             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
15876             
15877             return;
15878         }
15879         
15880         this.picker().addClass('bottom');
15881         
15882         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
15883     },
15884     
15885     parseDate : function(value)
15886     {
15887         if(!value || value instanceof Date){
15888             return value;
15889         }
15890         var v = Date.parseDate(value, this.format);
15891         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
15892             v = Date.parseDate(value, 'Y-m-d');
15893         }
15894         if(!v && this.altFormats){
15895             if(!this.altFormatsArray){
15896                 this.altFormatsArray = this.altFormats.split("|");
15897             }
15898             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
15899                 v = Date.parseDate(value, this.altFormatsArray[i]);
15900             }
15901         }
15902         return v;
15903     },
15904     
15905     formatDate : function(date, fmt)
15906     {   
15907         return (!date || !(date instanceof Date)) ?
15908         date : date.dateFormat(fmt || this.format);
15909     },
15910     
15911     onFocus : function()
15912     {
15913         Roo.bootstrap.DateField.superclass.onFocus.call(this);
15914         this.show();
15915     },
15916     
15917     onBlur : function()
15918     {
15919         Roo.bootstrap.DateField.superclass.onBlur.call(this);
15920         
15921         var d = this.inputEl().getValue();
15922         
15923         this.setValue(d);
15924                 
15925         this.hide();
15926     },
15927     
15928     show : function()
15929     {
15930         this.picker().show();
15931         this.update();
15932         this.place();
15933         
15934         this.fireEvent('show', this, this.date);
15935     },
15936     
15937     hide : function()
15938     {
15939         if(this.isInline) return;
15940         this.picker().hide();
15941         this.viewMode = this.startViewMode;
15942         this.showMode();
15943         
15944         this.fireEvent('hide', this, this.date);
15945         
15946     },
15947     
15948     onMousedown: function(e)
15949     {
15950         e.stopPropagation();
15951         e.preventDefault();
15952     },
15953     
15954     keyup: function(e)
15955     {
15956         Roo.bootstrap.DateField.superclass.keyup.call(this);
15957         this.update();
15958     },
15959
15960     setValue: function(v)
15961     {
15962         
15963         // v can be a string or a date..
15964         
15965         
15966         var d = new Date(this.parseDate(v) ).clearTime();
15967         
15968         if(isNaN(d.getTime())){
15969             this.date = this.viewDate = '';
15970             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
15971             return;
15972         }
15973         
15974         v = this.formatDate(d);
15975         
15976         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
15977         
15978         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
15979      
15980         this.update();
15981
15982         this.fireEvent('select', this, this.date);
15983         
15984     },
15985     
15986     getValue: function()
15987     {
15988         return this.formatDate(this.date);
15989     },
15990     
15991     fireKey: function(e)
15992     {
15993         if (!this.picker().isVisible()){
15994             if (e.keyCode == 27) // allow escape to hide and re-show picker
15995                 this.show();
15996             return;
15997         }
15998         
15999         var dateChanged = false,
16000         dir, day, month,
16001         newDate, newViewDate;
16002         
16003         switch(e.keyCode){
16004             case 27: // escape
16005                 this.hide();
16006                 e.preventDefault();
16007                 break;
16008             case 37: // left
16009             case 39: // right
16010                 if (!this.keyboardNavigation) break;
16011                 dir = e.keyCode == 37 ? -1 : 1;
16012                 
16013                 if (e.ctrlKey){
16014                     newDate = this.moveYear(this.date, dir);
16015                     newViewDate = this.moveYear(this.viewDate, dir);
16016                 } else if (e.shiftKey){
16017                     newDate = this.moveMonth(this.date, dir);
16018                     newViewDate = this.moveMonth(this.viewDate, dir);
16019                 } else {
16020                     newDate = new Date(this.date);
16021                     newDate.setUTCDate(this.date.getUTCDate() + dir);
16022                     newViewDate = new Date(this.viewDate);
16023                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
16024                 }
16025                 if (this.dateWithinRange(newDate)){
16026                     this.date = newDate;
16027                     this.viewDate = newViewDate;
16028                     this.setValue(this.formatDate(this.date));
16029 //                    this.update();
16030                     e.preventDefault();
16031                     dateChanged = true;
16032                 }
16033                 break;
16034             case 38: // up
16035             case 40: // down
16036                 if (!this.keyboardNavigation) break;
16037                 dir = e.keyCode == 38 ? -1 : 1;
16038                 if (e.ctrlKey){
16039                     newDate = this.moveYear(this.date, dir);
16040                     newViewDate = this.moveYear(this.viewDate, dir);
16041                 } else if (e.shiftKey){
16042                     newDate = this.moveMonth(this.date, dir);
16043                     newViewDate = this.moveMonth(this.viewDate, dir);
16044                 } else {
16045                     newDate = new Date(this.date);
16046                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
16047                     newViewDate = new Date(this.viewDate);
16048                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
16049                 }
16050                 if (this.dateWithinRange(newDate)){
16051                     this.date = newDate;
16052                     this.viewDate = newViewDate;
16053                     this.setValue(this.formatDate(this.date));
16054 //                    this.update();
16055                     e.preventDefault();
16056                     dateChanged = true;
16057                 }
16058                 break;
16059             case 13: // enter
16060                 this.setValue(this.formatDate(this.date));
16061                 this.hide();
16062                 e.preventDefault();
16063                 break;
16064             case 9: // tab
16065                 this.setValue(this.formatDate(this.date));
16066                 this.hide();
16067                 break;
16068             case 16: // shift
16069             case 17: // ctrl
16070             case 18: // alt
16071                 break;
16072             default :
16073                 this.hide();
16074                 
16075         }
16076     },
16077     
16078     
16079     onClick: function(e) 
16080     {
16081         e.stopPropagation();
16082         e.preventDefault();
16083         
16084         var target = e.getTarget();
16085         
16086         if(target.nodeName.toLowerCase() === 'i'){
16087             target = Roo.get(target).dom.parentNode;
16088         }
16089         
16090         var nodeName = target.nodeName;
16091         var className = target.className;
16092         var html = target.innerHTML;
16093         //Roo.log(nodeName);
16094         
16095         switch(nodeName.toLowerCase()) {
16096             case 'th':
16097                 switch(className) {
16098                     case 'switch':
16099                         this.showMode(1);
16100                         break;
16101                     case 'prev':
16102                     case 'next':
16103                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
16104                         switch(this.viewMode){
16105                                 case 0:
16106                                         this.viewDate = this.moveMonth(this.viewDate, dir);
16107                                         break;
16108                                 case 1:
16109                                 case 2:
16110                                         this.viewDate = this.moveYear(this.viewDate, dir);
16111                                         break;
16112                         }
16113                         this.fill();
16114                         break;
16115                     case 'today':
16116                         var date = new Date();
16117                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
16118 //                        this.fill()
16119                         this.setValue(this.formatDate(this.date));
16120                         
16121                         this.hide();
16122                         break;
16123                 }
16124                 break;
16125             case 'span':
16126                 if (className.indexOf('disabled') < 0) {
16127                     this.viewDate.setUTCDate(1);
16128                     if (className.indexOf('month') > -1) {
16129                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
16130                     } else {
16131                         var year = parseInt(html, 10) || 0;
16132                         this.viewDate.setUTCFullYear(year);
16133                         
16134                     }
16135                     
16136                     if(this.singleMode){
16137                         this.setValue(this.formatDate(this.viewDate));
16138                         this.hide();
16139                         return;
16140                     }
16141                     
16142                     this.showMode(-1);
16143                     this.fill();
16144                 }
16145                 break;
16146                 
16147             case 'td':
16148                 //Roo.log(className);
16149                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
16150                     var day = parseInt(html, 10) || 1;
16151                     var year = this.viewDate.getUTCFullYear(),
16152                         month = this.viewDate.getUTCMonth();
16153
16154                     if (className.indexOf('old') > -1) {
16155                         if(month === 0 ){
16156                             month = 11;
16157                             year -= 1;
16158                         }else{
16159                             month -= 1;
16160                         }
16161                     } else if (className.indexOf('new') > -1) {
16162                         if (month == 11) {
16163                             month = 0;
16164                             year += 1;
16165                         } else {
16166                             month += 1;
16167                         }
16168                     }
16169                     //Roo.log([year,month,day]);
16170                     this.date = this.UTCDate(year, month, day,0,0,0,0);
16171                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
16172 //                    this.fill();
16173                     //Roo.log(this.formatDate(this.date));
16174                     this.setValue(this.formatDate(this.date));
16175                     this.hide();
16176                 }
16177                 break;
16178         }
16179     },
16180     
16181     setStartDate: function(startDate)
16182     {
16183         this.startDate = startDate || -Infinity;
16184         if (this.startDate !== -Infinity) {
16185             this.startDate = this.parseDate(this.startDate);
16186         }
16187         this.update();
16188         this.updateNavArrows();
16189     },
16190
16191     setEndDate: function(endDate)
16192     {
16193         this.endDate = endDate || Infinity;
16194         if (this.endDate !== Infinity) {
16195             this.endDate = this.parseDate(this.endDate);
16196         }
16197         this.update();
16198         this.updateNavArrows();
16199     },
16200     
16201     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
16202     {
16203         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
16204         if (typeof(this.daysOfWeekDisabled) !== 'object') {
16205             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
16206         }
16207         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
16208             return parseInt(d, 10);
16209         });
16210         this.update();
16211         this.updateNavArrows();
16212     },
16213     
16214     updateNavArrows: function() 
16215     {
16216         if(this.singleMode){
16217             return;
16218         }
16219         
16220         var d = new Date(this.viewDate),
16221         year = d.getUTCFullYear(),
16222         month = d.getUTCMonth();
16223         
16224         Roo.each(this.picker().select('.prev', true).elements, function(v){
16225             v.show();
16226             switch (this.viewMode) {
16227                 case 0:
16228
16229                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
16230                         v.hide();
16231                     }
16232                     break;
16233                 case 1:
16234                 case 2:
16235                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
16236                         v.hide();
16237                     }
16238                     break;
16239             }
16240         });
16241         
16242         Roo.each(this.picker().select('.next', true).elements, function(v){
16243             v.show();
16244             switch (this.viewMode) {
16245                 case 0:
16246
16247                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
16248                         v.hide();
16249                     }
16250                     break;
16251                 case 1:
16252                 case 2:
16253                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
16254                         v.hide();
16255                     }
16256                     break;
16257             }
16258         })
16259     },
16260     
16261     moveMonth: function(date, dir)
16262     {
16263         if (!dir) return date;
16264         var new_date = new Date(date.valueOf()),
16265         day = new_date.getUTCDate(),
16266         month = new_date.getUTCMonth(),
16267         mag = Math.abs(dir),
16268         new_month, test;
16269         dir = dir > 0 ? 1 : -1;
16270         if (mag == 1){
16271             test = dir == -1
16272             // If going back one month, make sure month is not current month
16273             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
16274             ? function(){
16275                 return new_date.getUTCMonth() == month;
16276             }
16277             // If going forward one month, make sure month is as expected
16278             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
16279             : function(){
16280                 return new_date.getUTCMonth() != new_month;
16281             };
16282             new_month = month + dir;
16283             new_date.setUTCMonth(new_month);
16284             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
16285             if (new_month < 0 || new_month > 11)
16286                 new_month = (new_month + 12) % 12;
16287         } else {
16288             // For magnitudes >1, move one month at a time...
16289             for (var i=0; i<mag; i++)
16290                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
16291                 new_date = this.moveMonth(new_date, dir);
16292             // ...then reset the day, keeping it in the new month
16293             new_month = new_date.getUTCMonth();
16294             new_date.setUTCDate(day);
16295             test = function(){
16296                 return new_month != new_date.getUTCMonth();
16297             };
16298         }
16299         // Common date-resetting loop -- if date is beyond end of month, make it
16300         // end of month
16301         while (test()){
16302             new_date.setUTCDate(--day);
16303             new_date.setUTCMonth(new_month);
16304         }
16305         return new_date;
16306     },
16307
16308     moveYear: function(date, dir)
16309     {
16310         return this.moveMonth(date, dir*12);
16311     },
16312
16313     dateWithinRange: function(date)
16314     {
16315         return date >= this.startDate && date <= this.endDate;
16316     },
16317
16318     
16319     remove: function() 
16320     {
16321         this.picker().remove();
16322     }
16323    
16324 });
16325
16326 Roo.apply(Roo.bootstrap.DateField,  {
16327     
16328     head : {
16329         tag: 'thead',
16330         cn: [
16331         {
16332             tag: 'tr',
16333             cn: [
16334             {
16335                 tag: 'th',
16336                 cls: 'prev',
16337                 html: '<i class="fa fa-arrow-left"/>'
16338             },
16339             {
16340                 tag: 'th',
16341                 cls: 'switch',
16342                 colspan: '5'
16343             },
16344             {
16345                 tag: 'th',
16346                 cls: 'next',
16347                 html: '<i class="fa fa-arrow-right"/>'
16348             }
16349
16350             ]
16351         }
16352         ]
16353     },
16354     
16355     content : {
16356         tag: 'tbody',
16357         cn: [
16358         {
16359             tag: 'tr',
16360             cn: [
16361             {
16362                 tag: 'td',
16363                 colspan: '7'
16364             }
16365             ]
16366         }
16367         ]
16368     },
16369     
16370     footer : {
16371         tag: 'tfoot',
16372         cn: [
16373         {
16374             tag: 'tr',
16375             cn: [
16376             {
16377                 tag: 'th',
16378                 colspan: '7',
16379                 cls: 'today'
16380             }
16381                     
16382             ]
16383         }
16384         ]
16385     },
16386     
16387     dates:{
16388         en: {
16389             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
16390             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
16391             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
16392             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
16393             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
16394             today: "Today"
16395         }
16396     },
16397     
16398     modes: [
16399     {
16400         clsName: 'days',
16401         navFnc: 'Month',
16402         navStep: 1
16403     },
16404     {
16405         clsName: 'months',
16406         navFnc: 'FullYear',
16407         navStep: 1
16408     },
16409     {
16410         clsName: 'years',
16411         navFnc: 'FullYear',
16412         navStep: 10
16413     }]
16414 });
16415
16416 Roo.apply(Roo.bootstrap.DateField,  {
16417   
16418     template : {
16419         tag: 'div',
16420         cls: 'datepicker dropdown-menu roo-dynamic',
16421         cn: [
16422         {
16423             tag: 'div',
16424             cls: 'datepicker-days',
16425             cn: [
16426             {
16427                 tag: 'table',
16428                 cls: 'table-condensed',
16429                 cn:[
16430                 Roo.bootstrap.DateField.head,
16431                 {
16432                     tag: 'tbody'
16433                 },
16434                 Roo.bootstrap.DateField.footer
16435                 ]
16436             }
16437             ]
16438         },
16439         {
16440             tag: 'div',
16441             cls: 'datepicker-months',
16442             cn: [
16443             {
16444                 tag: 'table',
16445                 cls: 'table-condensed',
16446                 cn:[
16447                 Roo.bootstrap.DateField.head,
16448                 Roo.bootstrap.DateField.content,
16449                 Roo.bootstrap.DateField.footer
16450                 ]
16451             }
16452             ]
16453         },
16454         {
16455             tag: 'div',
16456             cls: 'datepicker-years',
16457             cn: [
16458             {
16459                 tag: 'table',
16460                 cls: 'table-condensed',
16461                 cn:[
16462                 Roo.bootstrap.DateField.head,
16463                 Roo.bootstrap.DateField.content,
16464                 Roo.bootstrap.DateField.footer
16465                 ]
16466             }
16467             ]
16468         }
16469         ]
16470     }
16471 });
16472
16473  
16474
16475  /*
16476  * - LGPL
16477  *
16478  * TimeField
16479  * 
16480  */
16481
16482 /**
16483  * @class Roo.bootstrap.TimeField
16484  * @extends Roo.bootstrap.Input
16485  * Bootstrap DateField class
16486  * 
16487  * 
16488  * @constructor
16489  * Create a new TimeField
16490  * @param {Object} config The config object
16491  */
16492
16493 Roo.bootstrap.TimeField = function(config){
16494     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
16495     this.addEvents({
16496             /**
16497              * @event show
16498              * Fires when this field show.
16499              * @param {Roo.bootstrap.DateField} thisthis
16500              * @param {Mixed} date The date value
16501              */
16502             show : true,
16503             /**
16504              * @event show
16505              * Fires when this field hide.
16506              * @param {Roo.bootstrap.DateField} this
16507              * @param {Mixed} date The date value
16508              */
16509             hide : true,
16510             /**
16511              * @event select
16512              * Fires when select a date.
16513              * @param {Roo.bootstrap.DateField} this
16514              * @param {Mixed} date The date value
16515              */
16516             select : true
16517         });
16518 };
16519
16520 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
16521     
16522     /**
16523      * @cfg {String} format
16524      * The default time format string which can be overriden for localization support.  The format must be
16525      * valid according to {@link Date#parseDate} (defaults to 'H:i').
16526      */
16527     format : "H:i",
16528        
16529     onRender: function(ct, position)
16530     {
16531         
16532         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
16533                 
16534         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
16535         
16536         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16537         
16538         this.pop = this.picker().select('>.datepicker-time',true).first();
16539         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16540         
16541         this.picker().on('mousedown', this.onMousedown, this);
16542         this.picker().on('click', this.onClick, this);
16543         
16544         this.picker().addClass('datepicker-dropdown');
16545     
16546         this.fillTime();
16547         this.update();
16548             
16549         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
16550         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
16551         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
16552         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
16553         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
16554         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
16555
16556     },
16557     
16558     fireKey: function(e){
16559         if (!this.picker().isVisible()){
16560             if (e.keyCode == 27) { // allow escape to hide and re-show picker
16561                 this.show();
16562             }
16563             return;
16564         }
16565
16566         e.preventDefault();
16567         
16568         switch(e.keyCode){
16569             case 27: // escape
16570                 this.hide();
16571                 break;
16572             case 37: // left
16573             case 39: // right
16574                 this.onTogglePeriod();
16575                 break;
16576             case 38: // up
16577                 this.onIncrementMinutes();
16578                 break;
16579             case 40: // down
16580                 this.onDecrementMinutes();
16581                 break;
16582             case 13: // enter
16583             case 9: // tab
16584                 this.setTime();
16585                 break;
16586         }
16587     },
16588     
16589     onClick: function(e) {
16590         e.stopPropagation();
16591         e.preventDefault();
16592     },
16593     
16594     picker : function()
16595     {
16596         return this.el.select('.datepicker', true).first();
16597     },
16598     
16599     fillTime: function()
16600     {    
16601         var time = this.pop.select('tbody', true).first();
16602         
16603         time.dom.innerHTML = '';
16604         
16605         time.createChild({
16606             tag: 'tr',
16607             cn: [
16608                 {
16609                     tag: 'td',
16610                     cn: [
16611                         {
16612                             tag: 'a',
16613                             href: '#',
16614                             cls: 'btn',
16615                             cn: [
16616                                 {
16617                                     tag: 'span',
16618                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
16619                                 }
16620                             ]
16621                         } 
16622                     ]
16623                 },
16624                 {
16625                     tag: 'td',
16626                     cls: 'separator'
16627                 },
16628                 {
16629                     tag: 'td',
16630                     cn: [
16631                         {
16632                             tag: 'a',
16633                             href: '#',
16634                             cls: 'btn',
16635                             cn: [
16636                                 {
16637                                     tag: 'span',
16638                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
16639                                 }
16640                             ]
16641                         }
16642                     ]
16643                 },
16644                 {
16645                     tag: 'td',
16646                     cls: 'separator'
16647                 }
16648             ]
16649         });
16650         
16651         time.createChild({
16652             tag: 'tr',
16653             cn: [
16654                 {
16655                     tag: 'td',
16656                     cn: [
16657                         {
16658                             tag: 'span',
16659                             cls: 'timepicker-hour',
16660                             html: '00'
16661                         }  
16662                     ]
16663                 },
16664                 {
16665                     tag: 'td',
16666                     cls: 'separator',
16667                     html: ':'
16668                 },
16669                 {
16670                     tag: 'td',
16671                     cn: [
16672                         {
16673                             tag: 'span',
16674                             cls: 'timepicker-minute',
16675                             html: '00'
16676                         }  
16677                     ]
16678                 },
16679                 {
16680                     tag: 'td',
16681                     cls: 'separator'
16682                 },
16683                 {
16684                     tag: 'td',
16685                     cn: [
16686                         {
16687                             tag: 'button',
16688                             type: 'button',
16689                             cls: 'btn btn-primary period',
16690                             html: 'AM'
16691                             
16692                         }
16693                     ]
16694                 }
16695             ]
16696         });
16697         
16698         time.createChild({
16699             tag: 'tr',
16700             cn: [
16701                 {
16702                     tag: 'td',
16703                     cn: [
16704                         {
16705                             tag: 'a',
16706                             href: '#',
16707                             cls: 'btn',
16708                             cn: [
16709                                 {
16710                                     tag: 'span',
16711                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
16712                                 }
16713                             ]
16714                         }
16715                     ]
16716                 },
16717                 {
16718                     tag: 'td',
16719                     cls: 'separator'
16720                 },
16721                 {
16722                     tag: 'td',
16723                     cn: [
16724                         {
16725                             tag: 'a',
16726                             href: '#',
16727                             cls: 'btn',
16728                             cn: [
16729                                 {
16730                                     tag: 'span',
16731                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
16732                                 }
16733                             ]
16734                         }
16735                     ]
16736                 },
16737                 {
16738                     tag: 'td',
16739                     cls: 'separator'
16740                 }
16741             ]
16742         });
16743         
16744     },
16745     
16746     update: function()
16747     {
16748         
16749         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
16750         
16751         this.fill();
16752     },
16753     
16754     fill: function() 
16755     {
16756         var hours = this.time.getHours();
16757         var minutes = this.time.getMinutes();
16758         var period = 'AM';
16759         
16760         if(hours > 11){
16761             period = 'PM';
16762         }
16763         
16764         if(hours == 0){
16765             hours = 12;
16766         }
16767         
16768         
16769         if(hours > 12){
16770             hours = hours - 12;
16771         }
16772         
16773         if(hours < 10){
16774             hours = '0' + hours;
16775         }
16776         
16777         if(minutes < 10){
16778             minutes = '0' + minutes;
16779         }
16780         
16781         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
16782         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
16783         this.pop.select('button', true).first().dom.innerHTML = period;
16784         
16785     },
16786     
16787     place: function()
16788     {   
16789         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
16790         
16791         var cls = ['bottom'];
16792         
16793         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
16794             cls.pop();
16795             cls.push('top');
16796         }
16797         
16798         cls.push('right');
16799         
16800         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
16801             cls.pop();
16802             cls.push('left');
16803         }
16804         
16805         this.picker().addClass(cls.join('-'));
16806         
16807         var _this = this;
16808         
16809         Roo.each(cls, function(c){
16810             if(c == 'bottom'){
16811                 _this.picker().setTop(_this.inputEl().getHeight());
16812                 return;
16813             }
16814             if(c == 'top'){
16815                 _this.picker().setTop(0 - _this.picker().getHeight());
16816                 return;
16817             }
16818             
16819             if(c == 'left'){
16820                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
16821                 return;
16822             }
16823             if(c == 'right'){
16824                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
16825                 return;
16826             }
16827         });
16828         
16829     },
16830   
16831     onFocus : function()
16832     {
16833         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
16834         this.show();
16835     },
16836     
16837     onBlur : function()
16838     {
16839         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
16840         this.hide();
16841     },
16842     
16843     show : function()
16844     {
16845         this.picker().show();
16846         this.pop.show();
16847         this.update();
16848         this.place();
16849         
16850         this.fireEvent('show', this, this.date);
16851     },
16852     
16853     hide : function()
16854     {
16855         this.picker().hide();
16856         this.pop.hide();
16857         
16858         this.fireEvent('hide', this, this.date);
16859     },
16860     
16861     setTime : function()
16862     {
16863         this.hide();
16864         this.setValue(this.time.format(this.format));
16865         
16866         this.fireEvent('select', this, this.date);
16867         
16868         
16869     },
16870     
16871     onMousedown: function(e){
16872         e.stopPropagation();
16873         e.preventDefault();
16874     },
16875     
16876     onIncrementHours: function()
16877     {
16878         Roo.log('onIncrementHours');
16879         this.time = this.time.add(Date.HOUR, 1);
16880         this.update();
16881         
16882     },
16883     
16884     onDecrementHours: function()
16885     {
16886         Roo.log('onDecrementHours');
16887         this.time = this.time.add(Date.HOUR, -1);
16888         this.update();
16889     },
16890     
16891     onIncrementMinutes: function()
16892     {
16893         Roo.log('onIncrementMinutes');
16894         this.time = this.time.add(Date.MINUTE, 1);
16895         this.update();
16896     },
16897     
16898     onDecrementMinutes: function()
16899     {
16900         Roo.log('onDecrementMinutes');
16901         this.time = this.time.add(Date.MINUTE, -1);
16902         this.update();
16903     },
16904     
16905     onTogglePeriod: function()
16906     {
16907         Roo.log('onTogglePeriod');
16908         this.time = this.time.add(Date.HOUR, 12);
16909         this.update();
16910     }
16911     
16912    
16913 });
16914
16915 Roo.apply(Roo.bootstrap.TimeField,  {
16916     
16917     content : {
16918         tag: 'tbody',
16919         cn: [
16920             {
16921                 tag: 'tr',
16922                 cn: [
16923                 {
16924                     tag: 'td',
16925                     colspan: '7'
16926                 }
16927                 ]
16928             }
16929         ]
16930     },
16931     
16932     footer : {
16933         tag: 'tfoot',
16934         cn: [
16935             {
16936                 tag: 'tr',
16937                 cn: [
16938                 {
16939                     tag: 'th',
16940                     colspan: '7',
16941                     cls: '',
16942                     cn: [
16943                         {
16944                             tag: 'button',
16945                             cls: 'btn btn-info ok',
16946                             html: 'OK'
16947                         }
16948                     ]
16949                 }
16950
16951                 ]
16952             }
16953         ]
16954     }
16955 });
16956
16957 Roo.apply(Roo.bootstrap.TimeField,  {
16958   
16959     template : {
16960         tag: 'div',
16961         cls: 'datepicker dropdown-menu',
16962         cn: [
16963             {
16964                 tag: 'div',
16965                 cls: 'datepicker-time',
16966                 cn: [
16967                 {
16968                     tag: 'table',
16969                     cls: 'table-condensed',
16970                     cn:[
16971                     Roo.bootstrap.TimeField.content,
16972                     Roo.bootstrap.TimeField.footer
16973                     ]
16974                 }
16975                 ]
16976             }
16977         ]
16978     }
16979 });
16980
16981  
16982
16983  /*
16984  * - LGPL
16985  *
16986  * MonthField
16987  * 
16988  */
16989
16990 /**
16991  * @class Roo.bootstrap.MonthField
16992  * @extends Roo.bootstrap.Input
16993  * Bootstrap MonthField class
16994  * 
16995  * @cfg {String} language default en
16996  * 
16997  * @constructor
16998  * Create a new MonthField
16999  * @param {Object} config The config object
17000  */
17001
17002 Roo.bootstrap.MonthField = function(config){
17003     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
17004     
17005     this.addEvents({
17006         /**
17007          * @event show
17008          * Fires when this field show.
17009          * @param {Roo.bootstrap.MonthField} this
17010          * @param {Mixed} date The date value
17011          */
17012         show : true,
17013         /**
17014          * @event show
17015          * Fires when this field hide.
17016          * @param {Roo.bootstrap.MonthField} this
17017          * @param {Mixed} date The date value
17018          */
17019         hide : true,
17020         /**
17021          * @event select
17022          * Fires when select a date.
17023          * @param {Roo.bootstrap.MonthField} this
17024          * @param {String} oldvalue The old value
17025          * @param {String} newvalue The new value
17026          */
17027         select : true
17028     });
17029 };
17030
17031 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
17032     
17033     onRender: function(ct, position)
17034     {
17035         
17036         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
17037         
17038         this.language = this.language || 'en';
17039         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
17040         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
17041         
17042         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
17043         this.isInline = false;
17044         this.isInput = true;
17045         this.component = this.el.select('.add-on', true).first() || false;
17046         this.component = (this.component && this.component.length === 0) ? false : this.component;
17047         this.hasInput = this.component && this.inputEL().length;
17048         
17049         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
17050         
17051         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17052         
17053         this.picker().on('mousedown', this.onMousedown, this);
17054         this.picker().on('click', this.onClick, this);
17055         
17056         this.picker().addClass('datepicker-dropdown');
17057         
17058         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17059             v.setStyle('width', '189px');
17060         });
17061         
17062         this.fillMonths();
17063         
17064         this.update();
17065         
17066         if(this.isInline) {
17067             this.show();
17068         }
17069         
17070     },
17071     
17072     setValue: function(v, suppressEvent)
17073     {   
17074         var o = this.getValue();
17075         
17076         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
17077         
17078         this.update();
17079
17080         if(suppressEvent !== true){
17081             this.fireEvent('select', this, o, v);
17082         }
17083         
17084     },
17085     
17086     getValue: function()
17087     {
17088         return this.value;
17089     },
17090     
17091     onClick: function(e) 
17092     {
17093         e.stopPropagation();
17094         e.preventDefault();
17095         
17096         var target = e.getTarget();
17097         
17098         if(target.nodeName.toLowerCase() === 'i'){
17099             target = Roo.get(target).dom.parentNode;
17100         }
17101         
17102         var nodeName = target.nodeName;
17103         var className = target.className;
17104         var html = target.innerHTML;
17105         
17106         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
17107             return;
17108         }
17109         
17110         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
17111         
17112         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17113         
17114         this.hide();
17115                         
17116     },
17117     
17118     picker : function()
17119     {
17120         return this.pickerEl;
17121     },
17122     
17123     fillMonths: function()
17124     {    
17125         var i = 0;
17126         var months = this.picker().select('>.datepicker-months td', true).first();
17127         
17128         months.dom.innerHTML = '';
17129         
17130         while (i < 12) {
17131             var month = {
17132                 tag: 'span',
17133                 cls: 'month',
17134                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
17135             }
17136             
17137             months.createChild(month);
17138         }
17139         
17140     },
17141     
17142     update: function()
17143     {
17144         var _this = this;
17145         
17146         if(typeof(this.vIndex) == 'undefined' && this.value.length){
17147             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
17148         }
17149         
17150         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
17151             e.removeClass('active');
17152             
17153             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
17154                 e.addClass('active');
17155             }
17156         })
17157     },
17158     
17159     place: function()
17160     {
17161         if(this.isInline) return;
17162         
17163         this.picker().removeClass(['bottom', 'top']);
17164         
17165         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17166             /*
17167              * place to the top of element!
17168              *
17169              */
17170             
17171             this.picker().addClass('top');
17172             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17173             
17174             return;
17175         }
17176         
17177         this.picker().addClass('bottom');
17178         
17179         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17180     },
17181     
17182     onFocus : function()
17183     {
17184         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
17185         this.show();
17186     },
17187     
17188     onBlur : function()
17189     {
17190         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
17191         
17192         var d = this.inputEl().getValue();
17193         
17194         this.setValue(d);
17195                 
17196         this.hide();
17197     },
17198     
17199     show : function()
17200     {
17201         this.picker().show();
17202         this.picker().select('>.datepicker-months', true).first().show();
17203         this.update();
17204         this.place();
17205         
17206         this.fireEvent('show', this, this.date);
17207     },
17208     
17209     hide : function()
17210     {
17211         if(this.isInline) return;
17212         this.picker().hide();
17213         this.fireEvent('hide', this, this.date);
17214         
17215     },
17216     
17217     onMousedown: function(e)
17218     {
17219         e.stopPropagation();
17220         e.preventDefault();
17221     },
17222     
17223     keyup: function(e)
17224     {
17225         Roo.bootstrap.MonthField.superclass.keyup.call(this);
17226         this.update();
17227     },
17228
17229     fireKey: function(e)
17230     {
17231         if (!this.picker().isVisible()){
17232             if (e.keyCode == 27) // allow escape to hide and re-show picker
17233                 this.show();
17234             return;
17235         }
17236         
17237         var dir;
17238         
17239         switch(e.keyCode){
17240             case 27: // escape
17241                 this.hide();
17242                 e.preventDefault();
17243                 break;
17244             case 37: // left
17245             case 39: // right
17246                 dir = e.keyCode == 37 ? -1 : 1;
17247                 
17248                 this.vIndex = this.vIndex + dir;
17249                 
17250                 if(this.vIndex < 0){
17251                     this.vIndex = 0;
17252                 }
17253                 
17254                 if(this.vIndex > 11){
17255                     this.vIndex = 11;
17256                 }
17257                 
17258                 if(isNaN(this.vIndex)){
17259                     this.vIndex = 0;
17260                 }
17261                 
17262                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17263                 
17264                 break;
17265             case 38: // up
17266             case 40: // down
17267                 
17268                 dir = e.keyCode == 38 ? -1 : 1;
17269                 
17270                 this.vIndex = this.vIndex + dir * 4;
17271                 
17272                 if(this.vIndex < 0){
17273                     this.vIndex = 0;
17274                 }
17275                 
17276                 if(this.vIndex > 11){
17277                     this.vIndex = 11;
17278                 }
17279                 
17280                 if(isNaN(this.vIndex)){
17281                     this.vIndex = 0;
17282                 }
17283                 
17284                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17285                 break;
17286                 
17287             case 13: // enter
17288                 
17289                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17290                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17291                 }
17292                 
17293                 this.hide();
17294                 e.preventDefault();
17295                 break;
17296             case 9: // tab
17297                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17298                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17299                 }
17300                 this.hide();
17301                 break;
17302             case 16: // shift
17303             case 17: // ctrl
17304             case 18: // alt
17305                 break;
17306             default :
17307                 this.hide();
17308                 
17309         }
17310     },
17311     
17312     remove: function() 
17313     {
17314         this.picker().remove();
17315     }
17316    
17317 });
17318
17319 Roo.apply(Roo.bootstrap.MonthField,  {
17320     
17321     content : {
17322         tag: 'tbody',
17323         cn: [
17324         {
17325             tag: 'tr',
17326             cn: [
17327             {
17328                 tag: 'td',
17329                 colspan: '7'
17330             }
17331             ]
17332         }
17333         ]
17334     },
17335     
17336     dates:{
17337         en: {
17338             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17339             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
17340         }
17341     }
17342 });
17343
17344 Roo.apply(Roo.bootstrap.MonthField,  {
17345   
17346     template : {
17347         tag: 'div',
17348         cls: 'datepicker dropdown-menu roo-dynamic',
17349         cn: [
17350             {
17351                 tag: 'div',
17352                 cls: 'datepicker-months',
17353                 cn: [
17354                 {
17355                     tag: 'table',
17356                     cls: 'table-condensed',
17357                     cn:[
17358                         Roo.bootstrap.DateField.content
17359                     ]
17360                 }
17361                 ]
17362             }
17363         ]
17364     }
17365 });
17366
17367  
17368
17369  
17370  /*
17371  * - LGPL
17372  *
17373  * CheckBox
17374  * 
17375  */
17376
17377 /**
17378  * @class Roo.bootstrap.CheckBox
17379  * @extends Roo.bootstrap.Input
17380  * Bootstrap CheckBox class
17381  * 
17382  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
17383  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
17384  * @cfg {String} boxLabel The text that appears beside the checkbox
17385  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
17386  * @cfg {Boolean} checked initnal the element
17387  * @cfg {Boolean} inline inline the element (default false)
17388  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
17389  * 
17390  * @constructor
17391  * Create a new CheckBox
17392  * @param {Object} config The config object
17393  */
17394
17395 Roo.bootstrap.CheckBox = function(config){
17396     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
17397    
17398     this.addEvents({
17399         /**
17400         * @event check
17401         * Fires when the element is checked or unchecked.
17402         * @param {Roo.bootstrap.CheckBox} this This input
17403         * @param {Boolean} checked The new checked value
17404         */
17405        check : true
17406     });
17407     
17408 };
17409
17410 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
17411   
17412     inputType: 'checkbox',
17413     inputValue: 1,
17414     valueOff: 0,
17415     boxLabel: false,
17416     checked: false,
17417     weight : false,
17418     inline: false,
17419     
17420     getAutoCreate : function()
17421     {
17422         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
17423         
17424         var id = Roo.id();
17425         
17426         var cfg = {};
17427         
17428         cfg.cls = 'form-group ' + this.inputType; //input-group
17429         
17430         if(this.inline){
17431             cfg.cls += ' ' + this.inputType + '-inline';
17432         }
17433         
17434         var input =  {
17435             tag: 'input',
17436             id : id,
17437             type : this.inputType,
17438             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
17439             cls : 'roo-' + this.inputType, //'form-box',
17440             placeholder : this.placeholder || ''
17441             
17442         };
17443         
17444         if (this.weight) { // Validity check?
17445             cfg.cls += " " + this.inputType + "-" + this.weight;
17446         }
17447         
17448         if (this.disabled) {
17449             input.disabled=true;
17450         }
17451         
17452         if(this.checked){
17453             input.checked = this.checked;
17454         }
17455         
17456         if (this.name) {
17457             input.name = this.name;
17458         }
17459         
17460         if (this.size) {
17461             input.cls += ' input-' + this.size;
17462         }
17463         
17464         var settings=this;
17465         
17466         ['xs','sm','md','lg'].map(function(size){
17467             if (settings[size]) {
17468                 cfg.cls += ' col-' + size + '-' + settings[size];
17469             }
17470         });
17471         
17472         var inputblock = input;
17473          
17474         if (this.before || this.after) {
17475             
17476             inputblock = {
17477                 cls : 'input-group',
17478                 cn :  [] 
17479             };
17480             
17481             if (this.before) {
17482                 inputblock.cn.push({
17483                     tag :'span',
17484                     cls : 'input-group-addon',
17485                     html : this.before
17486                 });
17487             }
17488             
17489             inputblock.cn.push(input);
17490             
17491             if (this.after) {
17492                 inputblock.cn.push({
17493                     tag :'span',
17494                     cls : 'input-group-addon',
17495                     html : this.after
17496                 });
17497             }
17498             
17499         }
17500         
17501         if (align ==='left' && this.fieldLabel.length) {
17502                 Roo.log("left and has label");
17503                 cfg.cn = [
17504                     
17505                     {
17506                         tag: 'label',
17507                         'for' :  id,
17508                         cls : 'control-label col-md-' + this.labelWidth,
17509                         html : this.fieldLabel
17510                         
17511                     },
17512                     {
17513                         cls : "col-md-" + (12 - this.labelWidth), 
17514                         cn: [
17515                             inputblock
17516                         ]
17517                     }
17518                     
17519                 ];
17520         } else if ( this.fieldLabel.length) {
17521                 Roo.log(" label");
17522                 cfg.cn = [
17523                    
17524                     {
17525                         tag: this.boxLabel ? 'span' : 'label',
17526                         'for': id,
17527                         cls: 'control-label box-input-label',
17528                         //cls : 'input-group-addon',
17529                         html : this.fieldLabel
17530                         
17531                     },
17532                     
17533                     inputblock
17534                     
17535                 ];
17536
17537         } else {
17538             
17539                 Roo.log(" no label && no align");
17540                 cfg.cn = [  inputblock ] ;
17541                 
17542                 
17543         }
17544         if(this.boxLabel){
17545              var boxLabelCfg = {
17546                 tag: 'label',
17547                 //'for': id, // box label is handled by onclick - so no for...
17548                 cls: 'box-label',
17549                 html: this.boxLabel
17550             }
17551             
17552             if(this.tooltip){
17553                 boxLabelCfg.tooltip = this.tooltip;
17554             }
17555              
17556             cfg.cn.push(boxLabelCfg);
17557         }
17558         
17559         
17560        
17561         return cfg;
17562         
17563     },
17564     
17565     /**
17566      * return the real input element.
17567      */
17568     inputEl: function ()
17569     {
17570         return this.el.select('input.roo-' + this.inputType,true).first();
17571     },
17572     
17573     labelEl: function()
17574     {
17575         return this.el.select('label.control-label',true).first();
17576     },
17577     /* depricated... */
17578     
17579     label: function()
17580     {
17581         return this.labelEl();
17582     },
17583     
17584     initEvents : function()
17585     {
17586 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
17587         
17588         this.inputEl().on('click', this.onClick,  this);
17589         
17590         if (this.boxLabel) { 
17591             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
17592         }
17593         
17594         this.startValue = this.getValue();
17595         
17596         if(this.groupId){
17597             Roo.bootstrap.CheckBox.register(this);
17598         }
17599     },
17600     
17601     onClick : function()
17602     {   
17603         this.setChecked(!this.checked);
17604     },
17605     
17606     setChecked : function(state,suppressEvent)
17607     {
17608         this.startValue = this.getValue();
17609         
17610         if(this.inputType == 'radio'){
17611             
17612             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17613                 e.dom.checked = false;
17614             });
17615             
17616             this.inputEl().dom.checked = true;
17617             
17618             this.inputEl().dom.value = this.inputValue;
17619             
17620             if(suppressEvent !== true){
17621                 this.fireEvent('check', this, true);
17622             }
17623             
17624             this.validate();
17625             
17626             return;
17627         }
17628         
17629         this.checked = state;
17630         
17631         this.inputEl().dom.checked = state;
17632         
17633         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
17634         
17635         if(suppressEvent !== true){
17636             this.fireEvent('check', this, state);
17637         }
17638         
17639         this.validate();
17640     },
17641     
17642     getValue : function()
17643     {
17644         if(this.inputType == 'radio'){
17645             return this.getGroupValue();
17646         }
17647         
17648         return this.inputEl().getValue();
17649         
17650     },
17651     
17652     getGroupValue : function()
17653     {
17654         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
17655             return '';
17656         }
17657         
17658         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
17659     },
17660     
17661     setValue : function(v,suppressEvent)
17662     {
17663         if(this.inputType == 'radio'){
17664             this.setGroupValue(v, suppressEvent);
17665             return;
17666         }
17667         
17668         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
17669         
17670         this.validate();
17671     },
17672     
17673     setGroupValue : function(v, suppressEvent)
17674     {
17675         this.startValue = this.getValue();
17676         
17677         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17678             e.dom.checked = false;
17679             
17680             if(e.dom.value == v){
17681                 e.dom.checked = true;
17682             }
17683         });
17684         
17685         if(suppressEvent !== true){
17686             this.fireEvent('check', this, true);
17687         }
17688
17689         this.validate();
17690         
17691         return;
17692     },
17693     
17694     validate : function()
17695     {
17696         if(
17697                 this.disabled || 
17698                 (this.inputType == 'radio' && this.validateRadio()) ||
17699                 (this.inputType == 'checkbox' && this.validateCheckbox())
17700         ){
17701             this.markValid();
17702             return true;
17703         }
17704         
17705         this.markInvalid();
17706         return false;
17707     },
17708     
17709     validateRadio : function()
17710     {
17711         var valid = false;
17712         
17713         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17714             if(!e.dom.checked){
17715                 return;
17716             }
17717             
17718             valid = true;
17719             
17720             return false;
17721         });
17722         
17723         return valid;
17724     },
17725     
17726     validateCheckbox : function()
17727     {
17728         if(!this.groupId){
17729             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
17730         }
17731         
17732         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17733         
17734         if(!group){
17735             return false;
17736         }
17737         
17738         var r = false;
17739         
17740         for(var i in group){
17741             if(r){
17742                 break;
17743             }
17744             
17745             r = (group[i].getValue() == group[i].inputValue) ? true : false;
17746         }
17747         
17748         return r;
17749     },
17750     
17751     /**
17752      * Mark this field as valid
17753      */
17754     markValid : function()
17755     {
17756         if(this.allowBlank){
17757             return;
17758         }
17759         
17760         var _this = this;
17761         
17762         this.fireEvent('valid', this);
17763         
17764         if(this.inputType == 'radio'){
17765             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17766                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
17767                 e.findParent('.form-group', false, true).addClass(_this.validClass);
17768             });
17769             
17770             return;
17771         }
17772         
17773         if(!this.groupId){
17774             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17775             this.el.findParent('.form-group', false, true).addClass(this.validClass);
17776             return;
17777         }
17778         
17779         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17780             
17781         if(!group){
17782             return;
17783         }
17784         
17785         for(var i in group){
17786             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17787             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
17788         }
17789     },
17790     
17791      /**
17792      * Mark this field as invalid
17793      * @param {String} msg The validation message
17794      */
17795     markInvalid : function(msg)
17796     {
17797         if(this.allowBlank){
17798             return;
17799         }
17800         
17801         var _this = this;
17802         
17803         this.fireEvent('invalid', this, msg);
17804         
17805         if(this.inputType == 'radio'){
17806             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17807                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
17808                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
17809             });
17810             
17811             return;
17812         }
17813         
17814         if(!this.groupId){
17815             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17816             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
17817             return;
17818         }
17819         
17820         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17821             
17822         if(!group){
17823             return;
17824         }
17825         
17826         for(var i in group){
17827             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17828             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
17829         }
17830         
17831     }
17832     
17833 });
17834
17835 Roo.apply(Roo.bootstrap.CheckBox, {
17836     
17837     groups: {},
17838     
17839      /**
17840     * register a CheckBox Group
17841     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
17842     */
17843     register : function(checkbox)
17844     {
17845         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
17846             this.groups[checkbox.groupId] = {};
17847         }
17848         
17849         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
17850             return;
17851         }
17852         
17853         this.groups[checkbox.groupId][checkbox.name] = checkbox;
17854         
17855     },
17856     /**
17857     * fetch a CheckBox Group based on the group ID
17858     * @param {string} the group ID
17859     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
17860     */
17861     get: function(groupId) {
17862         if (typeof(this.groups[groupId]) == 'undefined') {
17863             return false;
17864         }
17865         
17866         return this.groups[groupId] ;
17867     }
17868     
17869     
17870 });
17871 /*
17872  * - LGPL
17873  *
17874  * Radio
17875  *
17876  *
17877  * not inline
17878  *<div class="radio">
17879   <label>
17880     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
17881     Option one is this and that&mdash;be sure to include why it's great
17882   </label>
17883 </div>
17884  *
17885  *
17886  *inline
17887  *<span>
17888  *<label class="radio-inline">fieldLabel</label>
17889  *<label class="radio-inline">
17890   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
17891 </label>
17892 <span>
17893  * 
17894  * 
17895  */
17896
17897 /**
17898  * @class Roo.bootstrap.Radio
17899  * @extends Roo.bootstrap.CheckBox
17900  * Bootstrap Radio class
17901
17902  * @constructor
17903  * Create a new Radio
17904  * @param {Object} config The config object
17905  */
17906
17907 Roo.bootstrap.Radio = function(config){
17908     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
17909    
17910 };
17911
17912 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
17913     
17914     inputType: 'radio',
17915     inputValue: '',
17916     valueOff: '',
17917     
17918     getAutoCreate : function()
17919     {
17920         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
17921         align = align || 'left'; // default...
17922         
17923         
17924         
17925         var id = Roo.id();
17926         
17927         var cfg = {
17928                 tag : this.inline ? 'span' : 'div',
17929                 cls : '',
17930                 cn : []
17931         };
17932         
17933         var inline = this.inline ? ' radio-inline' : '';
17934         
17935         var lbl = {
17936                 tag: 'label' ,
17937                 // does not need for, as we wrap the input with it..
17938                 'for' : id,
17939                 cls : 'control-label box-label' + inline,
17940                 cn : []
17941         };
17942         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
17943         
17944         var fieldLabel = {
17945             tag: 'label' ,
17946             //cls : 'control-label' + inline,
17947             html : this.fieldLabel,
17948             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
17949         };
17950         
17951  
17952         
17953         
17954         var input =  {
17955             tag: 'input',
17956             id : id,
17957             type : this.inputType,
17958             //value : (!this.checked) ? this.valueOff : this.inputValue,
17959             value : this.inputValue,
17960             cls : 'roo-radio',
17961             placeholder : this.placeholder || '' // ?? needed????
17962             
17963         };
17964         if (this.weight) { // Validity check?
17965             input.cls += " radio-" + this.weight;
17966         }
17967         if (this.disabled) {
17968             input.disabled=true;
17969         }
17970         
17971         if(this.checked){
17972             input.checked = this.checked;
17973         }
17974         
17975         if (this.name) {
17976             input.name = this.name;
17977         }
17978         
17979         if (this.size) {
17980             input.cls += ' input-' + this.size;
17981         }
17982         
17983         //?? can span's inline have a width??
17984         
17985         var settings=this;
17986         ['xs','sm','md','lg'].map(function(size){
17987             if (settings[size]) {
17988                 cfg.cls += ' col-' + size + '-' + settings[size];
17989             }
17990         });
17991         
17992         var inputblock = input;
17993         
17994         if (this.before || this.after) {
17995             
17996             inputblock = {
17997                 cls : 'input-group',
17998                 tag : 'span',
17999                 cn :  [] 
18000             };
18001             if (this.before) {
18002                 inputblock.cn.push({
18003                     tag :'span',
18004                     cls : 'input-group-addon',
18005                     html : this.before
18006                 });
18007             }
18008             inputblock.cn.push(input);
18009             if (this.after) {
18010                 inputblock.cn.push({
18011                     tag :'span',
18012                     cls : 'input-group-addon',
18013                     html : this.after
18014                 });
18015             }
18016             
18017         };
18018         
18019         
18020         if (this.fieldLabel && this.fieldLabel.length) {
18021             cfg.cn.push(fieldLabel);
18022         }
18023        
18024         // normal bootstrap puts the input inside the label.
18025         // however with our styled version - it has to go after the input.
18026        
18027         //lbl.cn.push(inputblock);
18028         
18029         var lblwrap =  {
18030             tag: 'span',
18031             cls: 'radio' + inline,
18032             cn: [
18033                 inputblock,
18034                 lbl
18035             ]
18036         };
18037         
18038         cfg.cn.push( lblwrap);
18039         
18040         if(this.boxLabel){
18041             lbl.cn.push({
18042                 tag: 'span',
18043                 html: this.boxLabel
18044             })
18045         }
18046          
18047         
18048         return cfg;
18049         
18050     },
18051     
18052     initEvents : function()
18053     {
18054 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18055         
18056         this.inputEl().on('click', this.onClick,  this);
18057         if (this.boxLabel) {
18058             Roo.log('find label')
18059             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
18060         }
18061         
18062     },
18063     
18064     inputEl: function ()
18065     {
18066         return this.el.select('input.roo-radio',true).first();
18067     },
18068     onClick : function()
18069     {   
18070         Roo.log("click");
18071         this.setChecked(true);
18072     },
18073     
18074     setChecked : function(state,suppressEvent)
18075     {
18076         if(state){
18077             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18078                 v.dom.checked = false;
18079             });
18080         }
18081         Roo.log(this.inputEl().dom);
18082         this.checked = state;
18083         this.inputEl().dom.checked = state;
18084         
18085         if(suppressEvent !== true){
18086             this.fireEvent('check', this, state);
18087         }
18088         
18089         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18090         
18091     },
18092     
18093     getGroupValue : function()
18094     {
18095         var value = '';
18096         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18097             if(v.dom.checked == true){
18098                 value = v.dom.value;
18099             }
18100         });
18101         
18102         return value;
18103     },
18104     
18105     /**
18106      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
18107      * @return {Mixed} value The field value
18108      */
18109     getValue : function(){
18110         return this.getGroupValue();
18111     }
18112     
18113 });
18114
18115  
18116 //<script type="text/javascript">
18117
18118 /*
18119  * Based  Ext JS Library 1.1.1
18120  * Copyright(c) 2006-2007, Ext JS, LLC.
18121  * LGPL
18122  *
18123  */
18124  
18125 /**
18126  * @class Roo.HtmlEditorCore
18127  * @extends Roo.Component
18128  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
18129  *
18130  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
18131  */
18132
18133 Roo.HtmlEditorCore = function(config){
18134     
18135     
18136     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
18137     
18138     
18139     this.addEvents({
18140         /**
18141          * @event initialize
18142          * Fires when the editor is fully initialized (including the iframe)
18143          * @param {Roo.HtmlEditorCore} this
18144          */
18145         initialize: true,
18146         /**
18147          * @event activate
18148          * Fires when the editor is first receives the focus. Any insertion must wait
18149          * until after this event.
18150          * @param {Roo.HtmlEditorCore} this
18151          */
18152         activate: true,
18153          /**
18154          * @event beforesync
18155          * Fires before the textarea is updated with content from the editor iframe. Return false
18156          * to cancel the sync.
18157          * @param {Roo.HtmlEditorCore} this
18158          * @param {String} html
18159          */
18160         beforesync: true,
18161          /**
18162          * @event beforepush
18163          * Fires before the iframe editor is updated with content from the textarea. Return false
18164          * to cancel the push.
18165          * @param {Roo.HtmlEditorCore} this
18166          * @param {String} html
18167          */
18168         beforepush: true,
18169          /**
18170          * @event sync
18171          * Fires when the textarea is updated with content from the editor iframe.
18172          * @param {Roo.HtmlEditorCore} this
18173          * @param {String} html
18174          */
18175         sync: true,
18176          /**
18177          * @event push
18178          * Fires when the iframe editor is updated with content from the textarea.
18179          * @param {Roo.HtmlEditorCore} this
18180          * @param {String} html
18181          */
18182         push: true,
18183         
18184         /**
18185          * @event editorevent
18186          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
18187          * @param {Roo.HtmlEditorCore} this
18188          */
18189         editorevent: true
18190         
18191     });
18192     
18193     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
18194     
18195     // defaults : white / black...
18196     this.applyBlacklists();
18197     
18198     
18199     
18200 };
18201
18202
18203 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
18204
18205
18206      /**
18207      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
18208      */
18209     
18210     owner : false,
18211     
18212      /**
18213      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
18214      *                        Roo.resizable.
18215      */
18216     resizable : false,
18217      /**
18218      * @cfg {Number} height (in pixels)
18219      */   
18220     height: 300,
18221    /**
18222      * @cfg {Number} width (in pixels)
18223      */   
18224     width: 500,
18225     
18226     /**
18227      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
18228      * 
18229      */
18230     stylesheets: false,
18231     
18232     // id of frame..
18233     frameId: false,
18234     
18235     // private properties
18236     validationEvent : false,
18237     deferHeight: true,
18238     initialized : false,
18239     activated : false,
18240     sourceEditMode : false,
18241     onFocus : Roo.emptyFn,
18242     iframePad:3,
18243     hideMode:'offsets',
18244     
18245     clearUp: true,
18246     
18247     // blacklist + whitelisted elements..
18248     black: false,
18249     white: false,
18250      
18251     
18252
18253     /**
18254      * Protected method that will not generally be called directly. It
18255      * is called when the editor initializes the iframe with HTML contents. Override this method if you
18256      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
18257      */
18258     getDocMarkup : function(){
18259         // body styles..
18260         var st = '';
18261         
18262         // inherit styels from page...?? 
18263         if (this.stylesheets === false) {
18264             
18265             Roo.get(document.head).select('style').each(function(node) {
18266                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18267             });
18268             
18269             Roo.get(document.head).select('link').each(function(node) { 
18270                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18271             });
18272             
18273         } else if (!this.stylesheets.length) {
18274                 // simple..
18275                 st = '<style type="text/css">' +
18276                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18277                    '</style>';
18278         } else { 
18279             
18280         }
18281         
18282         st +=  '<style type="text/css">' +
18283             'IMG { cursor: pointer } ' +
18284         '</style>';
18285
18286         
18287         return '<html><head>' + st  +
18288             //<style type="text/css">' +
18289             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18290             //'</style>' +
18291             ' </head><body class="roo-htmleditor-body"></body></html>';
18292     },
18293
18294     // private
18295     onRender : function(ct, position)
18296     {
18297         var _t = this;
18298         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
18299         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
18300         
18301         
18302         this.el.dom.style.border = '0 none';
18303         this.el.dom.setAttribute('tabIndex', -1);
18304         this.el.addClass('x-hidden hide');
18305         
18306         
18307         
18308         if(Roo.isIE){ // fix IE 1px bogus margin
18309             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
18310         }
18311        
18312         
18313         this.frameId = Roo.id();
18314         
18315          
18316         
18317         var iframe = this.owner.wrap.createChild({
18318             tag: 'iframe',
18319             cls: 'form-control', // bootstrap..
18320             id: this.frameId,
18321             name: this.frameId,
18322             frameBorder : 'no',
18323             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
18324         }, this.el
18325         );
18326         
18327         
18328         this.iframe = iframe.dom;
18329
18330          this.assignDocWin();
18331         
18332         this.doc.designMode = 'on';
18333        
18334         this.doc.open();
18335         this.doc.write(this.getDocMarkup());
18336         this.doc.close();
18337
18338         
18339         var task = { // must defer to wait for browser to be ready
18340             run : function(){
18341                 //console.log("run task?" + this.doc.readyState);
18342                 this.assignDocWin();
18343                 if(this.doc.body || this.doc.readyState == 'complete'){
18344                     try {
18345                         this.doc.designMode="on";
18346                     } catch (e) {
18347                         return;
18348                     }
18349                     Roo.TaskMgr.stop(task);
18350                     this.initEditor.defer(10, this);
18351                 }
18352             },
18353             interval : 10,
18354             duration: 10000,
18355             scope: this
18356         };
18357         Roo.TaskMgr.start(task);
18358
18359     },
18360
18361     // private
18362     onResize : function(w, h)
18363     {
18364          Roo.log('resize: ' +w + ',' + h );
18365         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
18366         if(!this.iframe){
18367             return;
18368         }
18369         if(typeof w == 'number'){
18370             
18371             this.iframe.style.width = w + 'px';
18372         }
18373         if(typeof h == 'number'){
18374             
18375             this.iframe.style.height = h + 'px';
18376             if(this.doc){
18377                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
18378             }
18379         }
18380         
18381     },
18382
18383     /**
18384      * Toggles the editor between standard and source edit mode.
18385      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
18386      */
18387     toggleSourceEdit : function(sourceEditMode){
18388         
18389         this.sourceEditMode = sourceEditMode === true;
18390         
18391         if(this.sourceEditMode){
18392  
18393             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
18394             
18395         }else{
18396             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
18397             //this.iframe.className = '';
18398             this.deferFocus();
18399         }
18400         //this.setSize(this.owner.wrap.getSize());
18401         //this.fireEvent('editmodechange', this, this.sourceEditMode);
18402     },
18403
18404     
18405   
18406
18407     /**
18408      * Protected method that will not generally be called directly. If you need/want
18409      * custom HTML cleanup, this is the method you should override.
18410      * @param {String} html The HTML to be cleaned
18411      * return {String} The cleaned HTML
18412      */
18413     cleanHtml : function(html){
18414         html = String(html);
18415         if(html.length > 5){
18416             if(Roo.isSafari){ // strip safari nonsense
18417                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
18418             }
18419         }
18420         if(html == '&nbsp;'){
18421             html = '';
18422         }
18423         return html;
18424     },
18425
18426     /**
18427      * HTML Editor -> Textarea
18428      * Protected method that will not generally be called directly. Syncs the contents
18429      * of the editor iframe with the textarea.
18430      */
18431     syncValue : function(){
18432         if(this.initialized){
18433             var bd = (this.doc.body || this.doc.documentElement);
18434             //this.cleanUpPaste(); -- this is done else where and causes havoc..
18435             var html = bd.innerHTML;
18436             if(Roo.isSafari){
18437                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
18438                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
18439                 if(m && m[1]){
18440                     html = '<div style="'+m[0]+'">' + html + '</div>';
18441                 }
18442             }
18443             html = this.cleanHtml(html);
18444             // fix up the special chars.. normaly like back quotes in word...
18445             // however we do not want to do this with chinese..
18446             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
18447                 var cc = b.charCodeAt();
18448                 if (
18449                     (cc >= 0x4E00 && cc < 0xA000 ) ||
18450                     (cc >= 0x3400 && cc < 0x4E00 ) ||
18451                     (cc >= 0xf900 && cc < 0xfb00 )
18452                 ) {
18453                         return b;
18454                 }
18455                 return "&#"+cc+";" 
18456             });
18457             if(this.owner.fireEvent('beforesync', this, html) !== false){
18458                 this.el.dom.value = html;
18459                 this.owner.fireEvent('sync', this, html);
18460             }
18461         }
18462     },
18463
18464     /**
18465      * Protected method that will not generally be called directly. Pushes the value of the textarea
18466      * into the iframe editor.
18467      */
18468     pushValue : function(){
18469         if(this.initialized){
18470             var v = this.el.dom.value.trim();
18471             
18472 //            if(v.length < 1){
18473 //                v = '&#160;';
18474 //            }
18475             
18476             if(this.owner.fireEvent('beforepush', this, v) !== false){
18477                 var d = (this.doc.body || this.doc.documentElement);
18478                 d.innerHTML = v;
18479                 this.cleanUpPaste();
18480                 this.el.dom.value = d.innerHTML;
18481                 this.owner.fireEvent('push', this, v);
18482             }
18483         }
18484     },
18485
18486     // private
18487     deferFocus : function(){
18488         this.focus.defer(10, this);
18489     },
18490
18491     // doc'ed in Field
18492     focus : function(){
18493         if(this.win && !this.sourceEditMode){
18494             this.win.focus();
18495         }else{
18496             this.el.focus();
18497         }
18498     },
18499     
18500     assignDocWin: function()
18501     {
18502         var iframe = this.iframe;
18503         
18504          if(Roo.isIE){
18505             this.doc = iframe.contentWindow.document;
18506             this.win = iframe.contentWindow;
18507         } else {
18508 //            if (!Roo.get(this.frameId)) {
18509 //                return;
18510 //            }
18511 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
18512 //            this.win = Roo.get(this.frameId).dom.contentWindow;
18513             
18514             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
18515                 return;
18516             }
18517             
18518             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
18519             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
18520         }
18521     },
18522     
18523     // private
18524     initEditor : function(){
18525         //console.log("INIT EDITOR");
18526         this.assignDocWin();
18527         
18528         
18529         
18530         this.doc.designMode="on";
18531         this.doc.open();
18532         this.doc.write(this.getDocMarkup());
18533         this.doc.close();
18534         
18535         var dbody = (this.doc.body || this.doc.documentElement);
18536         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
18537         // this copies styles from the containing element into thsi one..
18538         // not sure why we need all of this..
18539         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
18540         
18541         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
18542         //ss['background-attachment'] = 'fixed'; // w3c
18543         dbody.bgProperties = 'fixed'; // ie
18544         //Roo.DomHelper.applyStyles(dbody, ss);
18545         Roo.EventManager.on(this.doc, {
18546             //'mousedown': this.onEditorEvent,
18547             'mouseup': this.onEditorEvent,
18548             'dblclick': this.onEditorEvent,
18549             'click': this.onEditorEvent,
18550             'keyup': this.onEditorEvent,
18551             buffer:100,
18552             scope: this
18553         });
18554         if(Roo.isGecko){
18555             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
18556         }
18557         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
18558             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
18559         }
18560         this.initialized = true;
18561
18562         this.owner.fireEvent('initialize', this);
18563         this.pushValue();
18564     },
18565
18566     // private
18567     onDestroy : function(){
18568         
18569         
18570         
18571         if(this.rendered){
18572             
18573             //for (var i =0; i < this.toolbars.length;i++) {
18574             //    // fixme - ask toolbars for heights?
18575             //    this.toolbars[i].onDestroy();
18576            // }
18577             
18578             //this.wrap.dom.innerHTML = '';
18579             //this.wrap.remove();
18580         }
18581     },
18582
18583     // private
18584     onFirstFocus : function(){
18585         
18586         this.assignDocWin();
18587         
18588         
18589         this.activated = true;
18590          
18591     
18592         if(Roo.isGecko){ // prevent silly gecko errors
18593             this.win.focus();
18594             var s = this.win.getSelection();
18595             if(!s.focusNode || s.focusNode.nodeType != 3){
18596                 var r = s.getRangeAt(0);
18597                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
18598                 r.collapse(true);
18599                 this.deferFocus();
18600             }
18601             try{
18602                 this.execCmd('useCSS', true);
18603                 this.execCmd('styleWithCSS', false);
18604             }catch(e){}
18605         }
18606         this.owner.fireEvent('activate', this);
18607     },
18608
18609     // private
18610     adjustFont: function(btn){
18611         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
18612         //if(Roo.isSafari){ // safari
18613         //    adjust *= 2;
18614        // }
18615         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
18616         if(Roo.isSafari){ // safari
18617             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
18618             v =  (v < 10) ? 10 : v;
18619             v =  (v > 48) ? 48 : v;
18620             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
18621             
18622         }
18623         
18624         
18625         v = Math.max(1, v+adjust);
18626         
18627         this.execCmd('FontSize', v  );
18628     },
18629
18630     onEditorEvent : function(e)
18631     {
18632         this.owner.fireEvent('editorevent', this, e);
18633       //  this.updateToolbar();
18634         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
18635     },
18636
18637     insertTag : function(tg)
18638     {
18639         // could be a bit smarter... -> wrap the current selected tRoo..
18640         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
18641             
18642             range = this.createRange(this.getSelection());
18643             var wrappingNode = this.doc.createElement(tg.toLowerCase());
18644             wrappingNode.appendChild(range.extractContents());
18645             range.insertNode(wrappingNode);
18646
18647             return;
18648             
18649             
18650             
18651         }
18652         this.execCmd("formatblock",   tg);
18653         
18654     },
18655     
18656     insertText : function(txt)
18657     {
18658         
18659         
18660         var range = this.createRange();
18661         range.deleteContents();
18662                //alert(Sender.getAttribute('label'));
18663                
18664         range.insertNode(this.doc.createTextNode(txt));
18665     } ,
18666     
18667      
18668
18669     /**
18670      * Executes a Midas editor command on the editor document and performs necessary focus and
18671      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
18672      * @param {String} cmd The Midas command
18673      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
18674      */
18675     relayCmd : function(cmd, value){
18676         this.win.focus();
18677         this.execCmd(cmd, value);
18678         this.owner.fireEvent('editorevent', this);
18679         //this.updateToolbar();
18680         this.owner.deferFocus();
18681     },
18682
18683     /**
18684      * Executes a Midas editor command directly on the editor document.
18685      * For visual commands, you should use {@link #relayCmd} instead.
18686      * <b>This should only be called after the editor is initialized.</b>
18687      * @param {String} cmd The Midas command
18688      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
18689      */
18690     execCmd : function(cmd, value){
18691         this.doc.execCommand(cmd, false, value === undefined ? null : value);
18692         this.syncValue();
18693     },
18694  
18695  
18696    
18697     /**
18698      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
18699      * to insert tRoo.
18700      * @param {String} text | dom node.. 
18701      */
18702     insertAtCursor : function(text)
18703     {
18704         
18705         
18706         
18707         if(!this.activated){
18708             return;
18709         }
18710         /*
18711         if(Roo.isIE){
18712             this.win.focus();
18713             var r = this.doc.selection.createRange();
18714             if(r){
18715                 r.collapse(true);
18716                 r.pasteHTML(text);
18717                 this.syncValue();
18718                 this.deferFocus();
18719             
18720             }
18721             return;
18722         }
18723         */
18724         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
18725             this.win.focus();
18726             
18727             
18728             // from jquery ui (MIT licenced)
18729             var range, node;
18730             var win = this.win;
18731             
18732             if (win.getSelection && win.getSelection().getRangeAt) {
18733                 range = win.getSelection().getRangeAt(0);
18734                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
18735                 range.insertNode(node);
18736             } else if (win.document.selection && win.document.selection.createRange) {
18737                 // no firefox support
18738                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
18739                 win.document.selection.createRange().pasteHTML(txt);
18740             } else {
18741                 // no firefox support
18742                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
18743                 this.execCmd('InsertHTML', txt);
18744             } 
18745             
18746             this.syncValue();
18747             
18748             this.deferFocus();
18749         }
18750     },
18751  // private
18752     mozKeyPress : function(e){
18753         if(e.ctrlKey){
18754             var c = e.getCharCode(), cmd;
18755           
18756             if(c > 0){
18757                 c = String.fromCharCode(c).toLowerCase();
18758                 switch(c){
18759                     case 'b':
18760                         cmd = 'bold';
18761                         break;
18762                     case 'i':
18763                         cmd = 'italic';
18764                         break;
18765                     
18766                     case 'u':
18767                         cmd = 'underline';
18768                         break;
18769                     
18770                     case 'v':
18771                         this.cleanUpPaste.defer(100, this);
18772                         return;
18773                         
18774                 }
18775                 if(cmd){
18776                     this.win.focus();
18777                     this.execCmd(cmd);
18778                     this.deferFocus();
18779                     e.preventDefault();
18780                 }
18781                 
18782             }
18783         }
18784     },
18785
18786     // private
18787     fixKeys : function(){ // load time branching for fastest keydown performance
18788         if(Roo.isIE){
18789             return function(e){
18790                 var k = e.getKey(), r;
18791                 if(k == e.TAB){
18792                     e.stopEvent();
18793                     r = this.doc.selection.createRange();
18794                     if(r){
18795                         r.collapse(true);
18796                         r.pasteHTML('&#160;&#160;&#160;&#160;');
18797                         this.deferFocus();
18798                     }
18799                     return;
18800                 }
18801                 
18802                 if(k == e.ENTER){
18803                     r = this.doc.selection.createRange();
18804                     if(r){
18805                         var target = r.parentElement();
18806                         if(!target || target.tagName.toLowerCase() != 'li'){
18807                             e.stopEvent();
18808                             r.pasteHTML('<br />');
18809                             r.collapse(false);
18810                             r.select();
18811                         }
18812                     }
18813                 }
18814                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18815                     this.cleanUpPaste.defer(100, this);
18816                     return;
18817                 }
18818                 
18819                 
18820             };
18821         }else if(Roo.isOpera){
18822             return function(e){
18823                 var k = e.getKey();
18824                 if(k == e.TAB){
18825                     e.stopEvent();
18826                     this.win.focus();
18827                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
18828                     this.deferFocus();
18829                 }
18830                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18831                     this.cleanUpPaste.defer(100, this);
18832                     return;
18833                 }
18834                 
18835             };
18836         }else if(Roo.isSafari){
18837             return function(e){
18838                 var k = e.getKey();
18839                 
18840                 if(k == e.TAB){
18841                     e.stopEvent();
18842                     this.execCmd('InsertText','\t');
18843                     this.deferFocus();
18844                     return;
18845                 }
18846                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18847                     this.cleanUpPaste.defer(100, this);
18848                     return;
18849                 }
18850                 
18851              };
18852         }
18853     }(),
18854     
18855     getAllAncestors: function()
18856     {
18857         var p = this.getSelectedNode();
18858         var a = [];
18859         if (!p) {
18860             a.push(p); // push blank onto stack..
18861             p = this.getParentElement();
18862         }
18863         
18864         
18865         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
18866             a.push(p);
18867             p = p.parentNode;
18868         }
18869         a.push(this.doc.body);
18870         return a;
18871     },
18872     lastSel : false,
18873     lastSelNode : false,
18874     
18875     
18876     getSelection : function() 
18877     {
18878         this.assignDocWin();
18879         return Roo.isIE ? this.doc.selection : this.win.getSelection();
18880     },
18881     
18882     getSelectedNode: function() 
18883     {
18884         // this may only work on Gecko!!!
18885         
18886         // should we cache this!!!!
18887         
18888         
18889         
18890          
18891         var range = this.createRange(this.getSelection()).cloneRange();
18892         
18893         if (Roo.isIE) {
18894             var parent = range.parentElement();
18895             while (true) {
18896                 var testRange = range.duplicate();
18897                 testRange.moveToElementText(parent);
18898                 if (testRange.inRange(range)) {
18899                     break;
18900                 }
18901                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
18902                     break;
18903                 }
18904                 parent = parent.parentElement;
18905             }
18906             return parent;
18907         }
18908         
18909         // is ancestor a text element.
18910         var ac =  range.commonAncestorContainer;
18911         if (ac.nodeType == 3) {
18912             ac = ac.parentNode;
18913         }
18914         
18915         var ar = ac.childNodes;
18916          
18917         var nodes = [];
18918         var other_nodes = [];
18919         var has_other_nodes = false;
18920         for (var i=0;i<ar.length;i++) {
18921             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
18922                 continue;
18923             }
18924             // fullly contained node.
18925             
18926             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
18927                 nodes.push(ar[i]);
18928                 continue;
18929             }
18930             
18931             // probably selected..
18932             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
18933                 other_nodes.push(ar[i]);
18934                 continue;
18935             }
18936             // outer..
18937             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
18938                 continue;
18939             }
18940             
18941             
18942             has_other_nodes = true;
18943         }
18944         if (!nodes.length && other_nodes.length) {
18945             nodes= other_nodes;
18946         }
18947         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
18948             return false;
18949         }
18950         
18951         return nodes[0];
18952     },
18953     createRange: function(sel)
18954     {
18955         // this has strange effects when using with 
18956         // top toolbar - not sure if it's a great idea.
18957         //this.editor.contentWindow.focus();
18958         if (typeof sel != "undefined") {
18959             try {
18960                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
18961             } catch(e) {
18962                 return this.doc.createRange();
18963             }
18964         } else {
18965             return this.doc.createRange();
18966         }
18967     },
18968     getParentElement: function()
18969     {
18970         
18971         this.assignDocWin();
18972         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
18973         
18974         var range = this.createRange(sel);
18975          
18976         try {
18977             var p = range.commonAncestorContainer;
18978             while (p.nodeType == 3) { // text node
18979                 p = p.parentNode;
18980             }
18981             return p;
18982         } catch (e) {
18983             return null;
18984         }
18985     
18986     },
18987     /***
18988      *
18989      * Range intersection.. the hard stuff...
18990      *  '-1' = before
18991      *  '0' = hits..
18992      *  '1' = after.
18993      *         [ -- selected range --- ]
18994      *   [fail]                        [fail]
18995      *
18996      *    basically..
18997      *      if end is before start or  hits it. fail.
18998      *      if start is after end or hits it fail.
18999      *
19000      *   if either hits (but other is outside. - then it's not 
19001      *   
19002      *    
19003      **/
19004     
19005     
19006     // @see http://www.thismuchiknow.co.uk/?p=64.
19007     rangeIntersectsNode : function(range, node)
19008     {
19009         var nodeRange = node.ownerDocument.createRange();
19010         try {
19011             nodeRange.selectNode(node);
19012         } catch (e) {
19013             nodeRange.selectNodeContents(node);
19014         }
19015     
19016         var rangeStartRange = range.cloneRange();
19017         rangeStartRange.collapse(true);
19018     
19019         var rangeEndRange = range.cloneRange();
19020         rangeEndRange.collapse(false);
19021     
19022         var nodeStartRange = nodeRange.cloneRange();
19023         nodeStartRange.collapse(true);
19024     
19025         var nodeEndRange = nodeRange.cloneRange();
19026         nodeEndRange.collapse(false);
19027     
19028         return rangeStartRange.compareBoundaryPoints(
19029                  Range.START_TO_START, nodeEndRange) == -1 &&
19030                rangeEndRange.compareBoundaryPoints(
19031                  Range.START_TO_START, nodeStartRange) == 1;
19032         
19033          
19034     },
19035     rangeCompareNode : function(range, node)
19036     {
19037         var nodeRange = node.ownerDocument.createRange();
19038         try {
19039             nodeRange.selectNode(node);
19040         } catch (e) {
19041             nodeRange.selectNodeContents(node);
19042         }
19043         
19044         
19045         range.collapse(true);
19046     
19047         nodeRange.collapse(true);
19048      
19049         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
19050         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
19051          
19052         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
19053         
19054         var nodeIsBefore   =  ss == 1;
19055         var nodeIsAfter    = ee == -1;
19056         
19057         if (nodeIsBefore && nodeIsAfter)
19058             return 0; // outer
19059         if (!nodeIsBefore && nodeIsAfter)
19060             return 1; //right trailed.
19061         
19062         if (nodeIsBefore && !nodeIsAfter)
19063             return 2;  // left trailed.
19064         // fully contined.
19065         return 3;
19066     },
19067
19068     // private? - in a new class?
19069     cleanUpPaste :  function()
19070     {
19071         // cleans up the whole document..
19072         Roo.log('cleanuppaste');
19073         
19074         this.cleanUpChildren(this.doc.body);
19075         var clean = this.cleanWordChars(this.doc.body.innerHTML);
19076         if (clean != this.doc.body.innerHTML) {
19077             this.doc.body.innerHTML = clean;
19078         }
19079         
19080     },
19081     
19082     cleanWordChars : function(input) {// change the chars to hex code
19083         var he = Roo.HtmlEditorCore;
19084         
19085         var output = input;
19086         Roo.each(he.swapCodes, function(sw) { 
19087             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
19088             
19089             output = output.replace(swapper, sw[1]);
19090         });
19091         
19092         return output;
19093     },
19094     
19095     
19096     cleanUpChildren : function (n)
19097     {
19098         if (!n.childNodes.length) {
19099             return;
19100         }
19101         for (var i = n.childNodes.length-1; i > -1 ; i--) {
19102            this.cleanUpChild(n.childNodes[i]);
19103         }
19104     },
19105     
19106     
19107         
19108     
19109     cleanUpChild : function (node)
19110     {
19111         var ed = this;
19112         //console.log(node);
19113         if (node.nodeName == "#text") {
19114             // clean up silly Windows -- stuff?
19115             return; 
19116         }
19117         if (node.nodeName == "#comment") {
19118             node.parentNode.removeChild(node);
19119             // clean up silly Windows -- stuff?
19120             return; 
19121         }
19122         var lcname = node.tagName.toLowerCase();
19123         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
19124         // whitelist of tags..
19125         
19126         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
19127             // remove node.
19128             node.parentNode.removeChild(node);
19129             return;
19130             
19131         }
19132         
19133         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
19134         
19135         // remove <a name=....> as rendering on yahoo mailer is borked with this.
19136         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
19137         
19138         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
19139         //    remove_keep_children = true;
19140         //}
19141         
19142         if (remove_keep_children) {
19143             this.cleanUpChildren(node);
19144             // inserts everything just before this node...
19145             while (node.childNodes.length) {
19146                 var cn = node.childNodes[0];
19147                 node.removeChild(cn);
19148                 node.parentNode.insertBefore(cn, node);
19149             }
19150             node.parentNode.removeChild(node);
19151             return;
19152         }
19153         
19154         if (!node.attributes || !node.attributes.length) {
19155             this.cleanUpChildren(node);
19156             return;
19157         }
19158         
19159         function cleanAttr(n,v)
19160         {
19161             
19162             if (v.match(/^\./) || v.match(/^\//)) {
19163                 return;
19164             }
19165             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
19166                 return;
19167             }
19168             if (v.match(/^#/)) {
19169                 return;
19170             }
19171 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
19172             node.removeAttribute(n);
19173             
19174         }
19175         
19176         var cwhite = this.cwhite;
19177         var cblack = this.cblack;
19178             
19179         function cleanStyle(n,v)
19180         {
19181             if (v.match(/expression/)) { //XSS?? should we even bother..
19182                 node.removeAttribute(n);
19183                 return;
19184             }
19185             
19186             var parts = v.split(/;/);
19187             var clean = [];
19188             
19189             Roo.each(parts, function(p) {
19190                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
19191                 if (!p.length) {
19192                     return true;
19193                 }
19194                 var l = p.split(':').shift().replace(/\s+/g,'');
19195                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
19196                 
19197                 if ( cwhite.length && cblack.indexOf(l) > -1) {
19198 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19199                     //node.removeAttribute(n);
19200                     return true;
19201                 }
19202                 //Roo.log()
19203                 // only allow 'c whitelisted system attributes'
19204                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
19205 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19206                     //node.removeAttribute(n);
19207                     return true;
19208                 }
19209                 
19210                 
19211                  
19212                 
19213                 clean.push(p);
19214                 return true;
19215             });
19216             if (clean.length) { 
19217                 node.setAttribute(n, clean.join(';'));
19218             } else {
19219                 node.removeAttribute(n);
19220             }
19221             
19222         }
19223         
19224         
19225         for (var i = node.attributes.length-1; i > -1 ; i--) {
19226             var a = node.attributes[i];
19227             //console.log(a);
19228             
19229             if (a.name.toLowerCase().substr(0,2)=='on')  {
19230                 node.removeAttribute(a.name);
19231                 continue;
19232             }
19233             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
19234                 node.removeAttribute(a.name);
19235                 continue;
19236             }
19237             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
19238                 cleanAttr(a.name,a.value); // fixme..
19239                 continue;
19240             }
19241             if (a.name == 'style') {
19242                 cleanStyle(a.name,a.value);
19243                 continue;
19244             }
19245             /// clean up MS crap..
19246             // tecnically this should be a list of valid class'es..
19247             
19248             
19249             if (a.name == 'class') {
19250                 if (a.value.match(/^Mso/)) {
19251                     node.className = '';
19252                 }
19253                 
19254                 if (a.value.match(/body/)) {
19255                     node.className = '';
19256                 }
19257                 continue;
19258             }
19259             
19260             // style cleanup!?
19261             // class cleanup?
19262             
19263         }
19264         
19265         
19266         this.cleanUpChildren(node);
19267         
19268         
19269     },
19270     
19271     /**
19272      * Clean up MS wordisms...
19273      */
19274     cleanWord : function(node)
19275     {
19276         
19277         
19278         if (!node) {
19279             this.cleanWord(this.doc.body);
19280             return;
19281         }
19282         if (node.nodeName == "#text") {
19283             // clean up silly Windows -- stuff?
19284             return; 
19285         }
19286         if (node.nodeName == "#comment") {
19287             node.parentNode.removeChild(node);
19288             // clean up silly Windows -- stuff?
19289             return; 
19290         }
19291         
19292         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
19293             node.parentNode.removeChild(node);
19294             return;
19295         }
19296         
19297         // remove - but keep children..
19298         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
19299             while (node.childNodes.length) {
19300                 var cn = node.childNodes[0];
19301                 node.removeChild(cn);
19302                 node.parentNode.insertBefore(cn, node);
19303             }
19304             node.parentNode.removeChild(node);
19305             this.iterateChildren(node, this.cleanWord);
19306             return;
19307         }
19308         // clean styles
19309         if (node.className.length) {
19310             
19311             var cn = node.className.split(/\W+/);
19312             var cna = [];
19313             Roo.each(cn, function(cls) {
19314                 if (cls.match(/Mso[a-zA-Z]+/)) {
19315                     return;
19316                 }
19317                 cna.push(cls);
19318             });
19319             node.className = cna.length ? cna.join(' ') : '';
19320             if (!cna.length) {
19321                 node.removeAttribute("class");
19322             }
19323         }
19324         
19325         if (node.hasAttribute("lang")) {
19326             node.removeAttribute("lang");
19327         }
19328         
19329         if (node.hasAttribute("style")) {
19330             
19331             var styles = node.getAttribute("style").split(";");
19332             var nstyle = [];
19333             Roo.each(styles, function(s) {
19334                 if (!s.match(/:/)) {
19335                     return;
19336                 }
19337                 var kv = s.split(":");
19338                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
19339                     return;
19340                 }
19341                 // what ever is left... we allow.
19342                 nstyle.push(s);
19343             });
19344             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
19345             if (!nstyle.length) {
19346                 node.removeAttribute('style');
19347             }
19348         }
19349         this.iterateChildren(node, this.cleanWord);
19350         
19351         
19352         
19353     },
19354     /**
19355      * iterateChildren of a Node, calling fn each time, using this as the scole..
19356      * @param {DomNode} node node to iterate children of.
19357      * @param {Function} fn method of this class to call on each item.
19358      */
19359     iterateChildren : function(node, fn)
19360     {
19361         if (!node.childNodes.length) {
19362                 return;
19363         }
19364         for (var i = node.childNodes.length-1; i > -1 ; i--) {
19365            fn.call(this, node.childNodes[i])
19366         }
19367     },
19368     
19369     
19370     /**
19371      * cleanTableWidths.
19372      *
19373      * Quite often pasting from word etc.. results in tables with column and widths.
19374      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
19375      *
19376      */
19377     cleanTableWidths : function(node)
19378     {
19379          
19380          
19381         if (!node) {
19382             this.cleanTableWidths(this.doc.body);
19383             return;
19384         }
19385         
19386         // ignore list...
19387         if (node.nodeName == "#text" || node.nodeName == "#comment") {
19388             return; 
19389         }
19390         Roo.log(node.tagName);
19391         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
19392             this.iterateChildren(node, this.cleanTableWidths);
19393             return;
19394         }
19395         if (node.hasAttribute('width')) {
19396             node.removeAttribute('width');
19397         }
19398         
19399          
19400         if (node.hasAttribute("style")) {
19401             // pretty basic...
19402             
19403             var styles = node.getAttribute("style").split(";");
19404             var nstyle = [];
19405             Roo.each(styles, function(s) {
19406                 if (!s.match(/:/)) {
19407                     return;
19408                 }
19409                 var kv = s.split(":");
19410                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
19411                     return;
19412                 }
19413                 // what ever is left... we allow.
19414                 nstyle.push(s);
19415             });
19416             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
19417             if (!nstyle.length) {
19418                 node.removeAttribute('style');
19419             }
19420         }
19421         
19422         this.iterateChildren(node, this.cleanTableWidths);
19423         
19424         
19425     },
19426     
19427     
19428     
19429     
19430     domToHTML : function(currentElement, depth, nopadtext) {
19431         
19432         depth = depth || 0;
19433         nopadtext = nopadtext || false;
19434     
19435         if (!currentElement) {
19436             return this.domToHTML(this.doc.body);
19437         }
19438         
19439         //Roo.log(currentElement);
19440         var j;
19441         var allText = false;
19442         var nodeName = currentElement.nodeName;
19443         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
19444         
19445         if  (nodeName == '#text') {
19446             
19447             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
19448         }
19449         
19450         
19451         var ret = '';
19452         if (nodeName != 'BODY') {
19453              
19454             var i = 0;
19455             // Prints the node tagName, such as <A>, <IMG>, etc
19456             if (tagName) {
19457                 var attr = [];
19458                 for(i = 0; i < currentElement.attributes.length;i++) {
19459                     // quoting?
19460                     var aname = currentElement.attributes.item(i).name;
19461                     if (!currentElement.attributes.item(i).value.length) {
19462                         continue;
19463                     }
19464                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
19465                 }
19466                 
19467                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
19468             } 
19469             else {
19470                 
19471                 // eack
19472             }
19473         } else {
19474             tagName = false;
19475         }
19476         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
19477             return ret;
19478         }
19479         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
19480             nopadtext = true;
19481         }
19482         
19483         
19484         // Traverse the tree
19485         i = 0;
19486         var currentElementChild = currentElement.childNodes.item(i);
19487         var allText = true;
19488         var innerHTML  = '';
19489         lastnode = '';
19490         while (currentElementChild) {
19491             // Formatting code (indent the tree so it looks nice on the screen)
19492             var nopad = nopadtext;
19493             if (lastnode == 'SPAN') {
19494                 nopad  = true;
19495             }
19496             // text
19497             if  (currentElementChild.nodeName == '#text') {
19498                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
19499                 toadd = nopadtext ? toadd : toadd.trim();
19500                 if (!nopad && toadd.length > 80) {
19501                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
19502                 }
19503                 innerHTML  += toadd;
19504                 
19505                 i++;
19506                 currentElementChild = currentElement.childNodes.item(i);
19507                 lastNode = '';
19508                 continue;
19509             }
19510             allText = false;
19511             
19512             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
19513                 
19514             // Recursively traverse the tree structure of the child node
19515             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
19516             lastnode = currentElementChild.nodeName;
19517             i++;
19518             currentElementChild=currentElement.childNodes.item(i);
19519         }
19520         
19521         ret += innerHTML;
19522         
19523         if (!allText) {
19524                 // The remaining code is mostly for formatting the tree
19525             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
19526         }
19527         
19528         
19529         if (tagName) {
19530             ret+= "</"+tagName+">";
19531         }
19532         return ret;
19533         
19534     },
19535         
19536     applyBlacklists : function()
19537     {
19538         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
19539         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
19540         
19541         this.white = [];
19542         this.black = [];
19543         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
19544             if (b.indexOf(tag) > -1) {
19545                 return;
19546             }
19547             this.white.push(tag);
19548             
19549         }, this);
19550         
19551         Roo.each(w, function(tag) {
19552             if (b.indexOf(tag) > -1) {
19553                 return;
19554             }
19555             if (this.white.indexOf(tag) > -1) {
19556                 return;
19557             }
19558             this.white.push(tag);
19559             
19560         }, this);
19561         
19562         
19563         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
19564             if (w.indexOf(tag) > -1) {
19565                 return;
19566             }
19567             this.black.push(tag);
19568             
19569         }, this);
19570         
19571         Roo.each(b, function(tag) {
19572             if (w.indexOf(tag) > -1) {
19573                 return;
19574             }
19575             if (this.black.indexOf(tag) > -1) {
19576                 return;
19577             }
19578             this.black.push(tag);
19579             
19580         }, this);
19581         
19582         
19583         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
19584         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
19585         
19586         this.cwhite = [];
19587         this.cblack = [];
19588         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
19589             if (b.indexOf(tag) > -1) {
19590                 return;
19591             }
19592             this.cwhite.push(tag);
19593             
19594         }, this);
19595         
19596         Roo.each(w, function(tag) {
19597             if (b.indexOf(tag) > -1) {
19598                 return;
19599             }
19600             if (this.cwhite.indexOf(tag) > -1) {
19601                 return;
19602             }
19603             this.cwhite.push(tag);
19604             
19605         }, this);
19606         
19607         
19608         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
19609             if (w.indexOf(tag) > -1) {
19610                 return;
19611             }
19612             this.cblack.push(tag);
19613             
19614         }, this);
19615         
19616         Roo.each(b, function(tag) {
19617             if (w.indexOf(tag) > -1) {
19618                 return;
19619             }
19620             if (this.cblack.indexOf(tag) > -1) {
19621                 return;
19622             }
19623             this.cblack.push(tag);
19624             
19625         }, this);
19626     },
19627     
19628     setStylesheets : function(stylesheets)
19629     {
19630         if(typeof(stylesheets) == 'string'){
19631             Roo.get(this.iframe.contentDocument.head).createChild({
19632                 tag : 'link',
19633                 rel : 'stylesheet',
19634                 type : 'text/css',
19635                 href : stylesheets
19636             });
19637             
19638             return;
19639         }
19640         var _this = this;
19641      
19642         Roo.each(stylesheets, function(s) {
19643             if(!s.length){
19644                 return;
19645             }
19646             
19647             Roo.get(_this.iframe.contentDocument.head).createChild({
19648                 tag : 'link',
19649                 rel : 'stylesheet',
19650                 type : 'text/css',
19651                 href : s
19652             });
19653         });
19654
19655         
19656     },
19657     
19658     removeStylesheets : function()
19659     {
19660         var _this = this;
19661         
19662         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
19663             s.remove();
19664         });
19665     }
19666     
19667     // hide stuff that is not compatible
19668     /**
19669      * @event blur
19670      * @hide
19671      */
19672     /**
19673      * @event change
19674      * @hide
19675      */
19676     /**
19677      * @event focus
19678      * @hide
19679      */
19680     /**
19681      * @event specialkey
19682      * @hide
19683      */
19684     /**
19685      * @cfg {String} fieldClass @hide
19686      */
19687     /**
19688      * @cfg {String} focusClass @hide
19689      */
19690     /**
19691      * @cfg {String} autoCreate @hide
19692      */
19693     /**
19694      * @cfg {String} inputType @hide
19695      */
19696     /**
19697      * @cfg {String} invalidClass @hide
19698      */
19699     /**
19700      * @cfg {String} invalidText @hide
19701      */
19702     /**
19703      * @cfg {String} msgFx @hide
19704      */
19705     /**
19706      * @cfg {String} validateOnBlur @hide
19707      */
19708 });
19709
19710 Roo.HtmlEditorCore.white = [
19711         'area', 'br', 'img', 'input', 'hr', 'wbr',
19712         
19713        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
19714        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
19715        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
19716        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
19717        'table',   'ul',         'xmp', 
19718        
19719        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
19720       'thead',   'tr', 
19721      
19722       'dir', 'menu', 'ol', 'ul', 'dl',
19723        
19724       'embed',  'object'
19725 ];
19726
19727
19728 Roo.HtmlEditorCore.black = [
19729     //    'embed',  'object', // enable - backend responsiblity to clean thiese
19730         'applet', // 
19731         'base',   'basefont', 'bgsound', 'blink',  'body', 
19732         'frame',  'frameset', 'head',    'html',   'ilayer', 
19733         'iframe', 'layer',  'link',     'meta',    'object',   
19734         'script', 'style' ,'title',  'xml' // clean later..
19735 ];
19736 Roo.HtmlEditorCore.clean = [
19737     'script', 'style', 'title', 'xml'
19738 ];
19739 Roo.HtmlEditorCore.remove = [
19740     'font'
19741 ];
19742 // attributes..
19743
19744 Roo.HtmlEditorCore.ablack = [
19745     'on'
19746 ];
19747     
19748 Roo.HtmlEditorCore.aclean = [ 
19749     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
19750 ];
19751
19752 // protocols..
19753 Roo.HtmlEditorCore.pwhite= [
19754         'http',  'https',  'mailto'
19755 ];
19756
19757 // white listed style attributes.
19758 Roo.HtmlEditorCore.cwhite= [
19759       //  'text-align', /// default is to allow most things..
19760       
19761          
19762 //        'font-size'//??
19763 ];
19764
19765 // black listed style attributes.
19766 Roo.HtmlEditorCore.cblack= [
19767       //  'font-size' -- this can be set by the project 
19768 ];
19769
19770
19771 Roo.HtmlEditorCore.swapCodes   =[ 
19772     [    8211, "--" ], 
19773     [    8212, "--" ], 
19774     [    8216,  "'" ],  
19775     [    8217, "'" ],  
19776     [    8220, '"' ],  
19777     [    8221, '"' ],  
19778     [    8226, "*" ],  
19779     [    8230, "..." ]
19780 ]; 
19781
19782     /*
19783  * - LGPL
19784  *
19785  * HtmlEditor
19786  * 
19787  */
19788
19789 /**
19790  * @class Roo.bootstrap.HtmlEditor
19791  * @extends Roo.bootstrap.TextArea
19792  * Bootstrap HtmlEditor class
19793
19794  * @constructor
19795  * Create a new HtmlEditor
19796  * @param {Object} config The config object
19797  */
19798
19799 Roo.bootstrap.HtmlEditor = function(config){
19800     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
19801     if (!this.toolbars) {
19802         this.toolbars = [];
19803     }
19804     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
19805     this.addEvents({
19806             /**
19807              * @event initialize
19808              * Fires when the editor is fully initialized (including the iframe)
19809              * @param {HtmlEditor} this
19810              */
19811             initialize: true,
19812             /**
19813              * @event activate
19814              * Fires when the editor is first receives the focus. Any insertion must wait
19815              * until after this event.
19816              * @param {HtmlEditor} this
19817              */
19818             activate: true,
19819              /**
19820              * @event beforesync
19821              * Fires before the textarea is updated with content from the editor iframe. Return false
19822              * to cancel the sync.
19823              * @param {HtmlEditor} this
19824              * @param {String} html
19825              */
19826             beforesync: true,
19827              /**
19828              * @event beforepush
19829              * Fires before the iframe editor is updated with content from the textarea. Return false
19830              * to cancel the push.
19831              * @param {HtmlEditor} this
19832              * @param {String} html
19833              */
19834             beforepush: true,
19835              /**
19836              * @event sync
19837              * Fires when the textarea is updated with content from the editor iframe.
19838              * @param {HtmlEditor} this
19839              * @param {String} html
19840              */
19841             sync: true,
19842              /**
19843              * @event push
19844              * Fires when the iframe editor is updated with content from the textarea.
19845              * @param {HtmlEditor} this
19846              * @param {String} html
19847              */
19848             push: true,
19849              /**
19850              * @event editmodechange
19851              * Fires when the editor switches edit modes
19852              * @param {HtmlEditor} this
19853              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
19854              */
19855             editmodechange: true,
19856             /**
19857              * @event editorevent
19858              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19859              * @param {HtmlEditor} this
19860              */
19861             editorevent: true,
19862             /**
19863              * @event firstfocus
19864              * Fires when on first focus - needed by toolbars..
19865              * @param {HtmlEditor} this
19866              */
19867             firstfocus: true,
19868             /**
19869              * @event autosave
19870              * Auto save the htmlEditor value as a file into Events
19871              * @param {HtmlEditor} this
19872              */
19873             autosave: true,
19874             /**
19875              * @event savedpreview
19876              * preview the saved version of htmlEditor
19877              * @param {HtmlEditor} this
19878              */
19879             savedpreview: true
19880         });
19881 };
19882
19883
19884 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
19885     
19886     
19887       /**
19888      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
19889      */
19890     toolbars : false,
19891    
19892      /**
19893      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
19894      *                        Roo.resizable.
19895      */
19896     resizable : false,
19897      /**
19898      * @cfg {Number} height (in pixels)
19899      */   
19900     height: 300,
19901    /**
19902      * @cfg {Number} width (in pixels)
19903      */   
19904     width: false,
19905     
19906     /**
19907      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19908      * 
19909      */
19910     stylesheets: false,
19911     
19912     // id of frame..
19913     frameId: false,
19914     
19915     // private properties
19916     validationEvent : false,
19917     deferHeight: true,
19918     initialized : false,
19919     activated : false,
19920     
19921     onFocus : Roo.emptyFn,
19922     iframePad:3,
19923     hideMode:'offsets',
19924     
19925     
19926     tbContainer : false,
19927     
19928     toolbarContainer :function() {
19929         return this.wrap.select('.x-html-editor-tb',true).first();
19930     },
19931
19932     /**
19933      * Protected method that will not generally be called directly. It
19934      * is called when the editor creates its toolbar. Override this method if you need to
19935      * add custom toolbar buttons.
19936      * @param {HtmlEditor} editor
19937      */
19938     createToolbar : function(){
19939         
19940         Roo.log("create toolbars");
19941         
19942         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
19943         this.toolbars[0].render(this.toolbarContainer());
19944         
19945         return;
19946         
19947 //        if (!editor.toolbars || !editor.toolbars.length) {
19948 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
19949 //        }
19950 //        
19951 //        for (var i =0 ; i < editor.toolbars.length;i++) {
19952 //            editor.toolbars[i] = Roo.factory(
19953 //                    typeof(editor.toolbars[i]) == 'string' ?
19954 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
19955 //                Roo.bootstrap.HtmlEditor);
19956 //            editor.toolbars[i].init(editor);
19957 //        }
19958     },
19959
19960      
19961     // private
19962     onRender : function(ct, position)
19963     {
19964        // Roo.log("Call onRender: " + this.xtype);
19965         var _t = this;
19966         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
19967       
19968         this.wrap = this.inputEl().wrap({
19969             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
19970         });
19971         
19972         this.editorcore.onRender(ct, position);
19973          
19974         if (this.resizable) {
19975             this.resizeEl = new Roo.Resizable(this.wrap, {
19976                 pinned : true,
19977                 wrap: true,
19978                 dynamic : true,
19979                 minHeight : this.height,
19980                 height: this.height,
19981                 handles : this.resizable,
19982                 width: this.width,
19983                 listeners : {
19984                     resize : function(r, w, h) {
19985                         _t.onResize(w,h); // -something
19986                     }
19987                 }
19988             });
19989             
19990         }
19991         this.createToolbar(this);
19992        
19993         
19994         if(!this.width && this.resizable){
19995             this.setSize(this.wrap.getSize());
19996         }
19997         if (this.resizeEl) {
19998             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
19999             // should trigger onReize..
20000         }
20001         
20002     },
20003
20004     // private
20005     onResize : function(w, h)
20006     {
20007         Roo.log('resize: ' +w + ',' + h );
20008         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
20009         var ew = false;
20010         var eh = false;
20011         
20012         if(this.inputEl() ){
20013             if(typeof w == 'number'){
20014                 var aw = w - this.wrap.getFrameWidth('lr');
20015                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
20016                 ew = aw;
20017             }
20018             if(typeof h == 'number'){
20019                  var tbh = -11;  // fixme it needs to tool bar size!
20020                 for (var i =0; i < this.toolbars.length;i++) {
20021                     // fixme - ask toolbars for heights?
20022                     tbh += this.toolbars[i].el.getHeight();
20023                     //if (this.toolbars[i].footer) {
20024                     //    tbh += this.toolbars[i].footer.el.getHeight();
20025                     //}
20026                 }
20027               
20028                 
20029                 
20030                 
20031                 
20032                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
20033                 ah -= 5; // knock a few pixes off for look..
20034                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
20035                 var eh = ah;
20036             }
20037         }
20038         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
20039         this.editorcore.onResize(ew,eh);
20040         
20041     },
20042
20043     /**
20044      * Toggles the editor between standard and source edit mode.
20045      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20046      */
20047     toggleSourceEdit : function(sourceEditMode)
20048     {
20049         this.editorcore.toggleSourceEdit(sourceEditMode);
20050         
20051         if(this.editorcore.sourceEditMode){
20052             Roo.log('editor - showing textarea');
20053             
20054 //            Roo.log('in');
20055 //            Roo.log(this.syncValue());
20056             this.syncValue();
20057             this.inputEl().removeClass(['hide', 'x-hidden']);
20058             this.inputEl().dom.removeAttribute('tabIndex');
20059             this.inputEl().focus();
20060         }else{
20061             Roo.log('editor - hiding textarea');
20062 //            Roo.log('out')
20063 //            Roo.log(this.pushValue()); 
20064             this.pushValue();
20065             
20066             this.inputEl().addClass(['hide', 'x-hidden']);
20067             this.inputEl().dom.setAttribute('tabIndex', -1);
20068             //this.deferFocus();
20069         }
20070          
20071         if(this.resizable){
20072             this.setSize(this.wrap.getSize());
20073         }
20074         
20075         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
20076     },
20077  
20078     // private (for BoxComponent)
20079     adjustSize : Roo.BoxComponent.prototype.adjustSize,
20080
20081     // private (for BoxComponent)
20082     getResizeEl : function(){
20083         return this.wrap;
20084     },
20085
20086     // private (for BoxComponent)
20087     getPositionEl : function(){
20088         return this.wrap;
20089     },
20090
20091     // private
20092     initEvents : function(){
20093         this.originalValue = this.getValue();
20094     },
20095
20096 //    /**
20097 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20098 //     * @method
20099 //     */
20100 //    markInvalid : Roo.emptyFn,
20101 //    /**
20102 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20103 //     * @method
20104 //     */
20105 //    clearInvalid : Roo.emptyFn,
20106
20107     setValue : function(v){
20108         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
20109         this.editorcore.pushValue();
20110     },
20111
20112      
20113     // private
20114     deferFocus : function(){
20115         this.focus.defer(10, this);
20116     },
20117
20118     // doc'ed in Field
20119     focus : function(){
20120         this.editorcore.focus();
20121         
20122     },
20123       
20124
20125     // private
20126     onDestroy : function(){
20127         
20128         
20129         
20130         if(this.rendered){
20131             
20132             for (var i =0; i < this.toolbars.length;i++) {
20133                 // fixme - ask toolbars for heights?
20134                 this.toolbars[i].onDestroy();
20135             }
20136             
20137             this.wrap.dom.innerHTML = '';
20138             this.wrap.remove();
20139         }
20140     },
20141
20142     // private
20143     onFirstFocus : function(){
20144         //Roo.log("onFirstFocus");
20145         this.editorcore.onFirstFocus();
20146          for (var i =0; i < this.toolbars.length;i++) {
20147             this.toolbars[i].onFirstFocus();
20148         }
20149         
20150     },
20151     
20152     // private
20153     syncValue : function()
20154     {   
20155         this.editorcore.syncValue();
20156     },
20157     
20158     pushValue : function()
20159     {   
20160         this.editorcore.pushValue();
20161     }
20162      
20163     
20164     // hide stuff that is not compatible
20165     /**
20166      * @event blur
20167      * @hide
20168      */
20169     /**
20170      * @event change
20171      * @hide
20172      */
20173     /**
20174      * @event focus
20175      * @hide
20176      */
20177     /**
20178      * @event specialkey
20179      * @hide
20180      */
20181     /**
20182      * @cfg {String} fieldClass @hide
20183      */
20184     /**
20185      * @cfg {String} focusClass @hide
20186      */
20187     /**
20188      * @cfg {String} autoCreate @hide
20189      */
20190     /**
20191      * @cfg {String} inputType @hide
20192      */
20193     /**
20194      * @cfg {String} invalidClass @hide
20195      */
20196     /**
20197      * @cfg {String} invalidText @hide
20198      */
20199     /**
20200      * @cfg {String} msgFx @hide
20201      */
20202     /**
20203      * @cfg {String} validateOnBlur @hide
20204      */
20205 });
20206  
20207     
20208    
20209    
20210    
20211       
20212 Roo.namespace('Roo.bootstrap.htmleditor');
20213 /**
20214  * @class Roo.bootstrap.HtmlEditorToolbar1
20215  * Basic Toolbar
20216  * 
20217  * Usage:
20218  *
20219  new Roo.bootstrap.HtmlEditor({
20220     ....
20221     toolbars : [
20222         new Roo.bootstrap.HtmlEditorToolbar1({
20223             disable : { fonts: 1 , format: 1, ..., ... , ...],
20224             btns : [ .... ]
20225         })
20226     }
20227      
20228  * 
20229  * @cfg {Object} disable List of elements to disable..
20230  * @cfg {Array} btns List of additional buttons.
20231  * 
20232  * 
20233  * NEEDS Extra CSS? 
20234  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
20235  */
20236  
20237 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
20238 {
20239     
20240     Roo.apply(this, config);
20241     
20242     // default disabled, based on 'good practice'..
20243     this.disable = this.disable || {};
20244     Roo.applyIf(this.disable, {
20245         fontSize : true,
20246         colors : true,
20247         specialElements : true
20248     });
20249     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
20250     
20251     this.editor = config.editor;
20252     this.editorcore = config.editor.editorcore;
20253     
20254     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
20255     
20256     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
20257     // dont call parent... till later.
20258 }
20259 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
20260      
20261     bar : true,
20262     
20263     editor : false,
20264     editorcore : false,
20265     
20266     
20267     formats : [
20268         "p" ,  
20269         "h1","h2","h3","h4","h5","h6", 
20270         "pre", "code", 
20271         "abbr", "acronym", "address", "cite", "samp", "var",
20272         'div','span'
20273     ],
20274     
20275     onRender : function(ct, position)
20276     {
20277        // Roo.log("Call onRender: " + this.xtype);
20278         
20279        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
20280        Roo.log(this.el);
20281        this.el.dom.style.marginBottom = '0';
20282        var _this = this;
20283        var editorcore = this.editorcore;
20284        var editor= this.editor;
20285        
20286        var children = [];
20287        var btn = function(id,cmd , toggle, handler){
20288        
20289             var  event = toggle ? 'toggle' : 'click';
20290        
20291             var a = {
20292                 size : 'sm',
20293                 xtype: 'Button',
20294                 xns: Roo.bootstrap,
20295                 glyphicon : id,
20296                 cmd : id || cmd,
20297                 enableToggle:toggle !== false,
20298                 //html : 'submit'
20299                 pressed : toggle ? false : null,
20300                 listeners : {}
20301             }
20302             a.listeners[toggle ? 'toggle' : 'click'] = function() {
20303                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
20304             }
20305             children.push(a);
20306             return a;
20307        }
20308         
20309         var style = {
20310                 xtype: 'Button',
20311                 size : 'sm',
20312                 xns: Roo.bootstrap,
20313                 glyphicon : 'font',
20314                 //html : 'submit'
20315                 menu : {
20316                     xtype: 'Menu',
20317                     xns: Roo.bootstrap,
20318                     items:  []
20319                 }
20320         };
20321         Roo.each(this.formats, function(f) {
20322             style.menu.items.push({
20323                 xtype :'MenuItem',
20324                 xns: Roo.bootstrap,
20325                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
20326                 tagname : f,
20327                 listeners : {
20328                     click : function()
20329                     {
20330                         editorcore.insertTag(this.tagname);
20331                         editor.focus();
20332                     }
20333                 }
20334                 
20335             });
20336         });
20337          children.push(style);   
20338             
20339             
20340         btn('bold',false,true);
20341         btn('italic',false,true);
20342         btn('align-left', 'justifyleft',true);
20343         btn('align-center', 'justifycenter',true);
20344         btn('align-right' , 'justifyright',true);
20345         btn('link', false, false, function(btn) {
20346             //Roo.log("create link?");
20347             var url = prompt(this.createLinkText, this.defaultLinkValue);
20348             if(url && url != 'http:/'+'/'){
20349                 this.editorcore.relayCmd('createlink', url);
20350             }
20351         }),
20352         btn('list','insertunorderedlist',true);
20353         btn('pencil', false,true, function(btn){
20354                 Roo.log(this);
20355                 
20356                 this.toggleSourceEdit(btn.pressed);
20357         });
20358         /*
20359         var cog = {
20360                 xtype: 'Button',
20361                 size : 'sm',
20362                 xns: Roo.bootstrap,
20363                 glyphicon : 'cog',
20364                 //html : 'submit'
20365                 menu : {
20366                     xtype: 'Menu',
20367                     xns: Roo.bootstrap,
20368                     items:  []
20369                 }
20370         };
20371         
20372         cog.menu.items.push({
20373             xtype :'MenuItem',
20374             xns: Roo.bootstrap,
20375             html : Clean styles,
20376             tagname : f,
20377             listeners : {
20378                 click : function()
20379                 {
20380                     editorcore.insertTag(this.tagname);
20381                     editor.focus();
20382                 }
20383             }
20384             
20385         });
20386        */
20387         
20388          
20389        this.xtype = 'NavSimplebar';
20390         
20391         for(var i=0;i< children.length;i++) {
20392             
20393             this.buttons.add(this.addxtypeChild(children[i]));
20394             
20395         }
20396         
20397         editor.on('editorevent', this.updateToolbar, this);
20398     },
20399     onBtnClick : function(id)
20400     {
20401        this.editorcore.relayCmd(id);
20402        this.editorcore.focus();
20403     },
20404     
20405     /**
20406      * Protected method that will not generally be called directly. It triggers
20407      * a toolbar update by reading the markup state of the current selection in the editor.
20408      */
20409     updateToolbar: function(){
20410
20411         if(!this.editorcore.activated){
20412             this.editor.onFirstFocus(); // is this neeed?
20413             return;
20414         }
20415
20416         var btns = this.buttons; 
20417         var doc = this.editorcore.doc;
20418         btns.get('bold').setActive(doc.queryCommandState('bold'));
20419         btns.get('italic').setActive(doc.queryCommandState('italic'));
20420         //btns.get('underline').setActive(doc.queryCommandState('underline'));
20421         
20422         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
20423         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
20424         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
20425         
20426         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
20427         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
20428          /*
20429         
20430         var ans = this.editorcore.getAllAncestors();
20431         if (this.formatCombo) {
20432             
20433             
20434             var store = this.formatCombo.store;
20435             this.formatCombo.setValue("");
20436             for (var i =0; i < ans.length;i++) {
20437                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
20438                     // select it..
20439                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
20440                     break;
20441                 }
20442             }
20443         }
20444         
20445         
20446         
20447         // hides menus... - so this cant be on a menu...
20448         Roo.bootstrap.MenuMgr.hideAll();
20449         */
20450         Roo.bootstrap.MenuMgr.hideAll();
20451         //this.editorsyncValue();
20452     },
20453     onFirstFocus: function() {
20454         this.buttons.each(function(item){
20455            item.enable();
20456         });
20457     },
20458     toggleSourceEdit : function(sourceEditMode){
20459         
20460           
20461         if(sourceEditMode){
20462             Roo.log("disabling buttons");
20463            this.buttons.each( function(item){
20464                 if(item.cmd != 'pencil'){
20465                     item.disable();
20466                 }
20467             });
20468           
20469         }else{
20470             Roo.log("enabling buttons");
20471             if(this.editorcore.initialized){
20472                 this.buttons.each( function(item){
20473                     item.enable();
20474                 });
20475             }
20476             
20477         }
20478         Roo.log("calling toggole on editor");
20479         // tell the editor that it's been pressed..
20480         this.editor.toggleSourceEdit(sourceEditMode);
20481        
20482     }
20483 });
20484
20485
20486
20487
20488
20489 /**
20490  * @class Roo.bootstrap.Table.AbstractSelectionModel
20491  * @extends Roo.util.Observable
20492  * Abstract base class for grid SelectionModels.  It provides the interface that should be
20493  * implemented by descendant classes.  This class should not be directly instantiated.
20494  * @constructor
20495  */
20496 Roo.bootstrap.Table.AbstractSelectionModel = function(){
20497     this.locked = false;
20498     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
20499 };
20500
20501
20502 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
20503     /** @ignore Called by the grid automatically. Do not call directly. */
20504     init : function(grid){
20505         this.grid = grid;
20506         this.initEvents();
20507     },
20508
20509     /**
20510      * Locks the selections.
20511      */
20512     lock : function(){
20513         this.locked = true;
20514     },
20515
20516     /**
20517      * Unlocks the selections.
20518      */
20519     unlock : function(){
20520         this.locked = false;
20521     },
20522
20523     /**
20524      * Returns true if the selections are locked.
20525      * @return {Boolean}
20526      */
20527     isLocked : function(){
20528         return this.locked;
20529     }
20530 });
20531 /**
20532  * @extends Roo.bootstrap.Table.AbstractSelectionModel
20533  * @class Roo.bootstrap.Table.RowSelectionModel
20534  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
20535  * It supports multiple selections and keyboard selection/navigation. 
20536  * @constructor
20537  * @param {Object} config
20538  */
20539
20540 Roo.bootstrap.Table.RowSelectionModel = function(config){
20541     Roo.apply(this, config);
20542     this.selections = new Roo.util.MixedCollection(false, function(o){
20543         return o.id;
20544     });
20545
20546     this.last = false;
20547     this.lastActive = false;
20548
20549     this.addEvents({
20550         /**
20551              * @event selectionchange
20552              * Fires when the selection changes
20553              * @param {SelectionModel} this
20554              */
20555             "selectionchange" : true,
20556         /**
20557              * @event afterselectionchange
20558              * Fires after the selection changes (eg. by key press or clicking)
20559              * @param {SelectionModel} this
20560              */
20561             "afterselectionchange" : true,
20562         /**
20563              * @event beforerowselect
20564              * Fires when a row is selected being selected, return false to cancel.
20565              * @param {SelectionModel} this
20566              * @param {Number} rowIndex The selected index
20567              * @param {Boolean} keepExisting False if other selections will be cleared
20568              */
20569             "beforerowselect" : true,
20570         /**
20571              * @event rowselect
20572              * Fires when a row is selected.
20573              * @param {SelectionModel} this
20574              * @param {Number} rowIndex The selected index
20575              * @param {Roo.data.Record} r The record
20576              */
20577             "rowselect" : true,
20578         /**
20579              * @event rowdeselect
20580              * Fires when a row is deselected.
20581              * @param {SelectionModel} this
20582              * @param {Number} rowIndex The selected index
20583              */
20584         "rowdeselect" : true
20585     });
20586     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
20587     this.locked = false;
20588 };
20589
20590 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
20591     /**
20592      * @cfg {Boolean} singleSelect
20593      * True to allow selection of only one row at a time (defaults to false)
20594      */
20595     singleSelect : false,
20596
20597     // private
20598     initEvents : function(){
20599
20600         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
20601             this.grid.on("mousedown", this.handleMouseDown, this);
20602         }else{ // allow click to work like normal
20603             this.grid.on("rowclick", this.handleDragableRowClick, this);
20604         }
20605
20606         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
20607             "up" : function(e){
20608                 if(!e.shiftKey){
20609                     this.selectPrevious(e.shiftKey);
20610                 }else if(this.last !== false && this.lastActive !== false){
20611                     var last = this.last;
20612                     this.selectRange(this.last,  this.lastActive-1);
20613                     this.grid.getView().focusRow(this.lastActive);
20614                     if(last !== false){
20615                         this.last = last;
20616                     }
20617                 }else{
20618                     this.selectFirstRow();
20619                 }
20620                 this.fireEvent("afterselectionchange", this);
20621             },
20622             "down" : function(e){
20623                 if(!e.shiftKey){
20624                     this.selectNext(e.shiftKey);
20625                 }else if(this.last !== false && this.lastActive !== false){
20626                     var last = this.last;
20627                     this.selectRange(this.last,  this.lastActive+1);
20628                     this.grid.getView().focusRow(this.lastActive);
20629                     if(last !== false){
20630                         this.last = last;
20631                     }
20632                 }else{
20633                     this.selectFirstRow();
20634                 }
20635                 this.fireEvent("afterselectionchange", this);
20636             },
20637             scope: this
20638         });
20639
20640         var view = this.grid.view;
20641         view.on("refresh", this.onRefresh, this);
20642         view.on("rowupdated", this.onRowUpdated, this);
20643         view.on("rowremoved", this.onRemove, this);
20644     },
20645
20646     // private
20647     onRefresh : function(){
20648         var ds = this.grid.dataSource, i, v = this.grid.view;
20649         var s = this.selections;
20650         s.each(function(r){
20651             if((i = ds.indexOfId(r.id)) != -1){
20652                 v.onRowSelect(i);
20653             }else{
20654                 s.remove(r);
20655             }
20656         });
20657     },
20658
20659     // private
20660     onRemove : function(v, index, r){
20661         this.selections.remove(r);
20662     },
20663
20664     // private
20665     onRowUpdated : function(v, index, r){
20666         if(this.isSelected(r)){
20667             v.onRowSelect(index);
20668         }
20669     },
20670
20671     /**
20672      * Select records.
20673      * @param {Array} records The records to select
20674      * @param {Boolean} keepExisting (optional) True to keep existing selections
20675      */
20676     selectRecords : function(records, keepExisting){
20677         if(!keepExisting){
20678             this.clearSelections();
20679         }
20680         var ds = this.grid.dataSource;
20681         for(var i = 0, len = records.length; i < len; i++){
20682             this.selectRow(ds.indexOf(records[i]), true);
20683         }
20684     },
20685
20686     /**
20687      * Gets the number of selected rows.
20688      * @return {Number}
20689      */
20690     getCount : function(){
20691         return this.selections.length;
20692     },
20693
20694     /**
20695      * Selects the first row in the grid.
20696      */
20697     selectFirstRow : function(){
20698         this.selectRow(0);
20699     },
20700
20701     /**
20702      * Select the last row.
20703      * @param {Boolean} keepExisting (optional) True to keep existing selections
20704      */
20705     selectLastRow : function(keepExisting){
20706         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
20707     },
20708
20709     /**
20710      * Selects the row immediately following the last selected row.
20711      * @param {Boolean} keepExisting (optional) True to keep existing selections
20712      */
20713     selectNext : function(keepExisting){
20714         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
20715             this.selectRow(this.last+1, keepExisting);
20716             this.grid.getView().focusRow(this.last);
20717         }
20718     },
20719
20720     /**
20721      * Selects the row that precedes the last selected row.
20722      * @param {Boolean} keepExisting (optional) True to keep existing selections
20723      */
20724     selectPrevious : function(keepExisting){
20725         if(this.last){
20726             this.selectRow(this.last-1, keepExisting);
20727             this.grid.getView().focusRow(this.last);
20728         }
20729     },
20730
20731     /**
20732      * Returns the selected records
20733      * @return {Array} Array of selected records
20734      */
20735     getSelections : function(){
20736         return [].concat(this.selections.items);
20737     },
20738
20739     /**
20740      * Returns the first selected record.
20741      * @return {Record}
20742      */
20743     getSelected : function(){
20744         return this.selections.itemAt(0);
20745     },
20746
20747
20748     /**
20749      * Clears all selections.
20750      */
20751     clearSelections : function(fast){
20752         if(this.locked) return;
20753         if(fast !== true){
20754             var ds = this.grid.dataSource;
20755             var s = this.selections;
20756             s.each(function(r){
20757                 this.deselectRow(ds.indexOfId(r.id));
20758             }, this);
20759             s.clear();
20760         }else{
20761             this.selections.clear();
20762         }
20763         this.last = false;
20764     },
20765
20766
20767     /**
20768      * Selects all rows.
20769      */
20770     selectAll : function(){
20771         if(this.locked) return;
20772         this.selections.clear();
20773         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
20774             this.selectRow(i, true);
20775         }
20776     },
20777
20778     /**
20779      * Returns True if there is a selection.
20780      * @return {Boolean}
20781      */
20782     hasSelection : function(){
20783         return this.selections.length > 0;
20784     },
20785
20786     /**
20787      * Returns True if the specified row is selected.
20788      * @param {Number/Record} record The record or index of the record to check
20789      * @return {Boolean}
20790      */
20791     isSelected : function(index){
20792         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
20793         return (r && this.selections.key(r.id) ? true : false);
20794     },
20795
20796     /**
20797      * Returns True if the specified record id is selected.
20798      * @param {String} id The id of record to check
20799      * @return {Boolean}
20800      */
20801     isIdSelected : function(id){
20802         return (this.selections.key(id) ? true : false);
20803     },
20804
20805     // private
20806     handleMouseDown : function(e, t){
20807         var view = this.grid.getView(), rowIndex;
20808         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
20809             return;
20810         };
20811         if(e.shiftKey && this.last !== false){
20812             var last = this.last;
20813             this.selectRange(last, rowIndex, e.ctrlKey);
20814             this.last = last; // reset the last
20815             view.focusRow(rowIndex);
20816         }else{
20817             var isSelected = this.isSelected(rowIndex);
20818             if(e.button !== 0 && isSelected){
20819                 view.focusRow(rowIndex);
20820             }else if(e.ctrlKey && isSelected){
20821                 this.deselectRow(rowIndex);
20822             }else if(!isSelected){
20823                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
20824                 view.focusRow(rowIndex);
20825             }
20826         }
20827         this.fireEvent("afterselectionchange", this);
20828     },
20829     // private
20830     handleDragableRowClick :  function(grid, rowIndex, e) 
20831     {
20832         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
20833             this.selectRow(rowIndex, false);
20834             grid.view.focusRow(rowIndex);
20835              this.fireEvent("afterselectionchange", this);
20836         }
20837     },
20838     
20839     /**
20840      * Selects multiple rows.
20841      * @param {Array} rows Array of the indexes of the row to select
20842      * @param {Boolean} keepExisting (optional) True to keep existing selections
20843      */
20844     selectRows : function(rows, keepExisting){
20845         if(!keepExisting){
20846             this.clearSelections();
20847         }
20848         for(var i = 0, len = rows.length; i < len; i++){
20849             this.selectRow(rows[i], true);
20850         }
20851     },
20852
20853     /**
20854      * Selects a range of rows. All rows in between startRow and endRow are also selected.
20855      * @param {Number} startRow The index of the first row in the range
20856      * @param {Number} endRow The index of the last row in the range
20857      * @param {Boolean} keepExisting (optional) True to retain existing selections
20858      */
20859     selectRange : function(startRow, endRow, keepExisting){
20860         if(this.locked) return;
20861         if(!keepExisting){
20862             this.clearSelections();
20863         }
20864         if(startRow <= endRow){
20865             for(var i = startRow; i <= endRow; i++){
20866                 this.selectRow(i, true);
20867             }
20868         }else{
20869             for(var i = startRow; i >= endRow; i--){
20870                 this.selectRow(i, true);
20871             }
20872         }
20873     },
20874
20875     /**
20876      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
20877      * @param {Number} startRow The index of the first row in the range
20878      * @param {Number} endRow The index of the last row in the range
20879      */
20880     deselectRange : function(startRow, endRow, preventViewNotify){
20881         if(this.locked) return;
20882         for(var i = startRow; i <= endRow; i++){
20883             this.deselectRow(i, preventViewNotify);
20884         }
20885     },
20886
20887     /**
20888      * Selects a row.
20889      * @param {Number} row The index of the row to select
20890      * @param {Boolean} keepExisting (optional) True to keep existing selections
20891      */
20892     selectRow : function(index, keepExisting, preventViewNotify){
20893         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
20894         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
20895             if(!keepExisting || this.singleSelect){
20896                 this.clearSelections();
20897             }
20898             var r = this.grid.dataSource.getAt(index);
20899             this.selections.add(r);
20900             this.last = this.lastActive = index;
20901             if(!preventViewNotify){
20902                 this.grid.getView().onRowSelect(index);
20903             }
20904             this.fireEvent("rowselect", this, index, r);
20905             this.fireEvent("selectionchange", this);
20906         }
20907     },
20908
20909     /**
20910      * Deselects a row.
20911      * @param {Number} row The index of the row to deselect
20912      */
20913     deselectRow : function(index, preventViewNotify){
20914         if(this.locked) return;
20915         if(this.last == index){
20916             this.last = false;
20917         }
20918         if(this.lastActive == index){
20919             this.lastActive = false;
20920         }
20921         var r = this.grid.dataSource.getAt(index);
20922         this.selections.remove(r);
20923         if(!preventViewNotify){
20924             this.grid.getView().onRowDeselect(index);
20925         }
20926         this.fireEvent("rowdeselect", this, index);
20927         this.fireEvent("selectionchange", this);
20928     },
20929
20930     // private
20931     restoreLast : function(){
20932         if(this._last){
20933             this.last = this._last;
20934         }
20935     },
20936
20937     // private
20938     acceptsNav : function(row, col, cm){
20939         return !cm.isHidden(col) && cm.isCellEditable(col, row);
20940     },
20941
20942     // private
20943     onEditorKey : function(field, e){
20944         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
20945         if(k == e.TAB){
20946             e.stopEvent();
20947             ed.completeEdit();
20948             if(e.shiftKey){
20949                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
20950             }else{
20951                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
20952             }
20953         }else if(k == e.ENTER && !e.ctrlKey){
20954             e.stopEvent();
20955             ed.completeEdit();
20956             if(e.shiftKey){
20957                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
20958             }else{
20959                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
20960             }
20961         }else if(k == e.ESC){
20962             ed.cancelEdit();
20963         }
20964         if(newCell){
20965             g.startEditing(newCell[0], newCell[1]);
20966         }
20967     }
20968 });/*
20969  * Based on:
20970  * Ext JS Library 1.1.1
20971  * Copyright(c) 2006-2007, Ext JS, LLC.
20972  *
20973  * Originally Released Under LGPL - original licence link has changed is not relivant.
20974  *
20975  * Fork - LGPL
20976  * <script type="text/javascript">
20977  */
20978  
20979 /**
20980  * @class Roo.bootstrap.PagingToolbar
20981  * @extends Roo.Row
20982  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
20983  * @constructor
20984  * Create a new PagingToolbar
20985  * @param {Object} config The config object
20986  */
20987 Roo.bootstrap.PagingToolbar = function(config)
20988 {
20989     // old args format still supported... - xtype is prefered..
20990         // created from xtype...
20991     var ds = config.dataSource;
20992     this.toolbarItems = [];
20993     if (config.items) {
20994         this.toolbarItems = config.items;
20995 //        config.items = [];
20996     }
20997     
20998     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
20999     this.ds = ds;
21000     this.cursor = 0;
21001     if (ds) { 
21002         this.bind(ds);
21003     }
21004     
21005     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
21006     
21007 };
21008
21009 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
21010     /**
21011      * @cfg {Roo.data.Store} dataSource
21012      * The underlying data store providing the paged data
21013      */
21014     /**
21015      * @cfg {String/HTMLElement/Element} container
21016      * container The id or element that will contain the toolbar
21017      */
21018     /**
21019      * @cfg {Boolean} displayInfo
21020      * True to display the displayMsg (defaults to false)
21021      */
21022     /**
21023      * @cfg {Number} pageSize
21024      * The number of records to display per page (defaults to 20)
21025      */
21026     pageSize: 20,
21027     /**
21028      * @cfg {String} displayMsg
21029      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
21030      */
21031     displayMsg : 'Displaying {0} - {1} of {2}',
21032     /**
21033      * @cfg {String} emptyMsg
21034      * The message to display when no records are found (defaults to "No data to display")
21035      */
21036     emptyMsg : 'No data to display',
21037     /**
21038      * Customizable piece of the default paging text (defaults to "Page")
21039      * @type String
21040      */
21041     beforePageText : "Page",
21042     /**
21043      * Customizable piece of the default paging text (defaults to "of %0")
21044      * @type String
21045      */
21046     afterPageText : "of {0}",
21047     /**
21048      * Customizable piece of the default paging text (defaults to "First Page")
21049      * @type String
21050      */
21051     firstText : "First Page",
21052     /**
21053      * Customizable piece of the default paging text (defaults to "Previous Page")
21054      * @type String
21055      */
21056     prevText : "Previous Page",
21057     /**
21058      * Customizable piece of the default paging text (defaults to "Next Page")
21059      * @type String
21060      */
21061     nextText : "Next Page",
21062     /**
21063      * Customizable piece of the default paging text (defaults to "Last Page")
21064      * @type String
21065      */
21066     lastText : "Last Page",
21067     /**
21068      * Customizable piece of the default paging text (defaults to "Refresh")
21069      * @type String
21070      */
21071     refreshText : "Refresh",
21072
21073     buttons : false,
21074     // private
21075     onRender : function(ct, position) 
21076     {
21077         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
21078         this.navgroup.parentId = this.id;
21079         this.navgroup.onRender(this.el, null);
21080         // add the buttons to the navgroup
21081         
21082         if(this.displayInfo){
21083             Roo.log(this.el.select('ul.navbar-nav',true).first());
21084             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
21085             this.displayEl = this.el.select('.x-paging-info', true).first();
21086 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
21087 //            this.displayEl = navel.el.select('span',true).first();
21088         }
21089         
21090         var _this = this;
21091         
21092         if(this.buttons){
21093             Roo.each(_this.buttons, function(e){
21094                Roo.factory(e).onRender(_this.el, null);
21095             });
21096         }
21097             
21098         Roo.each(_this.toolbarItems, function(e) {
21099             _this.navgroup.addItem(e);
21100         });
21101         
21102         
21103         this.first = this.navgroup.addItem({
21104             tooltip: this.firstText,
21105             cls: "prev",
21106             icon : 'fa fa-backward',
21107             disabled: true,
21108             preventDefault: true,
21109             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
21110         });
21111         
21112         this.prev =  this.navgroup.addItem({
21113             tooltip: this.prevText,
21114             cls: "prev",
21115             icon : 'fa fa-step-backward',
21116             disabled: true,
21117             preventDefault: true,
21118             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
21119         });
21120     //this.addSeparator();
21121         
21122         
21123         var field = this.navgroup.addItem( {
21124             tagtype : 'span',
21125             cls : 'x-paging-position',
21126             
21127             html : this.beforePageText  +
21128                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
21129                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
21130          } ); //?? escaped?
21131         
21132         this.field = field.el.select('input', true).first();
21133         this.field.on("keydown", this.onPagingKeydown, this);
21134         this.field.on("focus", function(){this.dom.select();});
21135     
21136     
21137         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
21138         //this.field.setHeight(18);
21139         //this.addSeparator();
21140         this.next = this.navgroup.addItem({
21141             tooltip: this.nextText,
21142             cls: "next",
21143             html : ' <i class="fa fa-step-forward">',
21144             disabled: true,
21145             preventDefault: true,
21146             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
21147         });
21148         this.last = this.navgroup.addItem({
21149             tooltip: this.lastText,
21150             icon : 'fa fa-forward',
21151             cls: "next",
21152             disabled: true,
21153             preventDefault: true,
21154             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
21155         });
21156     //this.addSeparator();
21157         this.loading = this.navgroup.addItem({
21158             tooltip: this.refreshText,
21159             icon: 'fa fa-refresh',
21160             preventDefault: true,
21161             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
21162         });
21163
21164     },
21165
21166     // private
21167     updateInfo : function(){
21168         if(this.displayEl){
21169             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
21170             var msg = count == 0 ?
21171                 this.emptyMsg :
21172                 String.format(
21173                     this.displayMsg,
21174                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
21175                 );
21176             this.displayEl.update(msg);
21177         }
21178     },
21179
21180     // private
21181     onLoad : function(ds, r, o){
21182        this.cursor = o.params ? o.params.start : 0;
21183        var d = this.getPageData(),
21184             ap = d.activePage,
21185             ps = d.pages;
21186         
21187        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
21188        this.field.dom.value = ap;
21189        this.first.setDisabled(ap == 1);
21190        this.prev.setDisabled(ap == 1);
21191        this.next.setDisabled(ap == ps);
21192        this.last.setDisabled(ap == ps);
21193        this.loading.enable();
21194        this.updateInfo();
21195     },
21196
21197     // private
21198     getPageData : function(){
21199         var total = this.ds.getTotalCount();
21200         return {
21201             total : total,
21202             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
21203             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
21204         };
21205     },
21206
21207     // private
21208     onLoadError : function(){
21209         this.loading.enable();
21210     },
21211
21212     // private
21213     onPagingKeydown : function(e){
21214         var k = e.getKey();
21215         var d = this.getPageData();
21216         if(k == e.RETURN){
21217             var v = this.field.dom.value, pageNum;
21218             if(!v || isNaN(pageNum = parseInt(v, 10))){
21219                 this.field.dom.value = d.activePage;
21220                 return;
21221             }
21222             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
21223             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
21224             e.stopEvent();
21225         }
21226         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))
21227         {
21228           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
21229           this.field.dom.value = pageNum;
21230           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
21231           e.stopEvent();
21232         }
21233         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
21234         {
21235           var v = this.field.dom.value, pageNum; 
21236           var increment = (e.shiftKey) ? 10 : 1;
21237           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
21238             increment *= -1;
21239           if(!v || isNaN(pageNum = parseInt(v, 10))) {
21240             this.field.dom.value = d.activePage;
21241             return;
21242           }
21243           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
21244           {
21245             this.field.dom.value = parseInt(v, 10) + increment;
21246             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
21247             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
21248           }
21249           e.stopEvent();
21250         }
21251     },
21252
21253     // private
21254     beforeLoad : function(){
21255         if(this.loading){
21256             this.loading.disable();
21257         }
21258     },
21259
21260     // private
21261     onClick : function(which){
21262         
21263         var ds = this.ds;
21264         if (!ds) {
21265             return;
21266         }
21267         
21268         switch(which){
21269             case "first":
21270                 ds.load({params:{start: 0, limit: this.pageSize}});
21271             break;
21272             case "prev":
21273                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
21274             break;
21275             case "next":
21276                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
21277             break;
21278             case "last":
21279                 var total = ds.getTotalCount();
21280                 var extra = total % this.pageSize;
21281                 var lastStart = extra ? (total - extra) : total-this.pageSize;
21282                 ds.load({params:{start: lastStart, limit: this.pageSize}});
21283             break;
21284             case "refresh":
21285                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
21286             break;
21287         }
21288     },
21289
21290     /**
21291      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
21292      * @param {Roo.data.Store} store The data store to unbind
21293      */
21294     unbind : function(ds){
21295         ds.un("beforeload", this.beforeLoad, this);
21296         ds.un("load", this.onLoad, this);
21297         ds.un("loadexception", this.onLoadError, this);
21298         ds.un("remove", this.updateInfo, this);
21299         ds.un("add", this.updateInfo, this);
21300         this.ds = undefined;
21301     },
21302
21303     /**
21304      * Binds the paging toolbar to the specified {@link Roo.data.Store}
21305      * @param {Roo.data.Store} store The data store to bind
21306      */
21307     bind : function(ds){
21308         ds.on("beforeload", this.beforeLoad, this);
21309         ds.on("load", this.onLoad, this);
21310         ds.on("loadexception", this.onLoadError, this);
21311         ds.on("remove", this.updateInfo, this);
21312         ds.on("add", this.updateInfo, this);
21313         this.ds = ds;
21314     }
21315 });/*
21316  * - LGPL
21317  *
21318  * element
21319  * 
21320  */
21321
21322 /**
21323  * @class Roo.bootstrap.MessageBar
21324  * @extends Roo.bootstrap.Component
21325  * Bootstrap MessageBar class
21326  * @cfg {String} html contents of the MessageBar
21327  * @cfg {String} weight (info | success | warning | danger) default info
21328  * @cfg {String} beforeClass insert the bar before the given class
21329  * @cfg {Boolean} closable (true | false) default false
21330  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
21331  * 
21332  * @constructor
21333  * Create a new Element
21334  * @param {Object} config The config object
21335  */
21336
21337 Roo.bootstrap.MessageBar = function(config){
21338     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
21339 };
21340
21341 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
21342     
21343     html: '',
21344     weight: 'info',
21345     closable: false,
21346     fixed: false,
21347     beforeClass: 'bootstrap-sticky-wrap',
21348     
21349     getAutoCreate : function(){
21350         
21351         var cfg = {
21352             tag: 'div',
21353             cls: 'alert alert-dismissable alert-' + this.weight,
21354             cn: [
21355                 {
21356                     tag: 'span',
21357                     cls: 'message',
21358                     html: this.html || ''
21359                 }
21360             ]
21361         }
21362         
21363         if(this.fixed){
21364             cfg.cls += ' alert-messages-fixed';
21365         }
21366         
21367         if(this.closable){
21368             cfg.cn.push({
21369                 tag: 'button',
21370                 cls: 'close',
21371                 html: 'x'
21372             });
21373         }
21374         
21375         return cfg;
21376     },
21377     
21378     onRender : function(ct, position)
21379     {
21380         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21381         
21382         if(!this.el){
21383             var cfg = Roo.apply({},  this.getAutoCreate());
21384             cfg.id = Roo.id();
21385             
21386             if (this.cls) {
21387                 cfg.cls += ' ' + this.cls;
21388             }
21389             if (this.style) {
21390                 cfg.style = this.style;
21391             }
21392             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
21393             
21394             this.el.setVisibilityMode(Roo.Element.DISPLAY);
21395         }
21396         
21397         this.el.select('>button.close').on('click', this.hide, this);
21398         
21399     },
21400     
21401     show : function()
21402     {
21403         if (!this.rendered) {
21404             this.render();
21405         }
21406         
21407         this.el.show();
21408         
21409         this.fireEvent('show', this);
21410         
21411     },
21412     
21413     hide : function()
21414     {
21415         if (!this.rendered) {
21416             this.render();
21417         }
21418         
21419         this.el.hide();
21420         
21421         this.fireEvent('hide', this);
21422     },
21423     
21424     update : function()
21425     {
21426 //        var e = this.el.dom.firstChild;
21427 //        
21428 //        if(this.closable){
21429 //            e = e.nextSibling;
21430 //        }
21431 //        
21432 //        e.data = this.html || '';
21433
21434         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
21435     }
21436    
21437 });
21438
21439  
21440
21441      /*
21442  * - LGPL
21443  *
21444  * Graph
21445  * 
21446  */
21447
21448
21449 /**
21450  * @class Roo.bootstrap.Graph
21451  * @extends Roo.bootstrap.Component
21452  * Bootstrap Graph class
21453 > Prameters
21454  -sm {number} sm 4
21455  -md {number} md 5
21456  @cfg {String} graphtype  bar | vbar | pie
21457  @cfg {number} g_x coodinator | centre x (pie)
21458  @cfg {number} g_y coodinator | centre y (pie)
21459  @cfg {number} g_r radius (pie)
21460  @cfg {number} g_height height of the chart (respected by all elements in the set)
21461  @cfg {number} g_width width of the chart (respected by all elements in the set)
21462  @cfg {Object} title The title of the chart
21463     
21464  -{Array}  values
21465  -opts (object) options for the chart 
21466      o {
21467      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
21468      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
21469      o vgutter (number)
21470      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.
21471      o stacked (boolean) whether or not to tread values as in a stacked bar chart
21472      o to
21473      o stretch (boolean)
21474      o }
21475  -opts (object) options for the pie
21476      o{
21477      o cut
21478      o startAngle (number)
21479      o endAngle (number)
21480      } 
21481  *
21482  * @constructor
21483  * Create a new Input
21484  * @param {Object} config The config object
21485  */
21486
21487 Roo.bootstrap.Graph = function(config){
21488     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
21489     
21490     this.addEvents({
21491         // img events
21492         /**
21493          * @event click
21494          * The img click event for the img.
21495          * @param {Roo.EventObject} e
21496          */
21497         "click" : true
21498     });
21499 };
21500
21501 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
21502     
21503     sm: 4,
21504     md: 5,
21505     graphtype: 'bar',
21506     g_height: 250,
21507     g_width: 400,
21508     g_x: 50,
21509     g_y: 50,
21510     g_r: 30,
21511     opts:{
21512         //g_colors: this.colors,
21513         g_type: 'soft',
21514         g_gutter: '20%'
21515
21516     },
21517     title : false,
21518
21519     getAutoCreate : function(){
21520         
21521         var cfg = {
21522             tag: 'div',
21523             html : null
21524         }
21525         
21526         
21527         return  cfg;
21528     },
21529
21530     onRender : function(ct,position){
21531         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
21532         this.raphael = Raphael(this.el.dom);
21533         
21534                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
21535                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
21536                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
21537                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
21538                 /*
21539                 r.text(160, 10, "Single Series Chart").attr(txtattr);
21540                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
21541                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
21542                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
21543                 
21544                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
21545                 r.barchart(330, 10, 300, 220, data1);
21546                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
21547                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
21548                 */
21549                 
21550                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
21551                 // r.barchart(30, 30, 560, 250,  xdata, {
21552                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
21553                 //     axis : "0 0 1 1",
21554                 //     axisxlabels :  xdata
21555                 //     //yvalues : cols,
21556                    
21557                 // });
21558 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
21559 //        
21560 //        this.load(null,xdata,{
21561 //                axis : "0 0 1 1",
21562 //                axisxlabels :  xdata
21563 //                });
21564
21565     },
21566
21567     load : function(graphtype,xdata,opts){
21568         this.raphael.clear();
21569         if(!graphtype) {
21570             graphtype = this.graphtype;
21571         }
21572         if(!opts){
21573             opts = this.opts;
21574         }
21575         var r = this.raphael,
21576             fin = function () {
21577                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
21578             },
21579             fout = function () {
21580                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
21581             },
21582             pfin = function() {
21583                 this.sector.stop();
21584                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
21585
21586                 if (this.label) {
21587                     this.label[0].stop();
21588                     this.label[0].attr({ r: 7.5 });
21589                     this.label[1].attr({ "font-weight": 800 });
21590                 }
21591             },
21592             pfout = function() {
21593                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
21594
21595                 if (this.label) {
21596                     this.label[0].animate({ r: 5 }, 500, "bounce");
21597                     this.label[1].attr({ "font-weight": 400 });
21598                 }
21599             };
21600
21601         switch(graphtype){
21602             case 'bar':
21603                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
21604                 break;
21605             case 'hbar':
21606                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
21607                 break;
21608             case 'pie':
21609 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
21610 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
21611 //            
21612                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
21613                 
21614                 break;
21615
21616         }
21617         
21618         if(this.title){
21619             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
21620         }
21621         
21622     },
21623     
21624     setTitle: function(o)
21625     {
21626         this.title = o;
21627     },
21628     
21629     initEvents: function() {
21630         
21631         if(!this.href){
21632             this.el.on('click', this.onClick, this);
21633         }
21634     },
21635     
21636     onClick : function(e)
21637     {
21638         Roo.log('img onclick');
21639         this.fireEvent('click', this, e);
21640     }
21641    
21642 });
21643
21644  
21645 /*
21646  * - LGPL
21647  *
21648  * numberBox
21649  * 
21650  */
21651 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21652
21653 /**
21654  * @class Roo.bootstrap.dash.NumberBox
21655  * @extends Roo.bootstrap.Component
21656  * Bootstrap NumberBox class
21657  * @cfg {String} headline Box headline
21658  * @cfg {String} content Box content
21659  * @cfg {String} icon Box icon
21660  * @cfg {String} footer Footer text
21661  * @cfg {String} fhref Footer href
21662  * 
21663  * @constructor
21664  * Create a new NumberBox
21665  * @param {Object} config The config object
21666  */
21667
21668
21669 Roo.bootstrap.dash.NumberBox = function(config){
21670     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
21671     
21672 };
21673
21674 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
21675     
21676     headline : '',
21677     content : '',
21678     icon : '',
21679     footer : '',
21680     fhref : '',
21681     ficon : '',
21682     
21683     getAutoCreate : function(){
21684         
21685         var cfg = {
21686             tag : 'div',
21687             cls : 'small-box ',
21688             cn : [
21689                 {
21690                     tag : 'div',
21691                     cls : 'inner',
21692                     cn :[
21693                         {
21694                             tag : 'h3',
21695                             cls : 'roo-headline',
21696                             html : this.headline
21697                         },
21698                         {
21699                             tag : 'p',
21700                             cls : 'roo-content',
21701                             html : this.content
21702                         }
21703                     ]
21704                 }
21705             ]
21706         }
21707         
21708         if(this.icon){
21709             cfg.cn.push({
21710                 tag : 'div',
21711                 cls : 'icon',
21712                 cn :[
21713                     {
21714                         tag : 'i',
21715                         cls : 'ion ' + this.icon
21716                     }
21717                 ]
21718             });
21719         }
21720         
21721         if(this.footer){
21722             var footer = {
21723                 tag : 'a',
21724                 cls : 'small-box-footer',
21725                 href : this.fhref || '#',
21726                 html : this.footer
21727             };
21728             
21729             cfg.cn.push(footer);
21730             
21731         }
21732         
21733         return  cfg;
21734     },
21735
21736     onRender : function(ct,position){
21737         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
21738
21739
21740        
21741                 
21742     },
21743
21744     setHeadline: function (value)
21745     {
21746         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
21747     },
21748     
21749     setFooter: function (value, href)
21750     {
21751         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
21752         
21753         if(href){
21754             this.el.select('a.small-box-footer',true).first().attr('href', href);
21755         }
21756         
21757     },
21758
21759     setContent: function (value)
21760     {
21761         this.el.select('.roo-content',true).first().dom.innerHTML = value;
21762     },
21763
21764     initEvents: function() 
21765     {   
21766         
21767     }
21768     
21769 });
21770
21771  
21772 /*
21773  * - LGPL
21774  *
21775  * TabBox
21776  * 
21777  */
21778 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21779
21780 /**
21781  * @class Roo.bootstrap.dash.TabBox
21782  * @extends Roo.bootstrap.Component
21783  * Bootstrap TabBox class
21784  * @cfg {String} title Title of the TabBox
21785  * @cfg {String} icon Icon of the TabBox
21786  * @cfg {Boolean} showtabs (true|false) show the tabs default true
21787  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
21788  * 
21789  * @constructor
21790  * Create a new TabBox
21791  * @param {Object} config The config object
21792  */
21793
21794
21795 Roo.bootstrap.dash.TabBox = function(config){
21796     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
21797     this.addEvents({
21798         // raw events
21799         /**
21800          * @event addpane
21801          * When a pane is added
21802          * @param {Roo.bootstrap.dash.TabPane} pane
21803          */
21804         "addpane" : true,
21805         /**
21806          * @event activatepane
21807          * When a pane is activated
21808          * @param {Roo.bootstrap.dash.TabPane} pane
21809          */
21810         "activatepane" : true
21811         
21812          
21813     });
21814     
21815     this.panes = [];
21816 };
21817
21818 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
21819
21820     title : '',
21821     icon : false,
21822     showtabs : true,
21823     tabScrollable : false,
21824     
21825     getChildContainer : function()
21826     {
21827         return this.el.select('.tab-content', true).first();
21828     },
21829     
21830     getAutoCreate : function(){
21831         
21832         var header = {
21833             tag: 'li',
21834             cls: 'pull-left header',
21835             html: this.title,
21836             cn : []
21837         };
21838         
21839         if(this.icon){
21840             header.cn.push({
21841                 tag: 'i',
21842                 cls: 'fa ' + this.icon
21843             });
21844         }
21845         
21846         var h = {
21847             tag: 'ul',
21848             cls: 'nav nav-tabs pull-right',
21849             cn: [
21850                 header
21851             ]
21852         };
21853         
21854         if(this.tabScrollable){
21855             h = {
21856                 tag: 'div',
21857                 cls: 'tab-header',
21858                 cn: [
21859                     {
21860                         tag: 'ul',
21861                         cls: 'nav nav-tabs pull-right',
21862                         cn: [
21863                             header
21864                         ]
21865                     }
21866                 ]
21867             }
21868         }
21869         
21870         var cfg = {
21871             tag: 'div',
21872             cls: 'nav-tabs-custom',
21873             cn: [
21874                 h,
21875                 {
21876                     tag: 'div',
21877                     cls: 'tab-content no-padding',
21878                     cn: []
21879                 }
21880             ]
21881         }
21882
21883         return  cfg;
21884     },
21885     initEvents : function()
21886     {
21887         //Roo.log('add add pane handler');
21888         this.on('addpane', this.onAddPane, this);
21889     },
21890      /**
21891      * Updates the box title
21892      * @param {String} html to set the title to.
21893      */
21894     setTitle : function(value)
21895     {
21896         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
21897     },
21898     onAddPane : function(pane)
21899     {
21900         this.panes.push(pane);
21901         //Roo.log('addpane');
21902         //Roo.log(pane);
21903         // tabs are rendere left to right..
21904         if(!this.showtabs){
21905             return;
21906         }
21907         
21908         var ctr = this.el.select('.nav-tabs', true).first();
21909          
21910          
21911         var existing = ctr.select('.nav-tab',true);
21912         var qty = existing.getCount();;
21913         
21914         
21915         var tab = ctr.createChild({
21916             tag : 'li',
21917             cls : 'nav-tab' + (qty ? '' : ' active'),
21918             cn : [
21919                 {
21920                     tag : 'a',
21921                     href:'#',
21922                     html : pane.title
21923                 }
21924             ]
21925         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
21926         pane.tab = tab;
21927         
21928         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
21929         if (!qty) {
21930             pane.el.addClass('active');
21931         }
21932         
21933                 
21934     },
21935     onTabClick : function(ev,un,ob,pane)
21936     {
21937         //Roo.log('tab - prev default');
21938         ev.preventDefault();
21939         
21940         
21941         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
21942         pane.tab.addClass('active');
21943         //Roo.log(pane.title);
21944         this.getChildContainer().select('.tab-pane',true).removeClass('active');
21945         // technically we should have a deactivate event.. but maybe add later.
21946         // and it should not de-activate the selected tab...
21947         this.fireEvent('activatepane', pane);
21948         pane.el.addClass('active');
21949         pane.fireEvent('activate');
21950         
21951         
21952     },
21953     
21954     getActivePane : function()
21955     {
21956         var r = false;
21957         Roo.each(this.panes, function(p) {
21958             if(p.el.hasClass('active')){
21959                 r = p;
21960                 return false;
21961             }
21962             
21963             return;
21964         });
21965         
21966         return r;
21967     }
21968     
21969     
21970 });
21971
21972  
21973 /*
21974  * - LGPL
21975  *
21976  * Tab pane
21977  * 
21978  */
21979 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21980 /**
21981  * @class Roo.bootstrap.TabPane
21982  * @extends Roo.bootstrap.Component
21983  * Bootstrap TabPane class
21984  * @cfg {Boolean} active (false | true) Default false
21985  * @cfg {String} title title of panel
21986
21987  * 
21988  * @constructor
21989  * Create a new TabPane
21990  * @param {Object} config The config object
21991  */
21992
21993 Roo.bootstrap.dash.TabPane = function(config){
21994     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
21995     
21996     this.addEvents({
21997         // raw events
21998         /**
21999          * @event activate
22000          * When a pane is activated
22001          * @param {Roo.bootstrap.dash.TabPane} pane
22002          */
22003         "activate" : true
22004          
22005     });
22006 };
22007
22008 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
22009     
22010     active : false,
22011     title : '',
22012     
22013     // the tabBox that this is attached to.
22014     tab : false,
22015      
22016     getAutoCreate : function() 
22017     {
22018         var cfg = {
22019             tag: 'div',
22020             cls: 'tab-pane'
22021         }
22022         
22023         if(this.active){
22024             cfg.cls += ' active';
22025         }
22026         
22027         return cfg;
22028     },
22029     initEvents  : function()
22030     {
22031         //Roo.log('trigger add pane handler');
22032         this.parent().fireEvent('addpane', this)
22033     },
22034     
22035      /**
22036      * Updates the tab title 
22037      * @param {String} html to set the title to.
22038      */
22039     setTitle: function(str)
22040     {
22041         if (!this.tab) {
22042             return;
22043         }
22044         this.title = str;
22045         this.tab.select('a', true).first().dom.innerHTML = str;
22046         
22047     }
22048     
22049     
22050     
22051 });
22052
22053  
22054
22055
22056  /*
22057  * - LGPL
22058  *
22059  * menu
22060  * 
22061  */
22062 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22063
22064 /**
22065  * @class Roo.bootstrap.menu.Menu
22066  * @extends Roo.bootstrap.Component
22067  * Bootstrap Menu class - container for Menu
22068  * @cfg {String} html Text of the menu
22069  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
22070  * @cfg {String} icon Font awesome icon
22071  * @cfg {String} pos Menu align to (top | bottom) default bottom
22072  * 
22073  * 
22074  * @constructor
22075  * Create a new Menu
22076  * @param {Object} config The config object
22077  */
22078
22079
22080 Roo.bootstrap.menu.Menu = function(config){
22081     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
22082     
22083     this.addEvents({
22084         /**
22085          * @event beforeshow
22086          * Fires before this menu is displayed
22087          * @param {Roo.bootstrap.menu.Menu} this
22088          */
22089         beforeshow : true,
22090         /**
22091          * @event beforehide
22092          * Fires before this menu is hidden
22093          * @param {Roo.bootstrap.menu.Menu} this
22094          */
22095         beforehide : true,
22096         /**
22097          * @event show
22098          * Fires after this menu is displayed
22099          * @param {Roo.bootstrap.menu.Menu} this
22100          */
22101         show : true,
22102         /**
22103          * @event hide
22104          * Fires after this menu is hidden
22105          * @param {Roo.bootstrap.menu.Menu} this
22106          */
22107         hide : true,
22108         /**
22109          * @event click
22110          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
22111          * @param {Roo.bootstrap.menu.Menu} this
22112          * @param {Roo.EventObject} e
22113          */
22114         click : true
22115     });
22116     
22117 };
22118
22119 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
22120     
22121     submenu : false,
22122     html : '',
22123     weight : 'default',
22124     icon : false,
22125     pos : 'bottom',
22126     
22127     
22128     getChildContainer : function() {
22129         if(this.isSubMenu){
22130             return this.el;
22131         }
22132         
22133         return this.el.select('ul.dropdown-menu', true).first();  
22134     },
22135     
22136     getAutoCreate : function()
22137     {
22138         var text = [
22139             {
22140                 tag : 'span',
22141                 cls : 'roo-menu-text',
22142                 html : this.html
22143             }
22144         ];
22145         
22146         if(this.icon){
22147             text.unshift({
22148                 tag : 'i',
22149                 cls : 'fa ' + this.icon
22150             })
22151         }
22152         
22153         
22154         var cfg = {
22155             tag : 'div',
22156             cls : 'btn-group',
22157             cn : [
22158                 {
22159                     tag : 'button',
22160                     cls : 'dropdown-button btn btn-' + this.weight,
22161                     cn : text
22162                 },
22163                 {
22164                     tag : 'button',
22165                     cls : 'dropdown-toggle btn btn-' + this.weight,
22166                     cn : [
22167                         {
22168                             tag : 'span',
22169                             cls : 'caret'
22170                         }
22171                     ]
22172                 },
22173                 {
22174                     tag : 'ul',
22175                     cls : 'dropdown-menu'
22176                 }
22177             ]
22178             
22179         };
22180         
22181         if(this.pos == 'top'){
22182             cfg.cls += ' dropup';
22183         }
22184         
22185         if(this.isSubMenu){
22186             cfg = {
22187                 tag : 'ul',
22188                 cls : 'dropdown-menu'
22189             }
22190         }
22191         
22192         return cfg;
22193     },
22194     
22195     onRender : function(ct, position)
22196     {
22197         this.isSubMenu = ct.hasClass('dropdown-submenu');
22198         
22199         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
22200     },
22201     
22202     initEvents : function() 
22203     {
22204         if(this.isSubMenu){
22205             return;
22206         }
22207         
22208         this.hidden = true;
22209         
22210         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
22211         this.triggerEl.on('click', this.onTriggerPress, this);
22212         
22213         this.buttonEl = this.el.select('button.dropdown-button', true).first();
22214         this.buttonEl.on('click', this.onClick, this);
22215         
22216     },
22217     
22218     list : function()
22219     {
22220         if(this.isSubMenu){
22221             return this.el;
22222         }
22223         
22224         return this.el.select('ul.dropdown-menu', true).first();
22225     },
22226     
22227     onClick : function(e)
22228     {
22229         this.fireEvent("click", this, e);
22230     },
22231     
22232     onTriggerPress  : function(e)
22233     {   
22234         if (this.isVisible()) {
22235             this.hide();
22236         } else {
22237             this.show();
22238         }
22239     },
22240     
22241     isVisible : function(){
22242         return !this.hidden;
22243     },
22244     
22245     show : function()
22246     {
22247         this.fireEvent("beforeshow", this);
22248         
22249         this.hidden = false;
22250         this.el.addClass('open');
22251         
22252         Roo.get(document).on("mouseup", this.onMouseUp, this);
22253         
22254         this.fireEvent("show", this);
22255         
22256         
22257     },
22258     
22259     hide : function()
22260     {
22261         this.fireEvent("beforehide", this);
22262         
22263         this.hidden = true;
22264         this.el.removeClass('open');
22265         
22266         Roo.get(document).un("mouseup", this.onMouseUp);
22267         
22268         this.fireEvent("hide", this);
22269     },
22270     
22271     onMouseUp : function()
22272     {
22273         this.hide();
22274     }
22275     
22276 });
22277
22278  
22279  /*
22280  * - LGPL
22281  *
22282  * menu item
22283  * 
22284  */
22285 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22286
22287 /**
22288  * @class Roo.bootstrap.menu.Item
22289  * @extends Roo.bootstrap.Component
22290  * Bootstrap MenuItem class
22291  * @cfg {Boolean} submenu (true | false) default false
22292  * @cfg {String} html text of the item
22293  * @cfg {String} href the link
22294  * @cfg {Boolean} disable (true | false) default false
22295  * @cfg {Boolean} preventDefault (true | false) default true
22296  * @cfg {String} icon Font awesome icon
22297  * @cfg {String} pos Submenu align to (left | right) default right 
22298  * 
22299  * 
22300  * @constructor
22301  * Create a new Item
22302  * @param {Object} config The config object
22303  */
22304
22305
22306 Roo.bootstrap.menu.Item = function(config){
22307     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
22308     this.addEvents({
22309         /**
22310          * @event mouseover
22311          * Fires when the mouse is hovering over this menu
22312          * @param {Roo.bootstrap.menu.Item} this
22313          * @param {Roo.EventObject} e
22314          */
22315         mouseover : true,
22316         /**
22317          * @event mouseout
22318          * Fires when the mouse exits this menu
22319          * @param {Roo.bootstrap.menu.Item} this
22320          * @param {Roo.EventObject} e
22321          */
22322         mouseout : true,
22323         // raw events
22324         /**
22325          * @event click
22326          * The raw click event for the entire grid.
22327          * @param {Roo.EventObject} e
22328          */
22329         click : true
22330     });
22331 };
22332
22333 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
22334     
22335     submenu : false,
22336     href : '',
22337     html : '',
22338     preventDefault: true,
22339     disable : false,
22340     icon : false,
22341     pos : 'right',
22342     
22343     getAutoCreate : function()
22344     {
22345         var text = [
22346             {
22347                 tag : 'span',
22348                 cls : 'roo-menu-item-text',
22349                 html : this.html
22350             }
22351         ];
22352         
22353         if(this.icon){
22354             text.unshift({
22355                 tag : 'i',
22356                 cls : 'fa ' + this.icon
22357             })
22358         }
22359         
22360         var cfg = {
22361             tag : 'li',
22362             cn : [
22363                 {
22364                     tag : 'a',
22365                     href : this.href || '#',
22366                     cn : text
22367                 }
22368             ]
22369         };
22370         
22371         if(this.disable){
22372             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
22373         }
22374         
22375         if(this.submenu){
22376             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
22377             
22378             if(this.pos == 'left'){
22379                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
22380             }
22381         }
22382         
22383         return cfg;
22384     },
22385     
22386     initEvents : function() 
22387     {
22388         this.el.on('mouseover', this.onMouseOver, this);
22389         this.el.on('mouseout', this.onMouseOut, this);
22390         
22391         this.el.select('a', true).first().on('click', this.onClick, this);
22392         
22393     },
22394     
22395     onClick : function(e)
22396     {
22397         if(this.preventDefault){
22398             e.preventDefault();
22399         }
22400         
22401         this.fireEvent("click", this, e);
22402     },
22403     
22404     onMouseOver : function(e)
22405     {
22406         if(this.submenu && this.pos == 'left'){
22407             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
22408         }
22409         
22410         this.fireEvent("mouseover", this, e);
22411     },
22412     
22413     onMouseOut : function(e)
22414     {
22415         this.fireEvent("mouseout", this, e);
22416     }
22417 });
22418
22419  
22420
22421  /*
22422  * - LGPL
22423  *
22424  * menu separator
22425  * 
22426  */
22427 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22428
22429 /**
22430  * @class Roo.bootstrap.menu.Separator
22431  * @extends Roo.bootstrap.Component
22432  * Bootstrap Separator class
22433  * 
22434  * @constructor
22435  * Create a new Separator
22436  * @param {Object} config The config object
22437  */
22438
22439
22440 Roo.bootstrap.menu.Separator = function(config){
22441     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
22442 };
22443
22444 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
22445     
22446     getAutoCreate : function(){
22447         var cfg = {
22448             tag : 'li',
22449             cls: 'divider'
22450         };
22451         
22452         return cfg;
22453     }
22454    
22455 });
22456
22457  
22458
22459  /*
22460  * - LGPL
22461  *
22462  * Tooltip
22463  * 
22464  */
22465
22466 /**
22467  * @class Roo.bootstrap.Tooltip
22468  * Bootstrap Tooltip class
22469  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
22470  * to determine which dom element triggers the tooltip.
22471  * 
22472  * It needs to add support for additional attributes like tooltip-position
22473  * 
22474  * @constructor
22475  * Create a new Toolti
22476  * @param {Object} config The config object
22477  */
22478
22479 Roo.bootstrap.Tooltip = function(config){
22480     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
22481 };
22482
22483 Roo.apply(Roo.bootstrap.Tooltip, {
22484     /**
22485      * @function init initialize tooltip monitoring.
22486      * @static
22487      */
22488     currentEl : false,
22489     currentTip : false,
22490     currentRegion : false,
22491     
22492     //  init : delay?
22493     
22494     init : function()
22495     {
22496         Roo.get(document).on('mouseover', this.enter ,this);
22497         Roo.get(document).on('mouseout', this.leave, this);
22498          
22499         
22500         this.currentTip = new Roo.bootstrap.Tooltip();
22501     },
22502     
22503     enter : function(ev)
22504     {
22505         var dom = ev.getTarget();
22506         
22507         //Roo.log(['enter',dom]);
22508         var el = Roo.fly(dom);
22509         if (this.currentEl) {
22510             //Roo.log(dom);
22511             //Roo.log(this.currentEl);
22512             //Roo.log(this.currentEl.contains(dom));
22513             if (this.currentEl == el) {
22514                 return;
22515             }
22516             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
22517                 return;
22518             }
22519
22520         }
22521         
22522         
22523         
22524         if (this.currentTip.el) {
22525             this.currentTip.el.hide(); // force hiding...
22526         }    
22527         //Roo.log(ev);
22528         var bindEl = el;
22529         
22530         // you can not look for children, as if el is the body.. then everythign is the child..
22531         if (!el.attr('tooltip')) { //
22532             if (!el.select("[tooltip]").elements.length) {
22533                 return;
22534             }
22535             // is the mouse over this child...?
22536             bindEl = el.select("[tooltip]").first();
22537             var xy = ev.getXY();
22538             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
22539                 //Roo.log("not in region.");
22540                 return;
22541             }
22542             //Roo.log("child element over..");
22543             
22544         }
22545         this.currentEl = bindEl;
22546         this.currentTip.bind(bindEl);
22547         this.currentRegion = Roo.lib.Region.getRegion(dom);
22548         this.currentTip.enter();
22549         
22550     },
22551     leave : function(ev)
22552     {
22553         var dom = ev.getTarget();
22554         //Roo.log(['leave',dom]);
22555         if (!this.currentEl) {
22556             return;
22557         }
22558         
22559         
22560         if (dom != this.currentEl.dom) {
22561             return;
22562         }
22563         var xy = ev.getXY();
22564         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
22565             return;
22566         }
22567         // only activate leave if mouse cursor is outside... bounding box..
22568         
22569         
22570         
22571         
22572         if (this.currentTip) {
22573             this.currentTip.leave();
22574         }
22575         //Roo.log('clear currentEl');
22576         this.currentEl = false;
22577         
22578         
22579     },
22580     alignment : {
22581         'left' : ['r-l', [-2,0], 'right'],
22582         'right' : ['l-r', [2,0], 'left'],
22583         'bottom' : ['t-b', [0,2], 'top'],
22584         'top' : [ 'b-t', [0,-2], 'bottom']
22585     }
22586     
22587 });
22588
22589
22590 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
22591     
22592     
22593     bindEl : false,
22594     
22595     delay : null, // can be { show : 300 , hide: 500}
22596     
22597     timeout : null,
22598     
22599     hoverState : null, //???
22600     
22601     placement : 'bottom', 
22602     
22603     getAutoCreate : function(){
22604     
22605         var cfg = {
22606            cls : 'tooltip',
22607            role : 'tooltip',
22608            cn : [
22609                 {
22610                     cls : 'tooltip-arrow'
22611                 },
22612                 {
22613                     cls : 'tooltip-inner'
22614                 }
22615            ]
22616         };
22617         
22618         return cfg;
22619     },
22620     bind : function(el)
22621     {
22622         this.bindEl = el;
22623     },
22624       
22625     
22626     enter : function () {
22627        
22628         if (this.timeout != null) {
22629             clearTimeout(this.timeout);
22630         }
22631         
22632         this.hoverState = 'in';
22633          //Roo.log("enter - show");
22634         if (!this.delay || !this.delay.show) {
22635             this.show();
22636             return;
22637         }
22638         var _t = this;
22639         this.timeout = setTimeout(function () {
22640             if (_t.hoverState == 'in') {
22641                 _t.show();
22642             }
22643         }, this.delay.show);
22644     },
22645     leave : function()
22646     {
22647         clearTimeout(this.timeout);
22648     
22649         this.hoverState = 'out';
22650          if (!this.delay || !this.delay.hide) {
22651             this.hide();
22652             return;
22653         }
22654        
22655         var _t = this;
22656         this.timeout = setTimeout(function () {
22657             //Roo.log("leave - timeout");
22658             
22659             if (_t.hoverState == 'out') {
22660                 _t.hide();
22661                 Roo.bootstrap.Tooltip.currentEl = false;
22662             }
22663         }, delay);
22664     },
22665     
22666     show : function ()
22667     {
22668         if (!this.el) {
22669             this.render(document.body);
22670         }
22671         // set content.
22672         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
22673         
22674         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
22675         
22676         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
22677         
22678         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
22679         
22680         var placement = typeof this.placement == 'function' ?
22681             this.placement.call(this, this.el, on_el) :
22682             this.placement;
22683             
22684         var autoToken = /\s?auto?\s?/i;
22685         var autoPlace = autoToken.test(placement);
22686         if (autoPlace) {
22687             placement = placement.replace(autoToken, '') || 'top';
22688         }
22689         
22690         //this.el.detach()
22691         //this.el.setXY([0,0]);
22692         this.el.show();
22693         //this.el.dom.style.display='block';
22694         this.el.addClass(placement);
22695         
22696         //this.el.appendTo(on_el);
22697         
22698         var p = this.getPosition();
22699         var box = this.el.getBox();
22700         
22701         if (autoPlace) {
22702             // fixme..
22703         }
22704         var align = Roo.bootstrap.Tooltip.alignment[placement];
22705         this.el.alignTo(this.bindEl, align[0],align[1]);
22706         //var arrow = this.el.select('.arrow',true).first();
22707         //arrow.set(align[2], 
22708         
22709         this.el.addClass('in fade');
22710         this.hoverState = null;
22711         
22712         if (this.el.hasClass('fade')) {
22713             // fade it?
22714         }
22715         
22716     },
22717     hide : function()
22718     {
22719          
22720         if (!this.el) {
22721             return;
22722         }
22723         //this.el.setXY([0,0]);
22724         this.el.removeClass('in');
22725         //this.el.hide();
22726         
22727     }
22728     
22729 });
22730  
22731
22732  /*
22733  * - LGPL
22734  *
22735  * Location Picker
22736  * 
22737  */
22738
22739 /**
22740  * @class Roo.bootstrap.LocationPicker
22741  * @extends Roo.bootstrap.Component
22742  * Bootstrap LocationPicker class
22743  * @cfg {Number} latitude Position when init default 0
22744  * @cfg {Number} longitude Position when init default 0
22745  * @cfg {Number} zoom default 15
22746  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
22747  * @cfg {Boolean} mapTypeControl default false
22748  * @cfg {Boolean} disableDoubleClickZoom default false
22749  * @cfg {Boolean} scrollwheel default true
22750  * @cfg {Boolean} streetViewControl default false
22751  * @cfg {Number} radius default 0
22752  * @cfg {String} locationName
22753  * @cfg {Boolean} draggable default true
22754  * @cfg {Boolean} enableAutocomplete default false
22755  * @cfg {Boolean} enableReverseGeocode default true
22756  * @cfg {String} markerTitle
22757  * 
22758  * @constructor
22759  * Create a new LocationPicker
22760  * @param {Object} config The config object
22761  */
22762
22763
22764 Roo.bootstrap.LocationPicker = function(config){
22765     
22766     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
22767     
22768     this.addEvents({
22769         /**
22770          * @event initial
22771          * Fires when the picker initialized.
22772          * @param {Roo.bootstrap.LocationPicker} this
22773          * @param {Google Location} location
22774          */
22775         initial : true,
22776         /**
22777          * @event positionchanged
22778          * Fires when the picker position changed.
22779          * @param {Roo.bootstrap.LocationPicker} this
22780          * @param {Google Location} location
22781          */
22782         positionchanged : true,
22783         /**
22784          * @event resize
22785          * Fires when the map resize.
22786          * @param {Roo.bootstrap.LocationPicker} this
22787          */
22788         resize : true,
22789         /**
22790          * @event show
22791          * Fires when the map show.
22792          * @param {Roo.bootstrap.LocationPicker} this
22793          */
22794         show : true,
22795         /**
22796          * @event hide
22797          * Fires when the map hide.
22798          * @param {Roo.bootstrap.LocationPicker} this
22799          */
22800         hide : true,
22801         /**
22802          * @event mapClick
22803          * Fires when click the map.
22804          * @param {Roo.bootstrap.LocationPicker} this
22805          * @param {Map event} e
22806          */
22807         mapClick : true,
22808         /**
22809          * @event mapRightClick
22810          * Fires when right click the map.
22811          * @param {Roo.bootstrap.LocationPicker} this
22812          * @param {Map event} e
22813          */
22814         mapRightClick : true,
22815         /**
22816          * @event markerClick
22817          * Fires when click the marker.
22818          * @param {Roo.bootstrap.LocationPicker} this
22819          * @param {Map event} e
22820          */
22821         markerClick : true,
22822         /**
22823          * @event markerRightClick
22824          * Fires when right click the marker.
22825          * @param {Roo.bootstrap.LocationPicker} this
22826          * @param {Map event} e
22827          */
22828         markerRightClick : true,
22829         /**
22830          * @event OverlayViewDraw
22831          * Fires when OverlayView Draw
22832          * @param {Roo.bootstrap.LocationPicker} this
22833          */
22834         OverlayViewDraw : true,
22835         /**
22836          * @event OverlayViewOnAdd
22837          * Fires when OverlayView Draw
22838          * @param {Roo.bootstrap.LocationPicker} this
22839          */
22840         OverlayViewOnAdd : true,
22841         /**
22842          * @event OverlayViewOnRemove
22843          * Fires when OverlayView Draw
22844          * @param {Roo.bootstrap.LocationPicker} this
22845          */
22846         OverlayViewOnRemove : true,
22847         /**
22848          * @event OverlayViewShow
22849          * Fires when OverlayView Draw
22850          * @param {Roo.bootstrap.LocationPicker} this
22851          * @param {Pixel} cpx
22852          */
22853         OverlayViewShow : true,
22854         /**
22855          * @event OverlayViewHide
22856          * Fires when OverlayView Draw
22857          * @param {Roo.bootstrap.LocationPicker} this
22858          */
22859         OverlayViewHide : true
22860     });
22861         
22862 };
22863
22864 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
22865     
22866     gMapContext: false,
22867     
22868     latitude: 0,
22869     longitude: 0,
22870     zoom: 15,
22871     mapTypeId: false,
22872     mapTypeControl: false,
22873     disableDoubleClickZoom: false,
22874     scrollwheel: true,
22875     streetViewControl: false,
22876     radius: 0,
22877     locationName: '',
22878     draggable: true,
22879     enableAutocomplete: false,
22880     enableReverseGeocode: true,
22881     markerTitle: '',
22882     
22883     getAutoCreate: function()
22884     {
22885
22886         var cfg = {
22887             tag: 'div',
22888             cls: 'roo-location-picker'
22889         };
22890         
22891         return cfg
22892     },
22893     
22894     initEvents: function(ct, position)
22895     {       
22896         if(!this.el.getWidth() || this.isApplied()){
22897             return;
22898         }
22899         
22900         this.el.setVisibilityMode(Roo.Element.DISPLAY);
22901         
22902         this.initial();
22903     },
22904     
22905     initial: function()
22906     {
22907         if(!this.mapTypeId){
22908             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
22909         }
22910         
22911         this.gMapContext = this.GMapContext();
22912         
22913         this.initOverlayView();
22914         
22915         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
22916         
22917         var _this = this;
22918                 
22919         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
22920             _this.setPosition(_this.gMapContext.marker.position);
22921         });
22922         
22923         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
22924             _this.fireEvent('mapClick', this, event);
22925             
22926         });
22927
22928         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
22929             _this.fireEvent('mapRightClick', this, event);
22930             
22931         });
22932         
22933         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
22934             _this.fireEvent('markerClick', this, event);
22935             
22936         });
22937
22938         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
22939             _this.fireEvent('markerRightClick', this, event);
22940             
22941         });
22942         
22943         this.setPosition(this.gMapContext.location);
22944         
22945         this.fireEvent('initial', this, this.gMapContext.location);
22946     },
22947     
22948     initOverlayView: function()
22949     {
22950         var _this = this;
22951         
22952         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
22953             
22954             draw: function()
22955             {
22956                 _this.fireEvent('OverlayViewDraw', _this);
22957             },
22958             
22959             onAdd: function()
22960             {
22961                 _this.fireEvent('OverlayViewOnAdd', _this);
22962             },
22963             
22964             onRemove: function()
22965             {
22966                 _this.fireEvent('OverlayViewOnRemove', _this);
22967             },
22968             
22969             show: function(cpx)
22970             {
22971                 _this.fireEvent('OverlayViewShow', _this, cpx);
22972             },
22973             
22974             hide: function()
22975             {
22976                 _this.fireEvent('OverlayViewHide', _this);
22977             }
22978             
22979         });
22980     },
22981     
22982     fromLatLngToContainerPixel: function(event)
22983     {
22984         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
22985     },
22986     
22987     isApplied: function() 
22988     {
22989         return this.getGmapContext() == false ? false : true;
22990     },
22991     
22992     getGmapContext: function() 
22993     {
22994         return this.gMapContext
22995     },
22996     
22997     GMapContext: function() 
22998     {
22999         var position = new google.maps.LatLng(this.latitude, this.longitude);
23000         
23001         var _map = new google.maps.Map(this.el.dom, {
23002             center: position,
23003             zoom: this.zoom,
23004             mapTypeId: this.mapTypeId,
23005             mapTypeControl: this.mapTypeControl,
23006             disableDoubleClickZoom: this.disableDoubleClickZoom,
23007             scrollwheel: this.scrollwheel,
23008             streetViewControl: this.streetViewControl,
23009             locationName: this.locationName,
23010             draggable: this.draggable,
23011             enableAutocomplete: this.enableAutocomplete,
23012             enableReverseGeocode: this.enableReverseGeocode
23013         });
23014         
23015         var _marker = new google.maps.Marker({
23016             position: position,
23017             map: _map,
23018             title: this.markerTitle,
23019             draggable: this.draggable
23020         });
23021         
23022         return {
23023             map: _map,
23024             marker: _marker,
23025             circle: null,
23026             location: position,
23027             radius: this.radius,
23028             locationName: this.locationName,
23029             addressComponents: {
23030                 formatted_address: null,
23031                 addressLine1: null,
23032                 addressLine2: null,
23033                 streetName: null,
23034                 streetNumber: null,
23035                 city: null,
23036                 district: null,
23037                 state: null,
23038                 stateOrProvince: null
23039             },
23040             settings: this,
23041             domContainer: this.el.dom,
23042             geodecoder: new google.maps.Geocoder()
23043         };
23044     },
23045     
23046     drawCircle: function(center, radius, options) 
23047     {
23048         if (this.gMapContext.circle != null) {
23049             this.gMapContext.circle.setMap(null);
23050         }
23051         if (radius > 0) {
23052             radius *= 1;
23053             options = Roo.apply({}, options, {
23054                 strokeColor: "#0000FF",
23055                 strokeOpacity: .35,
23056                 strokeWeight: 2,
23057                 fillColor: "#0000FF",
23058                 fillOpacity: .2
23059             });
23060             
23061             options.map = this.gMapContext.map;
23062             options.radius = radius;
23063             options.center = center;
23064             this.gMapContext.circle = new google.maps.Circle(options);
23065             return this.gMapContext.circle;
23066         }
23067         
23068         return null;
23069     },
23070     
23071     setPosition: function(location) 
23072     {
23073         this.gMapContext.location = location;
23074         this.gMapContext.marker.setPosition(location);
23075         this.gMapContext.map.panTo(location);
23076         this.drawCircle(location, this.gMapContext.radius, {});
23077         
23078         var _this = this;
23079         
23080         if (this.gMapContext.settings.enableReverseGeocode) {
23081             this.gMapContext.geodecoder.geocode({
23082                 latLng: this.gMapContext.location
23083             }, function(results, status) {
23084                 
23085                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
23086                     _this.gMapContext.locationName = results[0].formatted_address;
23087                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
23088                     
23089                     _this.fireEvent('positionchanged', this, location);
23090                 }
23091             });
23092             
23093             return;
23094         }
23095         
23096         this.fireEvent('positionchanged', this, location);
23097     },
23098     
23099     resize: function()
23100     {
23101         google.maps.event.trigger(this.gMapContext.map, "resize");
23102         
23103         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
23104         
23105         this.fireEvent('resize', this);
23106     },
23107     
23108     setPositionByLatLng: function(latitude, longitude)
23109     {
23110         this.setPosition(new google.maps.LatLng(latitude, longitude));
23111     },
23112     
23113     getCurrentPosition: function() 
23114     {
23115         return {
23116             latitude: this.gMapContext.location.lat(),
23117             longitude: this.gMapContext.location.lng()
23118         };
23119     },
23120     
23121     getAddressName: function() 
23122     {
23123         return this.gMapContext.locationName;
23124     },
23125     
23126     getAddressComponents: function() 
23127     {
23128         return this.gMapContext.addressComponents;
23129     },
23130     
23131     address_component_from_google_geocode: function(address_components) 
23132     {
23133         var result = {};
23134         
23135         for (var i = 0; i < address_components.length; i++) {
23136             var component = address_components[i];
23137             if (component.types.indexOf("postal_code") >= 0) {
23138                 result.postalCode = component.short_name;
23139             } else if (component.types.indexOf("street_number") >= 0) {
23140                 result.streetNumber = component.short_name;
23141             } else if (component.types.indexOf("route") >= 0) {
23142                 result.streetName = component.short_name;
23143             } else if (component.types.indexOf("neighborhood") >= 0) {
23144                 result.city = component.short_name;
23145             } else if (component.types.indexOf("locality") >= 0) {
23146                 result.city = component.short_name;
23147             } else if (component.types.indexOf("sublocality") >= 0) {
23148                 result.district = component.short_name;
23149             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
23150                 result.stateOrProvince = component.short_name;
23151             } else if (component.types.indexOf("country") >= 0) {
23152                 result.country = component.short_name;
23153             }
23154         }
23155         
23156         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
23157         result.addressLine2 = "";
23158         return result;
23159     },
23160     
23161     setZoomLevel: function(zoom)
23162     {
23163         this.gMapContext.map.setZoom(zoom);
23164     },
23165     
23166     show: function()
23167     {
23168         if(!this.el){
23169             return;
23170         }
23171         
23172         this.el.show();
23173         
23174         this.resize();
23175         
23176         this.fireEvent('show', this);
23177     },
23178     
23179     hide: function()
23180     {
23181         if(!this.el){
23182             return;
23183         }
23184         
23185         this.el.hide();
23186         
23187         this.fireEvent('hide', this);
23188     }
23189     
23190 });
23191
23192 Roo.apply(Roo.bootstrap.LocationPicker, {
23193     
23194     OverlayView : function(map, options)
23195     {
23196         options = options || {};
23197         
23198         this.setMap(map);
23199     }
23200     
23201     
23202 });/*
23203  * - LGPL
23204  *
23205  * Alert
23206  * 
23207  */
23208
23209 /**
23210  * @class Roo.bootstrap.Alert
23211  * @extends Roo.bootstrap.Component
23212  * Bootstrap Alert class
23213  * @cfg {String} title The title of alert
23214  * @cfg {String} html The content of alert
23215  * @cfg {String} weight (  success | info | warning | danger )
23216  * @cfg {String} faicon font-awesomeicon
23217  * 
23218  * @constructor
23219  * Create a new alert
23220  * @param {Object} config The config object
23221  */
23222
23223
23224 Roo.bootstrap.Alert = function(config){
23225     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
23226     
23227 };
23228
23229 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
23230     
23231     title: '',
23232     html: '',
23233     weight: false,
23234     faicon: false,
23235     
23236     getAutoCreate : function()
23237     {
23238         
23239         var cfg = {
23240             tag : 'div',
23241             cls : 'alert',
23242             cn : [
23243                 {
23244                     tag : 'i',
23245                     cls : 'roo-alert-icon'
23246                     
23247                 },
23248                 {
23249                     tag : 'b',
23250                     cls : 'roo-alert-title',
23251                     html : this.title
23252                 },
23253                 {
23254                     tag : 'span',
23255                     cls : 'roo-alert-text',
23256                     html : this.html
23257                 }
23258             ]
23259         };
23260         
23261         if(this.faicon){
23262             cfg.cn[0].cls += ' fa ' + this.faicon;
23263         }
23264         
23265         if(this.weight){
23266             cfg.cls += ' alert-' + this.weight;
23267         }
23268         
23269         return cfg;
23270     },
23271     
23272     initEvents: function() 
23273     {
23274         this.el.setVisibilityMode(Roo.Element.DISPLAY);
23275     },
23276     
23277     setTitle : function(str)
23278     {
23279         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
23280     },
23281     
23282     setText : function(str)
23283     {
23284         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
23285     },
23286     
23287     setWeight : function(weight)
23288     {
23289         if(this.weight){
23290             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
23291         }
23292         
23293         this.weight = weight;
23294         
23295         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
23296     },
23297     
23298     setIcon : function(icon)
23299     {
23300         if(this.faicon){
23301             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
23302         }
23303         
23304         this.faicon = icon
23305         
23306         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
23307     },
23308     
23309     hide: function() 
23310     {
23311         this.el.hide();   
23312     },
23313     
23314     show: function() 
23315     {  
23316         this.el.show();   
23317     }
23318     
23319 });
23320
23321