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             Roo.each(s.splice(s.indexOf(size), 1), function(ss){
1396                 img.cls += ' hidden-' + ss;
1397             });
1398             
1399             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1400                 cfg.cls += ' img-' + _this.border;
1401             }
1402             
1403             if(_this.alt){
1404                 cfg.alt = _this.alt;
1405             }
1406             
1407             if(_this.href){
1408                 var a = {
1409                     tag: 'a',
1410                     href: _this.href,
1411                     cn: [
1412                         img
1413                     ]
1414                 }
1415
1416                 if(this.target){
1417                     a.target = _this.target;
1418                 }
1419             }
1420             
1421             cfg.cn.push((_this.href) ? a : img);
1422             
1423         });
1424         
1425         return cfg;
1426     },
1427     
1428     createSingleImg : function()
1429     {
1430         var cfg = {
1431             tag: 'img',
1432             cls: (this.imgResponsive) ? 'img-responsive' : '',
1433             html : null
1434         }
1435         
1436         cfg.html = this.html || cfg.html;
1437         
1438         cfg.src = this.src || cfg.src;
1439         
1440         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1441             cfg.cls += ' img-' + this.border;
1442         }
1443         
1444         if(this.alt){
1445             cfg.alt = this.alt;
1446         }
1447         
1448         if(this.href){
1449             var a = {
1450                 tag: 'a',
1451                 href: this.href,
1452                 cn: [
1453                     cfg
1454                 ]
1455             }
1456             
1457             if(this.target){
1458                 a.target = this.target;
1459             }
1460             
1461         }
1462         
1463         return (this.href) ? a : cfg;
1464     },
1465     
1466     initEvents: function() 
1467     {
1468         if(!this.href){
1469             this.el.on('click', this.onClick, this);
1470         }
1471         
1472     },
1473     
1474     onClick : function(e)
1475     {
1476         Roo.log('img onclick');
1477         this.fireEvent('click', this, e);
1478     }
1479    
1480 });
1481
1482  /*
1483  * - LGPL
1484  *
1485  * image
1486  * 
1487  */
1488
1489
1490 /**
1491  * @class Roo.bootstrap.Link
1492  * @extends Roo.bootstrap.Component
1493  * Bootstrap Link Class
1494  * @cfg {String} alt image alternative text
1495  * @cfg {String} href a tag href
1496  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1497  * @cfg {String} html the content of the link.
1498  * @cfg {String} anchor name for the anchor link
1499
1500  * @cfg {Boolean} preventDefault (true | false) default false
1501
1502  * 
1503  * @constructor
1504  * Create a new Input
1505  * @param {Object} config The config object
1506  */
1507
1508 Roo.bootstrap.Link = function(config){
1509     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1510     
1511     this.addEvents({
1512         // img events
1513         /**
1514          * @event click
1515          * The img click event for the img.
1516          * @param {Roo.EventObject} e
1517          */
1518         "click" : true
1519     });
1520 };
1521
1522 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1523     
1524     href: false,
1525     target: false,
1526     preventDefault: false,
1527     anchor : false,
1528     alt : false,
1529
1530     getAutoCreate : function()
1531     {
1532         
1533         var cfg = {
1534             tag: 'a'
1535         };
1536         // anchor's do not require html/href...
1537         if (this.anchor === false) {
1538             cfg.html = this.html || '';
1539             cfg.href = this.href || '#';
1540         } else {
1541             cfg.name = this.anchor;
1542             if (this.html !== false) {
1543                 cfg.html = this.html;
1544             }
1545             if (this.href !== false) {
1546                 cfg.href = this.href;
1547             }
1548         }
1549         
1550         if(this.alt !== false){
1551             cfg.alt = this.alt;
1552         }
1553         
1554         
1555         if(this.target !== false) {
1556             cfg.target = this.target;
1557         }
1558         
1559         return cfg;
1560     },
1561     
1562     initEvents: function() {
1563         
1564         if(!this.href || this.preventDefault){
1565             this.el.on('click', this.onClick, this);
1566         }
1567     },
1568     
1569     onClick : function(e)
1570     {
1571         if(this.preventDefault){
1572             e.preventDefault();
1573         }
1574         //Roo.log('img onclick');
1575         this.fireEvent('click', this, e);
1576     }
1577    
1578 });
1579
1580  /*
1581  * - LGPL
1582  *
1583  * header
1584  * 
1585  */
1586
1587 /**
1588  * @class Roo.bootstrap.Header
1589  * @extends Roo.bootstrap.Component
1590  * Bootstrap Header class
1591  * @cfg {String} html content of header
1592  * @cfg {Number} level (1|2|3|4|5|6) default 1
1593  * 
1594  * @constructor
1595  * Create a new Header
1596  * @param {Object} config The config object
1597  */
1598
1599
1600 Roo.bootstrap.Header  = function(config){
1601     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1602 };
1603
1604 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1605     
1606     //href : false,
1607     html : false,
1608     level : 1,
1609     
1610     
1611     
1612     getAutoCreate : function(){
1613         
1614         
1615         
1616         var cfg = {
1617             tag: 'h' + (1 *this.level),
1618             html: this.html || ''
1619         } ;
1620         
1621         return cfg;
1622     }
1623    
1624 });
1625
1626  
1627
1628  /*
1629  * Based on:
1630  * Ext JS Library 1.1.1
1631  * Copyright(c) 2006-2007, Ext JS, LLC.
1632  *
1633  * Originally Released Under LGPL - original licence link has changed is not relivant.
1634  *
1635  * Fork - LGPL
1636  * <script type="text/javascript">
1637  */
1638  
1639 /**
1640  * @class Roo.bootstrap.MenuMgr
1641  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1642  * @singleton
1643  */
1644 Roo.bootstrap.MenuMgr = function(){
1645    var menus, active, groups = {}, attached = false, lastShow = new Date();
1646
1647    // private - called when first menu is created
1648    function init(){
1649        menus = {};
1650        active = new Roo.util.MixedCollection();
1651        Roo.get(document).addKeyListener(27, function(){
1652            if(active.length > 0){
1653                hideAll();
1654            }
1655        });
1656    }
1657
1658    // private
1659    function hideAll(){
1660        if(active && active.length > 0){
1661            var c = active.clone();
1662            c.each(function(m){
1663                m.hide();
1664            });
1665        }
1666    }
1667
1668    // private
1669    function onHide(m){
1670        active.remove(m);
1671        if(active.length < 1){
1672            Roo.get(document).un("mouseup", onMouseDown);
1673             
1674            attached = false;
1675        }
1676    }
1677
1678    // private
1679    function onShow(m){
1680        var last = active.last();
1681        lastShow = new Date();
1682        active.add(m);
1683        if(!attached){
1684           Roo.get(document).on("mouseup", onMouseDown);
1685            
1686            attached = true;
1687        }
1688        if(m.parentMenu){
1689           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1690           m.parentMenu.activeChild = m;
1691        }else if(last && last.isVisible()){
1692           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1693        }
1694    }
1695
1696    // private
1697    function onBeforeHide(m){
1698        if(m.activeChild){
1699            m.activeChild.hide();
1700        }
1701        if(m.autoHideTimer){
1702            clearTimeout(m.autoHideTimer);
1703            delete m.autoHideTimer;
1704        }
1705    }
1706
1707    // private
1708    function onBeforeShow(m){
1709        var pm = m.parentMenu;
1710        if(!pm && !m.allowOtherMenus){
1711            hideAll();
1712        }else if(pm && pm.activeChild && active != m){
1713            pm.activeChild.hide();
1714        }
1715    }
1716
1717    // private this should really trigger on mouseup..
1718    function onMouseDown(e){
1719         Roo.log("on Mouse Up");
1720         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1721             Roo.log("hideAll");
1722             hideAll();
1723             e.stopEvent();
1724         }
1725         
1726         
1727    }
1728
1729    // private
1730    function onBeforeCheck(mi, state){
1731        if(state){
1732            var g = groups[mi.group];
1733            for(var i = 0, l = g.length; i < l; i++){
1734                if(g[i] != mi){
1735                    g[i].setChecked(false);
1736                }
1737            }
1738        }
1739    }
1740
1741    return {
1742
1743        /**
1744         * Hides all menus that are currently visible
1745         */
1746        hideAll : function(){
1747             hideAll();  
1748        },
1749
1750        // private
1751        register : function(menu){
1752            if(!menus){
1753                init();
1754            }
1755            menus[menu.id] = menu;
1756            menu.on("beforehide", onBeforeHide);
1757            menu.on("hide", onHide);
1758            menu.on("beforeshow", onBeforeShow);
1759            menu.on("show", onShow);
1760            var g = menu.group;
1761            if(g && menu.events["checkchange"]){
1762                if(!groups[g]){
1763                    groups[g] = [];
1764                }
1765                groups[g].push(menu);
1766                menu.on("checkchange", onCheck);
1767            }
1768        },
1769
1770         /**
1771          * Returns a {@link Roo.menu.Menu} object
1772          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1773          * be used to generate and return a new Menu instance.
1774          */
1775        get : function(menu){
1776            if(typeof menu == "string"){ // menu id
1777                return menus[menu];
1778            }else if(menu.events){  // menu instance
1779                return menu;
1780            }
1781            /*else if(typeof menu.length == 'number'){ // array of menu items?
1782                return new Roo.bootstrap.Menu({items:menu});
1783            }else{ // otherwise, must be a config
1784                return new Roo.bootstrap.Menu(menu);
1785            }
1786            */
1787            return false;
1788        },
1789
1790        // private
1791        unregister : function(menu){
1792            delete menus[menu.id];
1793            menu.un("beforehide", onBeforeHide);
1794            menu.un("hide", onHide);
1795            menu.un("beforeshow", onBeforeShow);
1796            menu.un("show", onShow);
1797            var g = menu.group;
1798            if(g && menu.events["checkchange"]){
1799                groups[g].remove(menu);
1800                menu.un("checkchange", onCheck);
1801            }
1802        },
1803
1804        // private
1805        registerCheckable : function(menuItem){
1806            var g = menuItem.group;
1807            if(g){
1808                if(!groups[g]){
1809                    groups[g] = [];
1810                }
1811                groups[g].push(menuItem);
1812                menuItem.on("beforecheckchange", onBeforeCheck);
1813            }
1814        },
1815
1816        // private
1817        unregisterCheckable : function(menuItem){
1818            var g = menuItem.group;
1819            if(g){
1820                groups[g].remove(menuItem);
1821                menuItem.un("beforecheckchange", onBeforeCheck);
1822            }
1823        }
1824    };
1825 }();/*
1826  * - LGPL
1827  *
1828  * menu
1829  * 
1830  */
1831
1832 /**
1833  * @class Roo.bootstrap.Menu
1834  * @extends Roo.bootstrap.Component
1835  * Bootstrap Menu class - container for MenuItems
1836  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1837  * 
1838  * @constructor
1839  * Create a new Menu
1840  * @param {Object} config The config object
1841  */
1842
1843
1844 Roo.bootstrap.Menu = function(config){
1845     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1846     if (this.registerMenu) {
1847         Roo.bootstrap.MenuMgr.register(this);
1848     }
1849     this.addEvents({
1850         /**
1851          * @event beforeshow
1852          * Fires before this menu is displayed
1853          * @param {Roo.menu.Menu} this
1854          */
1855         beforeshow : true,
1856         /**
1857          * @event beforehide
1858          * Fires before this menu is hidden
1859          * @param {Roo.menu.Menu} this
1860          */
1861         beforehide : true,
1862         /**
1863          * @event show
1864          * Fires after this menu is displayed
1865          * @param {Roo.menu.Menu} this
1866          */
1867         show : true,
1868         /**
1869          * @event hide
1870          * Fires after this menu is hidden
1871          * @param {Roo.menu.Menu} this
1872          */
1873         hide : true,
1874         /**
1875          * @event click
1876          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1877          * @param {Roo.menu.Menu} this
1878          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1879          * @param {Roo.EventObject} e
1880          */
1881         click : true,
1882         /**
1883          * @event mouseover
1884          * Fires when the mouse is hovering over this menu
1885          * @param {Roo.menu.Menu} this
1886          * @param {Roo.EventObject} e
1887          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1888          */
1889         mouseover : true,
1890         /**
1891          * @event mouseout
1892          * Fires when the mouse exits this menu
1893          * @param {Roo.menu.Menu} this
1894          * @param {Roo.EventObject} e
1895          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1896          */
1897         mouseout : true,
1898         /**
1899          * @event itemclick
1900          * Fires when a menu item contained in this menu is clicked
1901          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1902          * @param {Roo.EventObject} e
1903          */
1904         itemclick: true
1905     });
1906     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1907 };
1908
1909 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1910     
1911    /// html : false,
1912     //align : '',
1913     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1914     type: false,
1915     /**
1916      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1917      */
1918     registerMenu : true,
1919     
1920     menuItems :false, // stores the menu items..
1921     
1922     hidden:true,
1923     
1924     parentMenu : false,
1925     
1926     getChildContainer : function() {
1927         return this.el;  
1928     },
1929     
1930     getAutoCreate : function(){
1931          
1932         //if (['right'].indexOf(this.align)!==-1) {
1933         //    cfg.cn[1].cls += ' pull-right'
1934         //}
1935         
1936         
1937         var cfg = {
1938             tag : 'ul',
1939             cls : 'dropdown-menu' ,
1940             style : 'z-index:1000'
1941             
1942         }
1943         
1944         if (this.type === 'submenu') {
1945             cfg.cls = 'submenu active';
1946         }
1947         if (this.type === 'treeview') {
1948             cfg.cls = 'treeview-menu';
1949         }
1950         
1951         return cfg;
1952     },
1953     initEvents : function() {
1954         
1955        // Roo.log("ADD event");
1956        // Roo.log(this.triggerEl.dom);
1957         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
1958         
1959         this.triggerEl.addClass('dropdown-toggle');
1960         this.el.on(Roo.isTouch ? 'touchstart' : 'click' , this.onClick, this);
1961
1962         this.el.on("mouseover", this.onMouseOver, this);
1963         this.el.on("mouseout", this.onMouseOut, this);
1964         
1965         
1966     },
1967     findTargetItem : function(e){
1968         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1969         if(!t){
1970             return false;
1971         }
1972         //Roo.log(t);         Roo.log(t.id);
1973         if(t && t.id){
1974             //Roo.log(this.menuitems);
1975             return this.menuitems.get(t.id);
1976             
1977             //return this.items.get(t.menuItemId);
1978         }
1979         
1980         return false;
1981     },
1982     onClick : function(e){
1983         Roo.log("menu.onClick");
1984         var t = this.findTargetItem(e);
1985         if(!t || t.isContainer){
1986             return;
1987         }
1988         Roo.log(e);
1989         /*
1990         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1991             if(t == this.activeItem && t.shouldDeactivate(e)){
1992                 this.activeItem.deactivate();
1993                 delete this.activeItem;
1994                 return;
1995             }
1996             if(t.canActivate){
1997                 this.setActiveItem(t, true);
1998             }
1999             return;
2000             
2001             
2002         }
2003         */
2004        
2005         Roo.log('pass click event');
2006         
2007         t.onClick(e);
2008         
2009         this.fireEvent("click", this, t, e);
2010         
2011         this.hide();
2012     },
2013      onMouseOver : function(e){
2014         var t  = this.findTargetItem(e);
2015         //Roo.log(t);
2016         //if(t){
2017         //    if(t.canActivate && !t.disabled){
2018         //        this.setActiveItem(t, true);
2019         //    }
2020         //}
2021         
2022         this.fireEvent("mouseover", this, e, t);
2023     },
2024     isVisible : function(){
2025         return !this.hidden;
2026     },
2027      onMouseOut : function(e){
2028         var t  = this.findTargetItem(e);
2029         
2030         //if(t ){
2031         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2032         //        this.activeItem.deactivate();
2033         //        delete this.activeItem;
2034         //    }
2035         //}
2036         this.fireEvent("mouseout", this, e, t);
2037     },
2038     
2039     
2040     /**
2041      * Displays this menu relative to another element
2042      * @param {String/HTMLElement/Roo.Element} element The element to align to
2043      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2044      * the element (defaults to this.defaultAlign)
2045      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2046      */
2047     show : function(el, pos, parentMenu){
2048         this.parentMenu = parentMenu;
2049         if(!this.el){
2050             this.render();
2051         }
2052         this.fireEvent("beforeshow", this);
2053         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2054     },
2055      /**
2056      * Displays this menu at a specific xy position
2057      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2058      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2059      */
2060     showAt : function(xy, parentMenu, /* private: */_e){
2061         this.parentMenu = parentMenu;
2062         if(!this.el){
2063             this.render();
2064         }
2065         if(_e !== false){
2066             this.fireEvent("beforeshow", this);
2067             //xy = this.el.adjustForConstraints(xy);
2068         }
2069         
2070         //this.el.show();
2071         this.hideMenuItems();
2072         this.hidden = false;
2073         this.triggerEl.addClass('open');
2074         
2075         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2076             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2077         }
2078         
2079         this.el.setXY(xy);
2080         this.focus();
2081         this.fireEvent("show", this);
2082     },
2083     
2084     focus : function(){
2085         return;
2086         if(!this.hidden){
2087             this.doFocus.defer(50, this);
2088         }
2089     },
2090
2091     doFocus : function(){
2092         if(!this.hidden){
2093             this.focusEl.focus();
2094         }
2095     },
2096
2097     /**
2098      * Hides this menu and optionally all parent menus
2099      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2100      */
2101     hide : function(deep){
2102         
2103         this.hideMenuItems();
2104         if(this.el && this.isVisible()){
2105             this.fireEvent("beforehide", this);
2106             if(this.activeItem){
2107                 this.activeItem.deactivate();
2108                 this.activeItem = null;
2109             }
2110             this.triggerEl.removeClass('open');;
2111             this.hidden = true;
2112             this.fireEvent("hide", this);
2113         }
2114         if(deep === true && this.parentMenu){
2115             this.parentMenu.hide(true);
2116         }
2117     },
2118     
2119     onTriggerPress  : function(e)
2120     {
2121         
2122         Roo.log('trigger press');
2123         //Roo.log(e.getTarget());
2124        // Roo.log(this.triggerEl.dom);
2125         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
2126             return;
2127         }
2128         
2129         if (this.isVisible()) {
2130             Roo.log('hide');
2131             this.hide();
2132         } else {
2133             Roo.log('show');
2134             this.show(this.triggerEl, false, false);
2135         }
2136         
2137         e.stopEvent();
2138     },
2139     
2140          
2141        
2142     
2143     hideMenuItems : function()
2144     {
2145         //$(backdrop).remove()
2146         Roo.select('.open',true).each(function(aa) {
2147             
2148             aa.removeClass('open');
2149           //var parent = getParent($(this))
2150           //var relatedTarget = { relatedTarget: this }
2151           
2152            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2153           //if (e.isDefaultPrevented()) return
2154            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2155         })
2156     },
2157     addxtypeChild : function (tree, cntr) {
2158         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2159           
2160         this.menuitems.add(comp);
2161         return comp;
2162
2163     },
2164     getEl : function()
2165     {
2166         Roo.log(this.el);
2167         return this.el;
2168     }
2169 });
2170
2171  
2172  /*
2173  * - LGPL
2174  *
2175  * menu item
2176  * 
2177  */
2178
2179
2180 /**
2181  * @class Roo.bootstrap.MenuItem
2182  * @extends Roo.bootstrap.Component
2183  * Bootstrap MenuItem class
2184  * @cfg {String} html the menu label
2185  * @cfg {String} href the link
2186  * @cfg {Boolean} preventDefault (true | false) default true
2187  * @cfg {Boolean} isContainer (true | false) default false
2188  * 
2189  * 
2190  * @constructor
2191  * Create a new MenuItem
2192  * @param {Object} config The config object
2193  */
2194
2195
2196 Roo.bootstrap.MenuItem = function(config){
2197     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2198     this.addEvents({
2199         // raw events
2200         /**
2201          * @event click
2202          * The raw click event for the entire grid.
2203          * @param {Roo.bootstrap.MenuItem} this
2204          * @param {Roo.EventObject} e
2205          */
2206         "click" : true
2207     });
2208 };
2209
2210 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2211     
2212     href : false,
2213     html : false,
2214     preventDefault: true,
2215     isContainer : false,
2216     
2217     getAutoCreate : function(){
2218         
2219         if(this.isContainer){
2220             return {
2221                 tag: 'li',
2222                 cls: 'dropdown-menu-item'
2223             };
2224         }
2225         
2226         var cfg= {
2227             tag: 'li',
2228             cls: 'dropdown-menu-item',
2229             cn: [
2230                     {
2231                         tag : 'a',
2232                         href : '#',
2233                         html : 'Link'
2234                     }
2235                 ]
2236         };
2237         if (this.parent().type == 'treeview') {
2238             cfg.cls = 'treeview-menu';
2239         }
2240         
2241         cfg.cn[0].href = this.href || cfg.cn[0].href ;
2242         cfg.cn[0].html = this.html || cfg.cn[0].html ;
2243         return cfg;
2244     },
2245     
2246     initEvents: function() {
2247         
2248         //this.el.select('a').on('click', this.onClick, this);
2249         
2250     },
2251     onClick : function(e)
2252     {
2253         Roo.log('item on click ');
2254         //if(this.preventDefault){
2255         //    e.preventDefault();
2256         //}
2257         //this.parent().hideMenuItems();
2258         
2259         this.fireEvent('click', this, e);
2260     },
2261     getEl : function()
2262     {
2263         return this.el;
2264     }
2265 });
2266
2267  
2268
2269  /*
2270  * - LGPL
2271  *
2272  * menu separator
2273  * 
2274  */
2275
2276
2277 /**
2278  * @class Roo.bootstrap.MenuSeparator
2279  * @extends Roo.bootstrap.Component
2280  * Bootstrap MenuSeparator class
2281  * 
2282  * @constructor
2283  * Create a new MenuItem
2284  * @param {Object} config The config object
2285  */
2286
2287
2288 Roo.bootstrap.MenuSeparator = function(config){
2289     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2290 };
2291
2292 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2293     
2294     getAutoCreate : function(){
2295         var cfg = {
2296             cls: 'divider',
2297             tag : 'li'
2298         };
2299         
2300         return cfg;
2301     }
2302    
2303 });
2304
2305  
2306
2307  
2308 /*
2309 * Licence: LGPL
2310 */
2311
2312 /**
2313  * @class Roo.bootstrap.Modal
2314  * @extends Roo.bootstrap.Component
2315  * Bootstrap Modal class
2316  * @cfg {String} title Title of dialog
2317  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2318  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn 
2319  * @cfg {Boolean} specificTitle default false
2320  * @cfg {Array} buttons Array of buttons or standard button set..
2321  * @cfg {String} buttonPosition (left|right|center) default right
2322  * @cfg {Boolean} animate default true
2323  * @cfg {Boolean} allow_close default true
2324  * 
2325  * @constructor
2326  * Create a new Modal Dialog
2327  * @param {Object} config The config object
2328  */
2329
2330 Roo.bootstrap.Modal = function(config){
2331     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2332     this.addEvents({
2333         // raw events
2334         /**
2335          * @event btnclick
2336          * The raw btnclick event for the button
2337          * @param {Roo.EventObject} e
2338          */
2339         "btnclick" : true
2340     });
2341     this.buttons = this.buttons || [];
2342      
2343     if (this.tmpl) {
2344         this.tmpl = Roo.factory(this.tmpl);
2345     }
2346     
2347 };
2348
2349 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2350     
2351     title : 'test dialog',
2352    
2353     buttons : false,
2354     
2355     // set on load...
2356      
2357     html: false,
2358     
2359     tmp: false,
2360     
2361     specificTitle: false,
2362     
2363     buttonPosition: 'right',
2364     
2365     allow_close : true,
2366     
2367     animate : true,
2368     
2369     
2370      // private
2371     bodyEl:  false,
2372     footerEl:  false,
2373     titleEl:  false,
2374     closeEl:  false,
2375     
2376     
2377     onRender : function(ct, position)
2378     {
2379         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2380      
2381         if(!this.el){
2382             var cfg = Roo.apply({},  this.getAutoCreate());
2383             cfg.id = Roo.id();
2384             //if(!cfg.name){
2385             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2386             //}
2387             //if (!cfg.name.length) {
2388             //    delete cfg.name;
2389            // }
2390             if (this.cls) {
2391                 cfg.cls += ' ' + this.cls;
2392             }
2393             if (this.style) {
2394                 cfg.style = this.style;
2395             }
2396             this.el = Roo.get(document.body).createChild(cfg, position);
2397         }
2398         //var type = this.el.dom.type;
2399         
2400         
2401         
2402         
2403         if(this.tabIndex !== undefined){
2404             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2405         }
2406         
2407         
2408         this.bodyEl = this.el.select('.modal-body',true).first();
2409         this.closeEl = this.el.select('.modal-header .close', true).first();
2410         this.footerEl = this.el.select('.modal-footer',true).first();
2411         this.titleEl = this.el.select('.modal-title',true).first();
2412         
2413         
2414          
2415         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2416         this.maskEl.enableDisplayMode("block");
2417         this.maskEl.hide();
2418         //this.el.addClass("x-dlg-modal");
2419     
2420         if (this.buttons.length) {
2421             Roo.each(this.buttons, function(bb) {
2422                 b = Roo.apply({}, bb);
2423                 b.xns = b.xns || Roo.bootstrap;
2424                 b.xtype = b.xtype || 'Button';
2425                 if (typeof(b.listeners) == 'undefined') {
2426                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2427                 }
2428                 
2429                 var btn = Roo.factory(b);
2430                 
2431                 btn.onRender(this.el.select('.modal-footer div').first());
2432                 
2433             },this);
2434         }
2435         // render the children.
2436         var nitems = [];
2437         
2438         if(typeof(this.items) != 'undefined'){
2439             var items = this.items;
2440             delete this.items;
2441
2442             for(var i =0;i < items.length;i++) {
2443                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2444             }
2445         }
2446         
2447         this.items = nitems;
2448         
2449         // where are these used - they used to be body/close/footer
2450         
2451        
2452         this.initEvents();
2453         //this.el.addClass([this.fieldClass, this.cls]);
2454         
2455     },
2456     getAutoCreate : function(){
2457         
2458         
2459         var bdy = {
2460                 cls : 'modal-body',
2461                 html : this.html || ''
2462         };
2463         
2464         var title = {
2465             tag: 'h4',
2466             cls : 'modal-title',
2467             html : this.title
2468         };
2469         
2470         if(this.specificTitle){
2471             title = this.title;
2472             
2473         };
2474         
2475         var header = [];
2476         if (this.allow_close) {
2477             header.push({
2478                 tag: 'button',
2479                 cls : 'close',
2480                 html : '&times'
2481             });
2482         }
2483         header.push(title);
2484         
2485         var modal = {
2486             cls: "modal",
2487             style : 'display: none',
2488             cn : [
2489                 {
2490                     cls: "modal-dialog",
2491                     cn : [
2492                         {
2493                             cls : "modal-content",
2494                             cn : [
2495                                 {
2496                                     cls : 'modal-header',
2497                                     cn : header
2498                                 },
2499                                 bdy,
2500                                 {
2501                                     cls : 'modal-footer',
2502                                     cn : [
2503                                         {
2504                                             tag: 'div',
2505                                             cls: 'btn-' + this.buttonPosition
2506                                         }
2507                                     ]
2508                                     
2509                                 }
2510                                 
2511                                 
2512                             ]
2513                             
2514                         }
2515                     ]
2516                         
2517                 }
2518             ]
2519         };
2520         
2521         if(this.animate){
2522             modal.cls += ' fade';
2523         }
2524         
2525         return modal;
2526           
2527     },
2528     getChildContainer : function() {
2529          
2530          return this.bodyEl;
2531         
2532     },
2533     getButtonContainer : function() {
2534          return this.el.select('.modal-footer div',true).first();
2535         
2536     },
2537     initEvents : function()
2538     {
2539         if (this.allow_close) {
2540             this.closeEl.on('click', this.hide, this);
2541         }
2542
2543     },
2544     show : function() {
2545         
2546         if (!this.rendered) {
2547             this.render();
2548         }
2549         
2550         this.el.setStyle('display', 'block');
2551         
2552         if(this.animate){
2553             var _this = this;
2554             (function(){ _this.el.addClass('in'); }).defer(50);
2555         }else{
2556             this.el.addClass('in');
2557         }
2558         
2559         // not sure how we can show data in here.. 
2560         //if (this.tmpl) {
2561         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2562         //}
2563         
2564         Roo.get(document.body).addClass("x-body-masked");
2565         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2566         this.maskEl.show();
2567         this.el.setStyle('zIndex', '10001');
2568        
2569         this.fireEvent('show', this);
2570         
2571         
2572     },
2573     hide : function()
2574     {
2575         this.maskEl.hide();
2576         Roo.get(document.body).removeClass("x-body-masked");
2577         this.el.removeClass('in');
2578         
2579         if(this.animate){
2580             var _this = this;
2581             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2582         }else{
2583             this.el.setStyle('display', 'none');
2584         }
2585         
2586         this.fireEvent('hide', this);
2587     },
2588     
2589     addButton : function(str, cb)
2590     {
2591          
2592         
2593         var b = Roo.apply({}, { html : str } );
2594         b.xns = b.xns || Roo.bootstrap;
2595         b.xtype = b.xtype || 'Button';
2596         if (typeof(b.listeners) == 'undefined') {
2597             b.listeners = { click : cb.createDelegate(this)  };
2598         }
2599         
2600         var btn = Roo.factory(b);
2601            
2602         btn.onRender(this.el.select('.modal-footer div').first());
2603         
2604         return btn;   
2605        
2606     },
2607     
2608     setDefaultButton : function(btn)
2609     {
2610         //this.el.select('.modal-footer').()
2611     },
2612     resizeTo: function(w,h)
2613     {
2614         // skip..
2615     },
2616     setContentSize  : function(w, h)
2617     {
2618         
2619     },
2620     onButtonClick: function(btn,e)
2621     {
2622         //Roo.log([a,b,c]);
2623         this.fireEvent('btnclick', btn.name, e);
2624     },
2625      /**
2626      * Set the title of the Dialog
2627      * @param {String} str new Title
2628      */
2629     setTitle: function(str) {
2630         this.titleEl.dom.innerHTML = str;    
2631     },
2632     /**
2633      * Set the body of the Dialog
2634      * @param {String} str new Title
2635      */
2636     setBody: function(str) {
2637         this.bodyEl.dom.innerHTML = str;    
2638     },
2639     /**
2640      * Set the body of the Dialog using the template
2641      * @param {Obj} data - apply this data to the template and replace the body contents.
2642      */
2643     applyBody: function(obj)
2644     {
2645         if (!this.tmpl) {
2646             Roo.log("Error - using apply Body without a template");
2647             //code
2648         }
2649         this.tmpl.overwrite(this.bodyEl, obj);
2650     }
2651     
2652 });
2653
2654
2655 Roo.apply(Roo.bootstrap.Modal,  {
2656     /**
2657          * Button config that displays a single OK button
2658          * @type Object
2659          */
2660         OK :  [{
2661             name : 'ok',
2662             weight : 'primary',
2663             html : 'OK'
2664         }], 
2665         /**
2666          * Button config that displays Yes and No buttons
2667          * @type Object
2668          */
2669         YESNO : [
2670             {
2671                 name  : 'no',
2672                 html : 'No'
2673             },
2674             {
2675                 name  :'yes',
2676                 weight : 'primary',
2677                 html : 'Yes'
2678             }
2679         ],
2680         
2681         /**
2682          * Button config that displays OK and Cancel buttons
2683          * @type Object
2684          */
2685         OKCANCEL : [
2686             {
2687                name : 'cancel',
2688                 html : 'Cancel'
2689             },
2690             {
2691                 name : 'ok',
2692                 weight : 'primary',
2693                 html : 'OK'
2694             }
2695         ],
2696         /**
2697          * Button config that displays Yes, No and Cancel buttons
2698          * @type Object
2699          */
2700         YESNOCANCEL : [
2701             {
2702                 name : 'yes',
2703                 weight : 'primary',
2704                 html : 'Yes'
2705             },
2706             {
2707                 name : 'no',
2708                 html : 'No'
2709             },
2710             {
2711                 name : 'cancel',
2712                 html : 'Cancel'
2713             }
2714         ]
2715 });
2716  
2717  /*
2718  * - LGPL
2719  *
2720  * messagebox - can be used as a replace
2721  * 
2722  */
2723 /**
2724  * @class Roo.MessageBox
2725  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2726  * Example usage:
2727  *<pre><code>
2728 // Basic alert:
2729 Roo.Msg.alert('Status', 'Changes saved successfully.');
2730
2731 // Prompt for user data:
2732 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2733     if (btn == 'ok'){
2734         // process text value...
2735     }
2736 });
2737
2738 // Show a dialog using config options:
2739 Roo.Msg.show({
2740    title:'Save Changes?',
2741    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2742    buttons: Roo.Msg.YESNOCANCEL,
2743    fn: processResult,
2744    animEl: 'elId'
2745 });
2746 </code></pre>
2747  * @singleton
2748  */
2749 Roo.bootstrap.MessageBox = function(){
2750     var dlg, opt, mask, waitTimer;
2751     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2752     var buttons, activeTextEl, bwidth;
2753
2754     
2755     // private
2756     var handleButton = function(button){
2757         dlg.hide();
2758         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2759     };
2760
2761     // private
2762     var handleHide = function(){
2763         if(opt && opt.cls){
2764             dlg.el.removeClass(opt.cls);
2765         }
2766         //if(waitTimer){
2767         //    Roo.TaskMgr.stop(waitTimer);
2768         //    waitTimer = null;
2769         //}
2770     };
2771
2772     // private
2773     var updateButtons = function(b){
2774         var width = 0;
2775         if(!b){
2776             buttons["ok"].hide();
2777             buttons["cancel"].hide();
2778             buttons["yes"].hide();
2779             buttons["no"].hide();
2780             //dlg.footer.dom.style.display = 'none';
2781             return width;
2782         }
2783         dlg.footerEl.dom.style.display = '';
2784         for(var k in buttons){
2785             if(typeof buttons[k] != "function"){
2786                 if(b[k]){
2787                     buttons[k].show();
2788                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2789                     width += buttons[k].el.getWidth()+15;
2790                 }else{
2791                     buttons[k].hide();
2792                 }
2793             }
2794         }
2795         return width;
2796     };
2797
2798     // private
2799     var handleEsc = function(d, k, e){
2800         if(opt && opt.closable !== false){
2801             dlg.hide();
2802         }
2803         if(e){
2804             e.stopEvent();
2805         }
2806     };
2807
2808     return {
2809         /**
2810          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2811          * @return {Roo.BasicDialog} The BasicDialog element
2812          */
2813         getDialog : function(){
2814            if(!dlg){
2815                 dlg = new Roo.bootstrap.Modal( {
2816                     //draggable: true,
2817                     //resizable:false,
2818                     //constraintoviewport:false,
2819                     //fixedcenter:true,
2820                     //collapsible : false,
2821                     //shim:true,
2822                     //modal: true,
2823                   //  width:400,
2824                   //  height:100,
2825                     //buttonAlign:"center",
2826                     closeClick : function(){
2827                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2828                             handleButton("no");
2829                         }else{
2830                             handleButton("cancel");
2831                         }
2832                     }
2833                 });
2834                 dlg.render();
2835                 dlg.on("hide", handleHide);
2836                 mask = dlg.mask;
2837                 //dlg.addKeyListener(27, handleEsc);
2838                 buttons = {};
2839                 this.buttons = buttons;
2840                 var bt = this.buttonText;
2841                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2842                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2843                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2844                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2845                 Roo.log(buttons)
2846                 bodyEl = dlg.bodyEl.createChild({
2847
2848                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2849                         '<textarea class="roo-mb-textarea"></textarea>' +
2850                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2851                 });
2852                 msgEl = bodyEl.dom.firstChild;
2853                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2854                 textboxEl.enableDisplayMode();
2855                 textboxEl.addKeyListener([10,13], function(){
2856                     if(dlg.isVisible() && opt && opt.buttons){
2857                         if(opt.buttons.ok){
2858                             handleButton("ok");
2859                         }else if(opt.buttons.yes){
2860                             handleButton("yes");
2861                         }
2862                     }
2863                 });
2864                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2865                 textareaEl.enableDisplayMode();
2866                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2867                 progressEl.enableDisplayMode();
2868                 var pf = progressEl.dom.firstChild;
2869                 if (pf) {
2870                     pp = Roo.get(pf.firstChild);
2871                     pp.setHeight(pf.offsetHeight);
2872                 }
2873                 
2874             }
2875             return dlg;
2876         },
2877
2878         /**
2879          * Updates the message box body text
2880          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2881          * the XHTML-compliant non-breaking space character '&amp;#160;')
2882          * @return {Roo.MessageBox} This message box
2883          */
2884         updateText : function(text){
2885             if(!dlg.isVisible() && !opt.width){
2886                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2887             }
2888             msgEl.innerHTML = text || '&#160;';
2889       
2890             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2891             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2892             var w = Math.max(
2893                     Math.min(opt.width || cw , this.maxWidth), 
2894                     Math.max(opt.minWidth || this.minWidth, bwidth)
2895             );
2896             if(opt.prompt){
2897                 activeTextEl.setWidth(w);
2898             }
2899             if(dlg.isVisible()){
2900                 dlg.fixedcenter = false;
2901             }
2902             // to big, make it scroll. = But as usual stupid IE does not support
2903             // !important..
2904             
2905             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2906                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2907                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2908             } else {
2909                 bodyEl.dom.style.height = '';
2910                 bodyEl.dom.style.overflowY = '';
2911             }
2912             if (cw > w) {
2913                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2914             } else {
2915                 bodyEl.dom.style.overflowX = '';
2916             }
2917             
2918             dlg.setContentSize(w, bodyEl.getHeight());
2919             if(dlg.isVisible()){
2920                 dlg.fixedcenter = true;
2921             }
2922             return this;
2923         },
2924
2925         /**
2926          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2927          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2928          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2929          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2930          * @return {Roo.MessageBox} This message box
2931          */
2932         updateProgress : function(value, text){
2933             if(text){
2934                 this.updateText(text);
2935             }
2936             if (pp) { // weird bug on my firefox - for some reason this is not defined
2937                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2938             }
2939             return this;
2940         },        
2941
2942         /**
2943          * Returns true if the message box is currently displayed
2944          * @return {Boolean} True if the message box is visible, else false
2945          */
2946         isVisible : function(){
2947             return dlg && dlg.isVisible();  
2948         },
2949
2950         /**
2951          * Hides the message box if it is displayed
2952          */
2953         hide : function(){
2954             if(this.isVisible()){
2955                 dlg.hide();
2956             }  
2957         },
2958
2959         /**
2960          * Displays a new message box, or reinitializes an existing message box, based on the config options
2961          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2962          * The following config object properties are supported:
2963          * <pre>
2964 Property    Type             Description
2965 ----------  ---------------  ------------------------------------------------------------------------------------
2966 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2967                                    closes (defaults to undefined)
2968 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2969                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2970 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2971                                    progress and wait dialogs will ignore this property and always hide the
2972                                    close button as they can only be closed programmatically.
2973 cls               String           A custom CSS class to apply to the message box element
2974 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2975                                    displayed (defaults to 75)
2976 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2977                                    function will be btn (the name of the button that was clicked, if applicable,
2978                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2979                                    Progress and wait dialogs will ignore this option since they do not respond to
2980                                    user actions and can only be closed programmatically, so any required function
2981                                    should be called by the same code after it closes the dialog.
2982 icon              String           A CSS class that provides a background image to be used as an icon for
2983                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2984 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2985 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2986 modal             Boolean          False to allow user interaction with the page while the message box is
2987                                    displayed (defaults to true)
2988 msg               String           A string that will replace the existing message box body text (defaults
2989                                    to the XHTML-compliant non-breaking space character '&#160;')
2990 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2991 progress          Boolean          True to display a progress bar (defaults to false)
2992 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2993 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2994 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2995 title             String           The title text
2996 value             String           The string value to set into the active textbox element if displayed
2997 wait              Boolean          True to display a progress bar (defaults to false)
2998 width             Number           The width of the dialog in pixels
2999 </pre>
3000          *
3001          * Example usage:
3002          * <pre><code>
3003 Roo.Msg.show({
3004    title: 'Address',
3005    msg: 'Please enter your address:',
3006    width: 300,
3007    buttons: Roo.MessageBox.OKCANCEL,
3008    multiline: true,
3009    fn: saveAddress,
3010    animEl: 'addAddressBtn'
3011 });
3012 </code></pre>
3013          * @param {Object} config Configuration options
3014          * @return {Roo.MessageBox} This message box
3015          */
3016         show : function(options)
3017         {
3018             
3019             // this causes nightmares if you show one dialog after another
3020             // especially on callbacks..
3021              
3022             if(this.isVisible()){
3023                 
3024                 this.hide();
3025                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3026                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3027                 Roo.log("New Dialog Message:" +  options.msg )
3028                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3029                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3030                 
3031             }
3032             var d = this.getDialog();
3033             opt = options;
3034             d.setTitle(opt.title || "&#160;");
3035             d.closeEl.setDisplayed(opt.closable !== false);
3036             activeTextEl = textboxEl;
3037             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3038             if(opt.prompt){
3039                 if(opt.multiline){
3040                     textboxEl.hide();
3041                     textareaEl.show();
3042                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3043                         opt.multiline : this.defaultTextHeight);
3044                     activeTextEl = textareaEl;
3045                 }else{
3046                     textboxEl.show();
3047                     textareaEl.hide();
3048                 }
3049             }else{
3050                 textboxEl.hide();
3051                 textareaEl.hide();
3052             }
3053             progressEl.setDisplayed(opt.progress === true);
3054             this.updateProgress(0);
3055             activeTextEl.dom.value = opt.value || "";
3056             if(opt.prompt){
3057                 dlg.setDefaultButton(activeTextEl);
3058             }else{
3059                 var bs = opt.buttons;
3060                 var db = null;
3061                 if(bs && bs.ok){
3062                     db = buttons["ok"];
3063                 }else if(bs && bs.yes){
3064                     db = buttons["yes"];
3065                 }
3066                 dlg.setDefaultButton(db);
3067             }
3068             bwidth = updateButtons(opt.buttons);
3069             this.updateText(opt.msg);
3070             if(opt.cls){
3071                 d.el.addClass(opt.cls);
3072             }
3073             d.proxyDrag = opt.proxyDrag === true;
3074             d.modal = opt.modal !== false;
3075             d.mask = opt.modal !== false ? mask : false;
3076             if(!d.isVisible()){
3077                 // force it to the end of the z-index stack so it gets a cursor in FF
3078                 document.body.appendChild(dlg.el.dom);
3079                 d.animateTarget = null;
3080                 d.show(options.animEl);
3081             }
3082             return this;
3083         },
3084
3085         /**
3086          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3087          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3088          * and closing the message box when the process is complete.
3089          * @param {String} title The title bar text
3090          * @param {String} msg The message box body text
3091          * @return {Roo.MessageBox} This message box
3092          */
3093         progress : function(title, msg){
3094             this.show({
3095                 title : title,
3096                 msg : msg,
3097                 buttons: false,
3098                 progress:true,
3099                 closable:false,
3100                 minWidth: this.minProgressWidth,
3101                 modal : true
3102             });
3103             return this;
3104         },
3105
3106         /**
3107          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3108          * If a callback function is passed it will be called after the user clicks the button, and the
3109          * id of the button that was clicked will be passed as the only parameter to the callback
3110          * (could also be the top-right close button).
3111          * @param {String} title The title bar text
3112          * @param {String} msg The message box body text
3113          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3114          * @param {Object} scope (optional) The scope of the callback function
3115          * @return {Roo.MessageBox} This message box
3116          */
3117         alert : function(title, msg, fn, scope){
3118             this.show({
3119                 title : title,
3120                 msg : msg,
3121                 buttons: this.OK,
3122                 fn: fn,
3123                 scope : scope,
3124                 modal : true
3125             });
3126             return this;
3127         },
3128
3129         /**
3130          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3131          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3132          * You are responsible for closing the message box when the process is complete.
3133          * @param {String} msg The message box body text
3134          * @param {String} title (optional) The title bar text
3135          * @return {Roo.MessageBox} This message box
3136          */
3137         wait : function(msg, title){
3138             this.show({
3139                 title : title,
3140                 msg : msg,
3141                 buttons: false,
3142                 closable:false,
3143                 progress:true,
3144                 modal:true,
3145                 width:300,
3146                 wait:true
3147             });
3148             waitTimer = Roo.TaskMgr.start({
3149                 run: function(i){
3150                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3151                 },
3152                 interval: 1000
3153             });
3154             return this;
3155         },
3156
3157         /**
3158          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3159          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3160          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3161          * @param {String} title The title bar text
3162          * @param {String} msg The message box body text
3163          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3164          * @param {Object} scope (optional) The scope of the callback function
3165          * @return {Roo.MessageBox} This message box
3166          */
3167         confirm : function(title, msg, fn, scope){
3168             this.show({
3169                 title : title,
3170                 msg : msg,
3171                 buttons: this.YESNO,
3172                 fn: fn,
3173                 scope : scope,
3174                 modal : true
3175             });
3176             return this;
3177         },
3178
3179         /**
3180          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3181          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3182          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3183          * (could also be the top-right close button) and the text that was entered will be passed as the two
3184          * parameters to the callback.
3185          * @param {String} title The title bar text
3186          * @param {String} msg The message box body text
3187          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3188          * @param {Object} scope (optional) The scope of the callback function
3189          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3190          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3191          * @return {Roo.MessageBox} This message box
3192          */
3193         prompt : function(title, msg, fn, scope, multiline){
3194             this.show({
3195                 title : title,
3196                 msg : msg,
3197                 buttons: this.OKCANCEL,
3198                 fn: fn,
3199                 minWidth:250,
3200                 scope : scope,
3201                 prompt:true,
3202                 multiline: multiline,
3203                 modal : true
3204             });
3205             return this;
3206         },
3207
3208         /**
3209          * Button config that displays a single OK button
3210          * @type Object
3211          */
3212         OK : {ok:true},
3213         /**
3214          * Button config that displays Yes and No buttons
3215          * @type Object
3216          */
3217         YESNO : {yes:true, no:true},
3218         /**
3219          * Button config that displays OK and Cancel buttons
3220          * @type Object
3221          */
3222         OKCANCEL : {ok:true, cancel:true},
3223         /**
3224          * Button config that displays Yes, No and Cancel buttons
3225          * @type Object
3226          */
3227         YESNOCANCEL : {yes:true, no:true, cancel:true},
3228
3229         /**
3230          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3231          * @type Number
3232          */
3233         defaultTextHeight : 75,
3234         /**
3235          * The maximum width in pixels of the message box (defaults to 600)
3236          * @type Number
3237          */
3238         maxWidth : 600,
3239         /**
3240          * The minimum width in pixels of the message box (defaults to 100)
3241          * @type Number
3242          */
3243         minWidth : 100,
3244         /**
3245          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3246          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3247          * @type Number
3248          */
3249         minProgressWidth : 250,
3250         /**
3251          * An object containing the default button text strings that can be overriden for localized language support.
3252          * Supported properties are: ok, cancel, yes and no.
3253          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3254          * @type Object
3255          */
3256         buttonText : {
3257             ok : "OK",
3258             cancel : "Cancel",
3259             yes : "Yes",
3260             no : "No"
3261         }
3262     };
3263 }();
3264
3265 /**
3266  * Shorthand for {@link Roo.MessageBox}
3267  */
3268 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3269 Roo.Msg = Roo.Msg || Roo.MessageBox;
3270 /*
3271  * - LGPL
3272  *
3273  * navbar
3274  * 
3275  */
3276
3277 /**
3278  * @class Roo.bootstrap.Navbar
3279  * @extends Roo.bootstrap.Component
3280  * Bootstrap Navbar class
3281
3282  * @constructor
3283  * Create a new Navbar
3284  * @param {Object} config The config object
3285  */
3286
3287
3288 Roo.bootstrap.Navbar = function(config){
3289     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3290     
3291 };
3292
3293 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3294     
3295     
3296    
3297     // private
3298     navItems : false,
3299     loadMask : false,
3300     
3301     
3302     getAutoCreate : function(){
3303         
3304         
3305         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3306         
3307     },
3308     
3309     initEvents :function ()
3310     {
3311         //Roo.log(this.el.select('.navbar-toggle',true));
3312         this.el.select('.navbar-toggle',true).on('click', function() {
3313            // Roo.log('click');
3314             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3315         }, this);
3316         
3317         var mark = {
3318             tag: "div",
3319             cls:"x-dlg-mask"
3320         }
3321         
3322         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3323         
3324         var size = this.el.getSize();
3325         this.maskEl.setSize(size.width, size.height);
3326         this.maskEl.enableDisplayMode("block");
3327         this.maskEl.hide();
3328         
3329         if(this.loadMask){
3330             this.maskEl.show();
3331         }
3332     },
3333     
3334     
3335     getChildContainer : function()
3336     {
3337         if (this.el.select('.collapse').getCount()) {
3338             return this.el.select('.collapse',true).first();
3339         }
3340         
3341         return this.el;
3342     },
3343     
3344     mask : function()
3345     {
3346         this.maskEl.show();
3347     },
3348     
3349     unmask : function()
3350     {
3351         this.maskEl.hide();
3352     } 
3353     
3354     
3355     
3356     
3357 });
3358
3359
3360
3361  
3362
3363  /*
3364  * - LGPL
3365  *
3366  * navbar
3367  * 
3368  */
3369
3370 /**
3371  * @class Roo.bootstrap.NavSimplebar
3372  * @extends Roo.bootstrap.Navbar
3373  * Bootstrap Sidebar class
3374  *
3375  * @cfg {Boolean} inverse is inverted color
3376  * 
3377  * @cfg {String} type (nav | pills | tabs)
3378  * @cfg {Boolean} arrangement stacked | justified
3379  * @cfg {String} align (left | right) alignment
3380  * 
3381  * @cfg {Boolean} main (true|false) main nav bar? default false
3382  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3383  * 
3384  * @cfg {String} tag (header|footer|nav|div) default is nav 
3385
3386  * 
3387  * 
3388  * 
3389  * @constructor
3390  * Create a new Sidebar
3391  * @param {Object} config The config object
3392  */
3393
3394
3395 Roo.bootstrap.NavSimplebar = function(config){
3396     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3397 };
3398
3399 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3400     
3401     inverse: false,
3402     
3403     type: false,
3404     arrangement: '',
3405     align : false,
3406     
3407     
3408     
3409     main : false,
3410     
3411     
3412     tag : false,
3413     
3414     
3415     getAutoCreate : function(){
3416         
3417         
3418         var cfg = {
3419             tag : this.tag || 'div',
3420             cls : 'navbar'
3421         };
3422           
3423         
3424         cfg.cn = [
3425             {
3426                 cls: 'nav',
3427                 tag : 'ul'
3428             }
3429         ];
3430         
3431          
3432         this.type = this.type || 'nav';
3433         if (['tabs','pills'].indexOf(this.type)!==-1) {
3434             cfg.cn[0].cls += ' nav-' + this.type
3435         
3436         
3437         } else {
3438             if (this.type!=='nav') {
3439                 Roo.log('nav type must be nav/tabs/pills')
3440             }
3441             cfg.cn[0].cls += ' navbar-nav'
3442         }
3443         
3444         
3445         
3446         
3447         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3448             cfg.cn[0].cls += ' nav-' + this.arrangement;
3449         }
3450         
3451         
3452         if (this.align === 'right') {
3453             cfg.cn[0].cls += ' navbar-right';
3454         }
3455         
3456         if (this.inverse) {
3457             cfg.cls += ' navbar-inverse';
3458             
3459         }
3460         
3461         
3462         return cfg;
3463     
3464         
3465     }
3466     
3467     
3468     
3469 });
3470
3471
3472
3473  
3474
3475  
3476        /*
3477  * - LGPL
3478  *
3479  * navbar
3480  * 
3481  */
3482
3483 /**
3484  * @class Roo.bootstrap.NavHeaderbar
3485  * @extends Roo.bootstrap.NavSimplebar
3486  * Bootstrap Sidebar class
3487  *
3488  * @cfg {String} brand what is brand
3489  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3490  * @cfg {String} brand_href href of the brand
3491  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3492  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3493  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3494  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3495  * 
3496  * @constructor
3497  * Create a new Sidebar
3498  * @param {Object} config The config object
3499  */
3500
3501
3502 Roo.bootstrap.NavHeaderbar = function(config){
3503     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3504       
3505 };
3506
3507 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3508     
3509     position: '',
3510     brand: '',
3511     brand_href: false,
3512     srButton : true,
3513     autohide : false,
3514     desktopCenter : false,
3515    
3516     
3517     getAutoCreate : function(){
3518         
3519         var   cfg = {
3520             tag: this.nav || 'nav',
3521             cls: 'navbar',
3522             role: 'navigation',
3523             cn: []
3524         };
3525         
3526         var cn = cfg.cn;
3527         if (this.desktopCenter) {
3528             cn.push({cls : 'container', cn : []});
3529             cn = cn[0].cn;
3530         }
3531         
3532         if(this.srButton){
3533             cn.push({
3534                 tag: 'div',
3535                 cls: 'navbar-header',
3536                 cn: [
3537                     {
3538                         tag: 'button',
3539                         type: 'button',
3540                         cls: 'navbar-toggle',
3541                         'data-toggle': 'collapse',
3542                         cn: [
3543                             {
3544                                 tag: 'span',
3545                                 cls: 'sr-only',
3546                                 html: 'Toggle navigation'
3547                             },
3548                             {
3549                                 tag: 'span',
3550                                 cls: 'icon-bar'
3551                             },
3552                             {
3553                                 tag: 'span',
3554                                 cls: 'icon-bar'
3555                             },
3556                             {
3557                                 tag: 'span',
3558                                 cls: 'icon-bar'
3559                             }
3560                         ]
3561                     }
3562                 ]
3563             });
3564         }
3565         
3566         cn.push({
3567             tag: 'div',
3568             cls: 'collapse navbar-collapse',
3569             cn : []
3570         });
3571         
3572         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3573         
3574         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3575             cfg.cls += ' navbar-' + this.position;
3576             
3577             // tag can override this..
3578             
3579             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3580         }
3581         
3582         if (this.brand !== '') {
3583             cn[0].cn.push({
3584                 tag: 'a',
3585                 href: this.brand_href ? this.brand_href : '#',
3586                 cls: 'navbar-brand',
3587                 cn: [
3588                 this.brand
3589                 ]
3590             });
3591         }
3592         
3593         if(this.main){
3594             cfg.cls += ' main-nav';
3595         }
3596         
3597         
3598         return cfg;
3599
3600         
3601     },
3602     getHeaderChildContainer : function()
3603     {
3604         if (this.el.select('.navbar-header').getCount()) {
3605             return this.el.select('.navbar-header',true).first();
3606         }
3607         
3608         return this.getChildContainer();
3609     },
3610     
3611     
3612     initEvents : function()
3613     {
3614         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3615         
3616         if (this.autohide) {
3617             
3618             var prevScroll = 0;
3619             var ft = this.el;
3620             
3621             Roo.get(document).on('scroll',function(e) {
3622                 var ns = Roo.get(document).getScroll().top;
3623                 var os = prevScroll;
3624                 prevScroll = ns;
3625                 
3626                 if(ns > os){
3627                     ft.removeClass('slideDown');
3628                     ft.addClass('slideUp');
3629                     return;
3630                 }
3631                 ft.removeClass('slideUp');
3632                 ft.addClass('slideDown');
3633                  
3634               
3635           },this);
3636         }
3637     }    
3638     
3639 });
3640
3641
3642
3643  
3644
3645  /*
3646  * - LGPL
3647  *
3648  * navbar
3649  * 
3650  */
3651
3652 /**
3653  * @class Roo.bootstrap.NavSidebar
3654  * @extends Roo.bootstrap.Navbar
3655  * Bootstrap Sidebar class
3656  * 
3657  * @constructor
3658  * Create a new Sidebar
3659  * @param {Object} config The config object
3660  */
3661
3662
3663 Roo.bootstrap.NavSidebar = function(config){
3664     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3665 };
3666
3667 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3668     
3669     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3670     
3671     getAutoCreate : function(){
3672         
3673         
3674         return  {
3675             tag: 'div',
3676             cls: 'sidebar sidebar-nav'
3677         };
3678     
3679         
3680     }
3681     
3682     
3683     
3684 });
3685
3686
3687
3688  
3689
3690  /*
3691  * - LGPL
3692  *
3693  * nav group
3694  * 
3695  */
3696
3697 /**
3698  * @class Roo.bootstrap.NavGroup
3699  * @extends Roo.bootstrap.Component
3700  * Bootstrap NavGroup class
3701  * @cfg {String} align (left|right)
3702  * @cfg {Boolean} inverse
3703  * @cfg {String} type (nav|pills|tab) default nav
3704  * @cfg {String} navId - reference Id for navbar.
3705
3706  * 
3707  * @constructor
3708  * Create a new nav group
3709  * @param {Object} config The config object
3710  */
3711
3712 Roo.bootstrap.NavGroup = function(config){
3713     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3714     this.navItems = [];
3715    
3716     Roo.bootstrap.NavGroup.register(this);
3717      this.addEvents({
3718         /**
3719              * @event changed
3720              * Fires when the active item changes
3721              * @param {Roo.bootstrap.NavGroup} this
3722              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3723              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3724          */
3725         'changed': true
3726      });
3727     
3728 };
3729
3730 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3731     
3732     align: '',
3733     inverse: false,
3734     form: false,
3735     type: 'nav',
3736     navId : '',
3737     // private
3738     
3739     navItems : false, 
3740     
3741     getAutoCreate : function()
3742     {
3743         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3744         
3745         cfg = {
3746             tag : 'ul',
3747             cls: 'nav' 
3748         }
3749         
3750         if (['tabs','pills'].indexOf(this.type)!==-1) {
3751             cfg.cls += ' nav-' + this.type
3752         } else {
3753             if (this.type!=='nav') {
3754                 Roo.log('nav type must be nav/tabs/pills')
3755             }
3756             cfg.cls += ' navbar-nav'
3757         }
3758         
3759         if (this.parent().sidebar) {
3760             cfg = {
3761                 tag: 'ul',
3762                 cls: 'dashboard-menu sidebar-menu'
3763             }
3764             
3765             return cfg;
3766         }
3767         
3768         if (this.form === true) {
3769             cfg = {
3770                 tag: 'form',
3771                 cls: 'navbar-form'
3772             }
3773             
3774             if (this.align === 'right') {
3775                 cfg.cls += ' navbar-right';
3776             } else {
3777                 cfg.cls += ' navbar-left';
3778             }
3779         }
3780         
3781         if (this.align === 'right') {
3782             cfg.cls += ' navbar-right';
3783         }
3784         
3785         if (this.inverse) {
3786             cfg.cls += ' navbar-inverse';
3787             
3788         }
3789         
3790         
3791         return cfg;
3792     },
3793     /**
3794     * sets the active Navigation item
3795     * @param {Roo.bootstrap.NavItem} the new current navitem
3796     */
3797     setActiveItem : function(item)
3798     {
3799         var prev = false;
3800         Roo.each(this.navItems, function(v){
3801             if (v == item) {
3802                 return ;
3803             }
3804             if (v.isActive()) {
3805                 v.setActive(false, true);
3806                 prev = v;
3807                 
3808             }
3809             
3810         });
3811
3812         item.setActive(true, true);
3813         this.fireEvent('changed', this, item, prev);
3814         
3815         
3816     },
3817     /**
3818     * gets the active Navigation item
3819     * @return {Roo.bootstrap.NavItem} the current navitem
3820     */
3821     getActive : function()
3822     {
3823         
3824         var prev = false;
3825         Roo.each(this.navItems, function(v){
3826             
3827             if (v.isActive()) {
3828                 prev = v;
3829                 
3830             }
3831             
3832         });
3833         return prev;
3834     },
3835     
3836     indexOfNav : function()
3837     {
3838         
3839         var prev = false;
3840         Roo.each(this.navItems, function(v,i){
3841             
3842             if (v.isActive()) {
3843                 prev = i;
3844                 
3845             }
3846             
3847         });
3848         return prev;
3849     },
3850     /**
3851     * adds a Navigation item
3852     * @param {Roo.bootstrap.NavItem} the navitem to add
3853     */
3854     addItem : function(cfg)
3855     {
3856         var cn = new Roo.bootstrap.NavItem(cfg);
3857         this.register(cn);
3858         cn.parentId = this.id;
3859         cn.onRender(this.el, null);
3860         return cn;
3861     },
3862     /**
3863     * register a Navigation item
3864     * @param {Roo.bootstrap.NavItem} the navitem to add
3865     */
3866     register : function(item)
3867     {
3868         this.navItems.push( item);
3869         item.navId = this.navId;
3870     
3871     },
3872     
3873     /**
3874     * clear all the Navigation item
3875     */
3876    
3877     clearAll : function()
3878     {
3879         this.navItems = [];
3880         this.el.dom.innerHTML = '';
3881     },
3882     
3883     getNavItem: function(tabId)
3884     {
3885         var ret = false;
3886         Roo.each(this.navItems, function(e) {
3887             if (e.tabId == tabId) {
3888                ret =  e;
3889                return false;
3890             }
3891             return true;
3892             
3893         });
3894         return ret;
3895     },
3896     
3897     setActiveNext : function()
3898     {
3899         var i = this.indexOfNav(this.getActive());
3900         if (i > this.navItems.length) {
3901             return;
3902         }
3903         this.setActiveItem(this.navItems[i+1]);
3904     },
3905     setActivePrev : function()
3906     {
3907         var i = this.indexOfNav(this.getActive());
3908         if (i  < 1) {
3909             return;
3910         }
3911         this.setActiveItem(this.navItems[i-1]);
3912     },
3913     clearWasActive : function(except) {
3914         Roo.each(this.navItems, function(e) {
3915             if (e.tabId != except.tabId && e.was_active) {
3916                e.was_active = false;
3917                return false;
3918             }
3919             return true;
3920             
3921         });
3922     },
3923     getWasActive : function ()
3924     {
3925         var r = false;
3926         Roo.each(this.navItems, function(e) {
3927             if (e.was_active) {
3928                r = e;
3929                return false;
3930             }
3931             return true;
3932             
3933         });
3934         return r;
3935     }
3936     
3937     
3938 });
3939
3940  
3941 Roo.apply(Roo.bootstrap.NavGroup, {
3942     
3943     groups: {},
3944      /**
3945     * register a Navigation Group
3946     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3947     */
3948     register : function(navgrp)
3949     {
3950         this.groups[navgrp.navId] = navgrp;
3951         
3952     },
3953     /**
3954     * fetch a Navigation Group based on the navigation ID
3955     * @param {string} the navgroup to add
3956     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3957     */
3958     get: function(navId) {
3959         if (typeof(this.groups[navId]) == 'undefined') {
3960             return false;
3961             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3962         }
3963         return this.groups[navId] ;
3964     }
3965     
3966     
3967     
3968 });
3969
3970  /*
3971  * - LGPL
3972  *
3973  * row
3974  * 
3975  */
3976
3977 /**
3978  * @class Roo.bootstrap.NavItem
3979  * @extends Roo.bootstrap.Component
3980  * Bootstrap Navbar.NavItem class
3981  * @cfg {String} href  link to
3982  * @cfg {String} html content of button
3983  * @cfg {String} badge text inside badge
3984  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3985  * @cfg {String} glyphicon name of glyphicon
3986  * @cfg {String} icon name of font awesome icon
3987  * @cfg {Boolean} active Is item active
3988  * @cfg {Boolean} disabled Is item disabled
3989  
3990  * @cfg {Boolean} preventDefault (true | false) default false
3991  * @cfg {String} tabId the tab that this item activates.
3992  * @cfg {String} tagtype (a|span) render as a href or span?
3993  * @cfg {Boolean} animateRef (true|false) link to element default false  
3994   
3995  * @constructor
3996  * Create a new Navbar Item
3997  * @param {Object} config The config object
3998  */
3999 Roo.bootstrap.NavItem = function(config){
4000     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4001     this.addEvents({
4002         // raw events
4003         /**
4004          * @event click
4005          * The raw click event for the entire grid.
4006          * @param {Roo.EventObject} e
4007          */
4008         "click" : true,
4009          /**
4010             * @event changed
4011             * Fires when the active item active state changes
4012             * @param {Roo.bootstrap.NavItem} this
4013             * @param {boolean} state the new state
4014              
4015          */
4016         'changed': true,
4017         /**
4018             * @event scrollto
4019             * Fires when scroll to element
4020             * @param {Roo.bootstrap.NavItem} this
4021             * @param {Object} options
4022             * @param {Roo.EventObject} e
4023              
4024          */
4025         'scrollto': true
4026     });
4027    
4028 };
4029
4030 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4031     
4032     href: false,
4033     html: '',
4034     badge: '',
4035     icon: false,
4036     glyphicon: false,
4037     active: false,
4038     preventDefault : false,
4039     tabId : false,
4040     tagtype : 'a',
4041     disabled : false,
4042     animateRef : false,
4043     was_active : false,
4044     
4045     getAutoCreate : function(){
4046          
4047         var cfg = {
4048             tag: 'li',
4049             cls: 'nav-item'
4050             
4051         }
4052         if (this.active) {
4053             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4054         }
4055         if (this.disabled) {
4056             cfg.cls += ' disabled';
4057         }
4058         
4059         if (this.href || this.html || this.glyphicon || this.icon) {
4060             cfg.cn = [
4061                 {
4062                     tag: this.tagtype,
4063                     href : this.href || "#",
4064                     html: this.html || ''
4065                 }
4066             ];
4067             
4068             if (this.icon) {
4069                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4070             }
4071
4072             if(this.glyphicon) {
4073                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4074             }
4075             
4076             if (this.menu) {
4077                 
4078                 cfg.cn[0].html += " <span class='caret'></span>";
4079              
4080             }
4081             
4082             if (this.badge !== '') {
4083                  
4084                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4085             }
4086         }
4087         
4088         
4089         
4090         return cfg;
4091     },
4092     initEvents: function() 
4093     {
4094         if (typeof (this.menu) != 'undefined') {
4095             this.menu.parentType = this.xtype;
4096             this.menu.triggerEl = this.el;
4097             this.menu = this.addxtype(Roo.apply({}, this.menu));
4098         }
4099         
4100         this.el.select('a',true).on('click', this.onClick, this);
4101         
4102         if(this.tagtype == 'span'){
4103             this.el.select('span',true).on('click', this.onClick, this);
4104         }
4105        
4106         // at this point parent should be available..
4107         this.parent().register(this);
4108     },
4109     
4110     onClick : function(e)
4111     {
4112         if(
4113                 this.preventDefault || 
4114                 this.href == '#' 
4115         ){
4116             
4117             e.preventDefault();
4118         }
4119         
4120         if (this.disabled) {
4121             return;
4122         }
4123         
4124         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4125         if (tg && tg.transition) {
4126             Roo.log("waiting for the transitionend");
4127             return;
4128         }
4129         
4130         
4131         
4132         //Roo.log("fire event clicked");
4133         if(this.fireEvent('click', this, e) === false){
4134             return;
4135         };
4136         
4137         if(this.tagtype == 'span'){
4138             return;
4139         }
4140         
4141         //Roo.log(this.href);
4142         var ael = this.el.select('a',true).first();
4143         //Roo.log(ael);
4144         
4145         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4146             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4147             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4148                 return; // ignore... - it's a 'hash' to another page.
4149             }
4150             
4151             e.preventDefault();
4152             this.scrollToElement(e);
4153         }
4154         
4155         
4156         var p =  this.parent();
4157    
4158         if (['tabs','pills'].indexOf(p.type)!==-1) {
4159             if (typeof(p.setActiveItem) !== 'undefined') {
4160                 p.setActiveItem(this);
4161             }
4162         }
4163         
4164         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4165         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4166             // remove the collapsed menu expand...
4167             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4168         }
4169     },
4170     
4171     isActive: function () {
4172         return this.active
4173     },
4174     setActive : function(state, fire, is_was_active)
4175     {
4176         if (this.active && !state & this.navId) {
4177             this.was_active = true;
4178             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4179             if (nv) {
4180                 nv.clearWasActive(this);
4181             }
4182             
4183         }
4184         this.active = state;
4185         
4186         if (!state ) {
4187             this.el.removeClass('active');
4188         } else if (!this.el.hasClass('active')) {
4189             this.el.addClass('active');
4190         }
4191         if (fire) {
4192             this.fireEvent('changed', this, state);
4193         }
4194         
4195         // show a panel if it's registered and related..
4196         
4197         if (!this.navId || !this.tabId || !state || is_was_active) {
4198             return;
4199         }
4200         
4201         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4202         if (!tg) {
4203             return;
4204         }
4205         var pan = tg.getPanelByName(this.tabId);
4206         if (!pan) {
4207             return;
4208         }
4209         // if we can not flip to new panel - go back to old nav highlight..
4210         if (false == tg.showPanel(pan)) {
4211             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4212             if (nv) {
4213                 var onav = nv.getWasActive();
4214                 if (onav) {
4215                     onav.setActive(true, false, true);
4216                 }
4217             }
4218             
4219         }
4220         
4221         
4222         
4223     },
4224      // this should not be here...
4225     setDisabled : function(state)
4226     {
4227         this.disabled = state;
4228         if (!state ) {
4229             this.el.removeClass('disabled');
4230         } else if (!this.el.hasClass('disabled')) {
4231             this.el.addClass('disabled');
4232         }
4233         
4234     },
4235     
4236     /**
4237      * Fetch the element to display the tooltip on.
4238      * @return {Roo.Element} defaults to this.el
4239      */
4240     tooltipEl : function()
4241     {
4242         return this.el.select('' + this.tagtype + '', true).first();
4243     },
4244     
4245     scrollToElement : function(e)
4246     {
4247         var c = document.body;
4248         
4249         /*
4250          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4251          */
4252         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4253             c = document.documentElement;
4254         }
4255         
4256         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4257         
4258         if(!target){
4259             return;
4260         }
4261
4262         var o = target.calcOffsetsTo(c);
4263         
4264         var options = {
4265             target : target,
4266             value : o[1]
4267         }
4268         
4269         this.fireEvent('scrollto', this, options, e);
4270         
4271         Roo.get(c).scrollTo('top', options.value, true);
4272         
4273         return;
4274     }
4275 });
4276  
4277
4278  /*
4279  * - LGPL
4280  *
4281  * sidebar item
4282  *
4283  *  li
4284  *    <span> icon </span>
4285  *    <span> text </span>
4286  *    <span>badge </span>
4287  */
4288
4289 /**
4290  * @class Roo.bootstrap.NavSidebarItem
4291  * @extends Roo.bootstrap.NavItem
4292  * Bootstrap Navbar.NavSidebarItem class
4293  * @constructor
4294  * Create a new Navbar Button
4295  * @param {Object} config The config object
4296  */
4297 Roo.bootstrap.NavSidebarItem = function(config){
4298     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4299     this.addEvents({
4300         // raw events
4301         /**
4302          * @event click
4303          * The raw click event for the entire grid.
4304          * @param {Roo.EventObject} e
4305          */
4306         "click" : true,
4307          /**
4308             * @event changed
4309             * Fires when the active item active state changes
4310             * @param {Roo.bootstrap.NavSidebarItem} this
4311             * @param {boolean} state the new state
4312              
4313          */
4314         'changed': true
4315     });
4316    
4317 };
4318
4319 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4320     
4321     
4322     getAutoCreate : function(){
4323         
4324         
4325         var a = {
4326                 tag: 'a',
4327                 href : this.href || '#',
4328                 cls: '',
4329                 html : '',
4330                 cn : []
4331         };
4332         var cfg = {
4333             tag: 'li',
4334             cls: '',
4335             cn: [ a ]
4336         }
4337         var span = {
4338             tag: 'span',
4339             html : this.html || ''
4340         }
4341         
4342         
4343         if (this.active) {
4344             cfg.cls += ' active';
4345         }
4346         
4347         // left icon..
4348         if (this.glyphicon || this.icon) {
4349             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4350             a.cn.push({ tag : 'i', cls : c }) ;
4351         }
4352         // html..
4353         a.cn.push(span);
4354         // then badge..
4355         if (this.badge !== '') {
4356             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
4357         }
4358         // fi
4359         if (this.menu) {
4360             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4361             a.cls += 'dropdown-toggle treeview' ;
4362             
4363         }
4364         
4365         
4366         
4367         return cfg;
4368          
4369            
4370     }
4371    
4372      
4373  
4374 });
4375  
4376
4377  /*
4378  * - LGPL
4379  *
4380  * row
4381  * 
4382  */
4383
4384 /**
4385  * @class Roo.bootstrap.Row
4386  * @extends Roo.bootstrap.Component
4387  * Bootstrap Row class (contains columns...)
4388  * 
4389  * @constructor
4390  * Create a new Row
4391  * @param {Object} config The config object
4392  */
4393
4394 Roo.bootstrap.Row = function(config){
4395     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4396 };
4397
4398 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4399     
4400     getAutoCreate : function(){
4401        return {
4402             cls: 'row clearfix'
4403        };
4404     }
4405     
4406     
4407 });
4408
4409  
4410
4411  /*
4412  * - LGPL
4413  *
4414  * element
4415  * 
4416  */
4417
4418 /**
4419  * @class Roo.bootstrap.Element
4420  * @extends Roo.bootstrap.Component
4421  * Bootstrap Element class
4422  * @cfg {String} html contents of the element
4423  * @cfg {String} tag tag of the element
4424  * @cfg {String} cls class of the element
4425  * @cfg {Boolean} preventDefault (true|false) default false
4426  * @cfg {Boolean} clickable (true|false) default false
4427  * 
4428  * @constructor
4429  * Create a new Element
4430  * @param {Object} config The config object
4431  */
4432
4433 Roo.bootstrap.Element = function(config){
4434     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4435     
4436     this.addEvents({
4437         // raw events
4438         /**
4439          * @event click
4440          * When a element is chick
4441          * @param {Roo.bootstrap.Element} this
4442          * @param {Roo.EventObject} e
4443          */
4444         "click" : true
4445     });
4446 };
4447
4448 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4449     
4450     tag: 'div',
4451     cls: '',
4452     html: '',
4453     preventDefault: false, 
4454     clickable: false,
4455     
4456     getAutoCreate : function(){
4457         
4458         var cfg = {
4459             tag: this.tag,
4460             cls: this.cls,
4461             html: this.html
4462         }
4463         
4464         return cfg;
4465     },
4466     
4467     initEvents: function() 
4468     {
4469         Roo.bootstrap.Element.superclass.initEvents.call(this);
4470         
4471         if(this.clickable){
4472             this.el.on('click', this.onClick, this);
4473         }
4474         
4475     },
4476     
4477     onClick : function(e)
4478     {
4479         if(this.preventDefault){
4480             e.preventDefault();
4481         }
4482         
4483         this.fireEvent('click', this, e);
4484     },
4485     
4486     getValue : function()
4487     {
4488         return this.el.dom.innerHTML;
4489     },
4490     
4491     setValue : function(value)
4492     {
4493         this.el.dom.innerHTML = value;
4494     }
4495    
4496 });
4497
4498  
4499
4500  /*
4501  * - LGPL
4502  *
4503  * pagination
4504  * 
4505  */
4506
4507 /**
4508  * @class Roo.bootstrap.Pagination
4509  * @extends Roo.bootstrap.Component
4510  * Bootstrap Pagination class
4511  * @cfg {String} size xs | sm | md | lg
4512  * @cfg {Boolean} inverse false | true
4513  * 
4514  * @constructor
4515  * Create a new Pagination
4516  * @param {Object} config The config object
4517  */
4518
4519 Roo.bootstrap.Pagination = function(config){
4520     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4521 };
4522
4523 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4524     
4525     cls: false,
4526     size: false,
4527     inverse: false,
4528     
4529     getAutoCreate : function(){
4530         var cfg = {
4531             tag: 'ul',
4532                 cls: 'pagination'
4533         };
4534         if (this.inverse) {
4535             cfg.cls += ' inverse';
4536         }
4537         if (this.html) {
4538             cfg.html=this.html;
4539         }
4540         if (this.cls) {
4541             cfg.cls += " " + this.cls;
4542         }
4543         return cfg;
4544     }
4545    
4546 });
4547
4548  
4549
4550  /*
4551  * - LGPL
4552  *
4553  * Pagination item
4554  * 
4555  */
4556
4557
4558 /**
4559  * @class Roo.bootstrap.PaginationItem
4560  * @extends Roo.bootstrap.Component
4561  * Bootstrap PaginationItem class
4562  * @cfg {String} html text
4563  * @cfg {String} href the link
4564  * @cfg {Boolean} preventDefault (true | false) default true
4565  * @cfg {Boolean} active (true | false) default false
4566  * @cfg {Boolean} disabled default false
4567  * 
4568  * 
4569  * @constructor
4570  * Create a new PaginationItem
4571  * @param {Object} config The config object
4572  */
4573
4574
4575 Roo.bootstrap.PaginationItem = function(config){
4576     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4577     this.addEvents({
4578         // raw events
4579         /**
4580          * @event click
4581          * The raw click event for the entire grid.
4582          * @param {Roo.EventObject} e
4583          */
4584         "click" : true
4585     });
4586 };
4587
4588 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4589     
4590     href : false,
4591     html : false,
4592     preventDefault: true,
4593     active : false,
4594     cls : false,
4595     disabled: false,
4596     
4597     getAutoCreate : function(){
4598         var cfg= {
4599             tag: 'li',
4600             cn: [
4601                 {
4602                     tag : 'a',
4603                     href : this.href ? this.href : '#',
4604                     html : this.html ? this.html : ''
4605                 }
4606             ]
4607         };
4608         
4609         if(this.cls){
4610             cfg.cls = this.cls;
4611         }
4612         
4613         if(this.disabled){
4614             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4615         }
4616         
4617         if(this.active){
4618             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4619         }
4620         
4621         return cfg;
4622     },
4623     
4624     initEvents: function() {
4625         
4626         this.el.on('click', this.onClick, this);
4627         
4628     },
4629     onClick : function(e)
4630     {
4631         Roo.log('PaginationItem on click ');
4632         if(this.preventDefault){
4633             e.preventDefault();
4634         }
4635         
4636         if(this.disabled){
4637             return;
4638         }
4639         
4640         this.fireEvent('click', this, e);
4641     }
4642    
4643 });
4644
4645  
4646
4647  /*
4648  * - LGPL
4649  *
4650  * slider
4651  * 
4652  */
4653
4654
4655 /**
4656  * @class Roo.bootstrap.Slider
4657  * @extends Roo.bootstrap.Component
4658  * Bootstrap Slider class
4659  *    
4660  * @constructor
4661  * Create a new Slider
4662  * @param {Object} config The config object
4663  */
4664
4665 Roo.bootstrap.Slider = function(config){
4666     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4667 };
4668
4669 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4670     
4671     getAutoCreate : function(){
4672         
4673         var cfg = {
4674             tag: 'div',
4675             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4676             cn: [
4677                 {
4678                     tag: 'a',
4679                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4680                 }
4681             ]
4682         }
4683         
4684         return cfg;
4685     }
4686    
4687 });
4688
4689  /*
4690  * Based on:
4691  * Ext JS Library 1.1.1
4692  * Copyright(c) 2006-2007, Ext JS, LLC.
4693  *
4694  * Originally Released Under LGPL - original licence link has changed is not relivant.
4695  *
4696  * Fork - LGPL
4697  * <script type="text/javascript">
4698  */
4699  
4700
4701 /**
4702  * @class Roo.grid.ColumnModel
4703  * @extends Roo.util.Observable
4704  * This is the default implementation of a ColumnModel used by the Grid. It defines
4705  * the columns in the grid.
4706  * <br>Usage:<br>
4707  <pre><code>
4708  var colModel = new Roo.grid.ColumnModel([
4709         {header: "Ticker", width: 60, sortable: true, locked: true},
4710         {header: "Company Name", width: 150, sortable: true},
4711         {header: "Market Cap.", width: 100, sortable: true},
4712         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4713         {header: "Employees", width: 100, sortable: true, resizable: false}
4714  ]);
4715  </code></pre>
4716  * <p>
4717  
4718  * The config options listed for this class are options which may appear in each
4719  * individual column definition.
4720  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4721  * @constructor
4722  * @param {Object} config An Array of column config objects. See this class's
4723  * config objects for details.
4724 */
4725 Roo.grid.ColumnModel = function(config){
4726         /**
4727      * The config passed into the constructor
4728      */
4729     this.config = config;
4730     this.lookup = {};
4731
4732     // if no id, create one
4733     // if the column does not have a dataIndex mapping,
4734     // map it to the order it is in the config
4735     for(var i = 0, len = config.length; i < len; i++){
4736         var c = config[i];
4737         if(typeof c.dataIndex == "undefined"){
4738             c.dataIndex = i;
4739         }
4740         if(typeof c.renderer == "string"){
4741             c.renderer = Roo.util.Format[c.renderer];
4742         }
4743         if(typeof c.id == "undefined"){
4744             c.id = Roo.id();
4745         }
4746         if(c.editor && c.editor.xtype){
4747             c.editor  = Roo.factory(c.editor, Roo.grid);
4748         }
4749         if(c.editor && c.editor.isFormField){
4750             c.editor = new Roo.grid.GridEditor(c.editor);
4751         }
4752         this.lookup[c.id] = c;
4753     }
4754
4755     /**
4756      * The width of columns which have no width specified (defaults to 100)
4757      * @type Number
4758      */
4759     this.defaultWidth = 100;
4760
4761     /**
4762      * Default sortable of columns which have no sortable specified (defaults to false)
4763      * @type Boolean
4764      */
4765     this.defaultSortable = false;
4766
4767     this.addEvents({
4768         /**
4769              * @event widthchange
4770              * Fires when the width of a column changes.
4771              * @param {ColumnModel} this
4772              * @param {Number} columnIndex The column index
4773              * @param {Number} newWidth The new width
4774              */
4775             "widthchange": true,
4776         /**
4777              * @event headerchange
4778              * Fires when the text of a header changes.
4779              * @param {ColumnModel} this
4780              * @param {Number} columnIndex The column index
4781              * @param {Number} newText The new header text
4782              */
4783             "headerchange": true,
4784         /**
4785              * @event hiddenchange
4786              * Fires when a column is hidden or "unhidden".
4787              * @param {ColumnModel} this
4788              * @param {Number} columnIndex The column index
4789              * @param {Boolean} hidden true if hidden, false otherwise
4790              */
4791             "hiddenchange": true,
4792             /**
4793          * @event columnmoved
4794          * Fires when a column is moved.
4795          * @param {ColumnModel} this
4796          * @param {Number} oldIndex
4797          * @param {Number} newIndex
4798          */
4799         "columnmoved" : true,
4800         /**
4801          * @event columlockchange
4802          * Fires when a column's locked state is changed
4803          * @param {ColumnModel} this
4804          * @param {Number} colIndex
4805          * @param {Boolean} locked true if locked
4806          */
4807         "columnlockchange" : true
4808     });
4809     Roo.grid.ColumnModel.superclass.constructor.call(this);
4810 };
4811 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4812     /**
4813      * @cfg {String} header The header text to display in the Grid view.
4814      */
4815     /**
4816      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4817      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4818      * specified, the column's index is used as an index into the Record's data Array.
4819      */
4820     /**
4821      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4822      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4823      */
4824     /**
4825      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4826      * Defaults to the value of the {@link #defaultSortable} property.
4827      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4828      */
4829     /**
4830      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4831      */
4832     /**
4833      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4834      */
4835     /**
4836      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4837      */
4838     /**
4839      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4840      */
4841     /**
4842      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4843      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4844      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4845      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4846      */
4847        /**
4848      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4849      */
4850     /**
4851      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4852      */
4853     /**
4854      * @cfg {String} cursor (Optional)
4855      */
4856     /**
4857      * @cfg {String} tooltip (Optional)
4858      */
4859     /**
4860      * Returns the id of the column at the specified index.
4861      * @param {Number} index The column index
4862      * @return {String} the id
4863      */
4864     getColumnId : function(index){
4865         return this.config[index].id;
4866     },
4867
4868     /**
4869      * Returns the column for a specified id.
4870      * @param {String} id The column id
4871      * @return {Object} the column
4872      */
4873     getColumnById : function(id){
4874         return this.lookup[id];
4875     },
4876
4877     
4878     /**
4879      * Returns the column for a specified dataIndex.
4880      * @param {String} dataIndex The column dataIndex
4881      * @return {Object|Boolean} the column or false if not found
4882      */
4883     getColumnByDataIndex: function(dataIndex){
4884         var index = this.findColumnIndex(dataIndex);
4885         return index > -1 ? this.config[index] : false;
4886     },
4887     
4888     /**
4889      * Returns the index for a specified column id.
4890      * @param {String} id The column id
4891      * @return {Number} the index, or -1 if not found
4892      */
4893     getIndexById : function(id){
4894         for(var i = 0, len = this.config.length; i < len; i++){
4895             if(this.config[i].id == id){
4896                 return i;
4897             }
4898         }
4899         return -1;
4900     },
4901     
4902     /**
4903      * Returns the index for a specified column dataIndex.
4904      * @param {String} dataIndex The column dataIndex
4905      * @return {Number} the index, or -1 if not found
4906      */
4907     
4908     findColumnIndex : function(dataIndex){
4909         for(var i = 0, len = this.config.length; i < len; i++){
4910             if(this.config[i].dataIndex == dataIndex){
4911                 return i;
4912             }
4913         }
4914         return -1;
4915     },
4916     
4917     
4918     moveColumn : function(oldIndex, newIndex){
4919         var c = this.config[oldIndex];
4920         this.config.splice(oldIndex, 1);
4921         this.config.splice(newIndex, 0, c);
4922         this.dataMap = null;
4923         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4924     },
4925
4926     isLocked : function(colIndex){
4927         return this.config[colIndex].locked === true;
4928     },
4929
4930     setLocked : function(colIndex, value, suppressEvent){
4931         if(this.isLocked(colIndex) == value){
4932             return;
4933         }
4934         this.config[colIndex].locked = value;
4935         if(!suppressEvent){
4936             this.fireEvent("columnlockchange", this, colIndex, value);
4937         }
4938     },
4939
4940     getTotalLockedWidth : function(){
4941         var totalWidth = 0;
4942         for(var i = 0; i < this.config.length; i++){
4943             if(this.isLocked(i) && !this.isHidden(i)){
4944                 this.totalWidth += this.getColumnWidth(i);
4945             }
4946         }
4947         return totalWidth;
4948     },
4949
4950     getLockedCount : function(){
4951         for(var i = 0, len = this.config.length; i < len; i++){
4952             if(!this.isLocked(i)){
4953                 return i;
4954             }
4955         }
4956     },
4957
4958     /**
4959      * Returns the number of columns.
4960      * @return {Number}
4961      */
4962     getColumnCount : function(visibleOnly){
4963         if(visibleOnly === true){
4964             var c = 0;
4965             for(var i = 0, len = this.config.length; i < len; i++){
4966                 if(!this.isHidden(i)){
4967                     c++;
4968                 }
4969             }
4970             return c;
4971         }
4972         return this.config.length;
4973     },
4974
4975     /**
4976      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4977      * @param {Function} fn
4978      * @param {Object} scope (optional)
4979      * @return {Array} result
4980      */
4981     getColumnsBy : function(fn, scope){
4982         var r = [];
4983         for(var i = 0, len = this.config.length; i < len; i++){
4984             var c = this.config[i];
4985             if(fn.call(scope||this, c, i) === true){
4986                 r[r.length] = c;
4987             }
4988         }
4989         return r;
4990     },
4991
4992     /**
4993      * Returns true if the specified column is sortable.
4994      * @param {Number} col The column index
4995      * @return {Boolean}
4996      */
4997     isSortable : function(col){
4998         if(typeof this.config[col].sortable == "undefined"){
4999             return this.defaultSortable;
5000         }
5001         return this.config[col].sortable;
5002     },
5003
5004     /**
5005      * Returns the rendering (formatting) function defined for the column.
5006      * @param {Number} col The column index.
5007      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5008      */
5009     getRenderer : function(col){
5010         if(!this.config[col].renderer){
5011             return Roo.grid.ColumnModel.defaultRenderer;
5012         }
5013         return this.config[col].renderer;
5014     },
5015
5016     /**
5017      * Sets the rendering (formatting) function for a column.
5018      * @param {Number} col The column index
5019      * @param {Function} fn The function to use to process the cell's raw data
5020      * to return HTML markup for the grid view. The render function is called with
5021      * the following parameters:<ul>
5022      * <li>Data value.</li>
5023      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5024      * <li>css A CSS style string to apply to the table cell.</li>
5025      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5026      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5027      * <li>Row index</li>
5028      * <li>Column index</li>
5029      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5030      */
5031     setRenderer : function(col, fn){
5032         this.config[col].renderer = fn;
5033     },
5034
5035     /**
5036      * Returns the width for the specified column.
5037      * @param {Number} col The column index
5038      * @return {Number}
5039      */
5040     getColumnWidth : function(col){
5041         return this.config[col].width * 1 || this.defaultWidth;
5042     },
5043
5044     /**
5045      * Sets the width for a column.
5046      * @param {Number} col The column index
5047      * @param {Number} width The new width
5048      */
5049     setColumnWidth : function(col, width, suppressEvent){
5050         this.config[col].width = width;
5051         this.totalWidth = null;
5052         if(!suppressEvent){
5053              this.fireEvent("widthchange", this, col, width);
5054         }
5055     },
5056
5057     /**
5058      * Returns the total width of all columns.
5059      * @param {Boolean} includeHidden True to include hidden column widths
5060      * @return {Number}
5061      */
5062     getTotalWidth : function(includeHidden){
5063         if(!this.totalWidth){
5064             this.totalWidth = 0;
5065             for(var i = 0, len = this.config.length; i < len; i++){
5066                 if(includeHidden || !this.isHidden(i)){
5067                     this.totalWidth += this.getColumnWidth(i);
5068                 }
5069             }
5070         }
5071         return this.totalWidth;
5072     },
5073
5074     /**
5075      * Returns the header for the specified column.
5076      * @param {Number} col The column index
5077      * @return {String}
5078      */
5079     getColumnHeader : function(col){
5080         return this.config[col].header;
5081     },
5082
5083     /**
5084      * Sets the header for a column.
5085      * @param {Number} col The column index
5086      * @param {String} header The new header
5087      */
5088     setColumnHeader : function(col, header){
5089         this.config[col].header = header;
5090         this.fireEvent("headerchange", this, col, header);
5091     },
5092
5093     /**
5094      * Returns the tooltip for the specified column.
5095      * @param {Number} col The column index
5096      * @return {String}
5097      */
5098     getColumnTooltip : function(col){
5099             return this.config[col].tooltip;
5100     },
5101     /**
5102      * Sets the tooltip for a column.
5103      * @param {Number} col The column index
5104      * @param {String} tooltip The new tooltip
5105      */
5106     setColumnTooltip : function(col, tooltip){
5107             this.config[col].tooltip = tooltip;
5108     },
5109
5110     /**
5111      * Returns the dataIndex for the specified column.
5112      * @param {Number} col The column index
5113      * @return {Number}
5114      */
5115     getDataIndex : function(col){
5116         return this.config[col].dataIndex;
5117     },
5118
5119     /**
5120      * Sets the dataIndex for a column.
5121      * @param {Number} col The column index
5122      * @param {Number} dataIndex The new dataIndex
5123      */
5124     setDataIndex : function(col, dataIndex){
5125         this.config[col].dataIndex = dataIndex;
5126     },
5127
5128     
5129     
5130     /**
5131      * Returns true if the cell is editable.
5132      * @param {Number} colIndex The column index
5133      * @param {Number} rowIndex The row index
5134      * @return {Boolean}
5135      */
5136     isCellEditable : function(colIndex, rowIndex){
5137         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5138     },
5139
5140     /**
5141      * Returns the editor defined for the cell/column.
5142      * return false or null to disable editing.
5143      * @param {Number} colIndex The column index
5144      * @param {Number} rowIndex The row index
5145      * @return {Object}
5146      */
5147     getCellEditor : function(colIndex, rowIndex){
5148         return this.config[colIndex].editor;
5149     },
5150
5151     /**
5152      * Sets if a column is editable.
5153      * @param {Number} col The column index
5154      * @param {Boolean} editable True if the column is editable
5155      */
5156     setEditable : function(col, editable){
5157         this.config[col].editable = editable;
5158     },
5159
5160
5161     /**
5162      * Returns true if the column is hidden.
5163      * @param {Number} colIndex The column index
5164      * @return {Boolean}
5165      */
5166     isHidden : function(colIndex){
5167         return this.config[colIndex].hidden;
5168     },
5169
5170
5171     /**
5172      * Returns true if the column width cannot be changed
5173      */
5174     isFixed : function(colIndex){
5175         return this.config[colIndex].fixed;
5176     },
5177
5178     /**
5179      * Returns true if the column can be resized
5180      * @return {Boolean}
5181      */
5182     isResizable : function(colIndex){
5183         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5184     },
5185     /**
5186      * Sets if a column is hidden.
5187      * @param {Number} colIndex The column index
5188      * @param {Boolean} hidden True if the column is hidden
5189      */
5190     setHidden : function(colIndex, hidden){
5191         this.config[colIndex].hidden = hidden;
5192         this.totalWidth = null;
5193         this.fireEvent("hiddenchange", this, colIndex, hidden);
5194     },
5195
5196     /**
5197      * Sets the editor for a column.
5198      * @param {Number} col The column index
5199      * @param {Object} editor The editor object
5200      */
5201     setEditor : function(col, editor){
5202         this.config[col].editor = editor;
5203     }
5204 });
5205
5206 Roo.grid.ColumnModel.defaultRenderer = function(value){
5207         if(typeof value == "string" && value.length < 1){
5208             return "&#160;";
5209         }
5210         return value;
5211 };
5212
5213 // Alias for backwards compatibility
5214 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5215 /*
5216  * Based on:
5217  * Ext JS Library 1.1.1
5218  * Copyright(c) 2006-2007, Ext JS, LLC.
5219  *
5220  * Originally Released Under LGPL - original licence link has changed is not relivant.
5221  *
5222  * Fork - LGPL
5223  * <script type="text/javascript">
5224  */
5225  
5226 /**
5227  * @class Roo.LoadMask
5228  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5229  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5230  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5231  * element's UpdateManager load indicator and will be destroyed after the initial load.
5232  * @constructor
5233  * Create a new LoadMask
5234  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5235  * @param {Object} config The config object
5236  */
5237 Roo.LoadMask = function(el, config){
5238     this.el = Roo.get(el);
5239     Roo.apply(this, config);
5240     if(this.store){
5241         this.store.on('beforeload', this.onBeforeLoad, this);
5242         this.store.on('load', this.onLoad, this);
5243         this.store.on('loadexception', this.onLoadException, this);
5244         this.removeMask = false;
5245     }else{
5246         var um = this.el.getUpdateManager();
5247         um.showLoadIndicator = false; // disable the default indicator
5248         um.on('beforeupdate', this.onBeforeLoad, this);
5249         um.on('update', this.onLoad, this);
5250         um.on('failure', this.onLoad, this);
5251         this.removeMask = true;
5252     }
5253 };
5254
5255 Roo.LoadMask.prototype = {
5256     /**
5257      * @cfg {Boolean} removeMask
5258      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5259      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5260      */
5261     /**
5262      * @cfg {String} msg
5263      * The text to display in a centered loading message box (defaults to 'Loading...')
5264      */
5265     msg : 'Loading...',
5266     /**
5267      * @cfg {String} msgCls
5268      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5269      */
5270     msgCls : 'x-mask-loading',
5271
5272     /**
5273      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5274      * @type Boolean
5275      */
5276     disabled: false,
5277
5278     /**
5279      * Disables the mask to prevent it from being displayed
5280      */
5281     disable : function(){
5282        this.disabled = true;
5283     },
5284
5285     /**
5286      * Enables the mask so that it can be displayed
5287      */
5288     enable : function(){
5289         this.disabled = false;
5290     },
5291     
5292     onLoadException : function()
5293     {
5294         Roo.log(arguments);
5295         
5296         if (typeof(arguments[3]) != 'undefined') {
5297             Roo.MessageBox.alert("Error loading",arguments[3]);
5298         } 
5299         /*
5300         try {
5301             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5302                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5303             }   
5304         } catch(e) {
5305             
5306         }
5307         */
5308     
5309         
5310         
5311         this.el.unmask(this.removeMask);
5312     },
5313     // private
5314     onLoad : function()
5315     {
5316         this.el.unmask(this.removeMask);
5317     },
5318
5319     // private
5320     onBeforeLoad : function(){
5321         if(!this.disabled){
5322             this.el.mask(this.msg, this.msgCls);
5323         }
5324     },
5325
5326     // private
5327     destroy : function(){
5328         if(this.store){
5329             this.store.un('beforeload', this.onBeforeLoad, this);
5330             this.store.un('load', this.onLoad, this);
5331             this.store.un('loadexception', this.onLoadException, this);
5332         }else{
5333             var um = this.el.getUpdateManager();
5334             um.un('beforeupdate', this.onBeforeLoad, this);
5335             um.un('update', this.onLoad, this);
5336             um.un('failure', this.onLoad, this);
5337         }
5338     }
5339 };/*
5340  * - LGPL
5341  *
5342  * table
5343  * 
5344  */
5345
5346 /**
5347  * @class Roo.bootstrap.Table
5348  * @extends Roo.bootstrap.Component
5349  * Bootstrap Table class
5350  * @cfg {String} cls table class
5351  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5352  * @cfg {String} bgcolor Specifies the background color for a table
5353  * @cfg {Number} border Specifies whether the table cells should have borders or not
5354  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5355  * @cfg {Number} cellspacing Specifies the space between cells
5356  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5357  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5358  * @cfg {String} sortable Specifies that the table should be sortable
5359  * @cfg {String} summary Specifies a summary of the content of a table
5360  * @cfg {Number} width Specifies the width of a table
5361  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5362  * 
5363  * @cfg {boolean} striped Should the rows be alternative striped
5364  * @cfg {boolean} bordered Add borders to the table
5365  * @cfg {boolean} hover Add hover highlighting
5366  * @cfg {boolean} condensed Format condensed
5367  * @cfg {boolean} responsive Format condensed
5368  * @cfg {Boolean} loadMask (true|false) default false
5369  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
5370  * @cfg {Boolean} thead (true|false) generate thead, default true
5371  * @cfg {Boolean} RowSelection (true|false) default false
5372  * @cfg {Boolean} CellSelection (true|false) default false
5373  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5374  
5375  * 
5376  * @constructor
5377  * Create a new Table
5378  * @param {Object} config The config object
5379  */
5380
5381 Roo.bootstrap.Table = function(config){
5382     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5383     
5384     if (this.sm) {
5385         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5386         this.sm = this.selModel;
5387         this.sm.xmodule = this.xmodule || false;
5388     }
5389     if (this.cm && typeof(this.cm.config) == 'undefined') {
5390         this.colModel = new Roo.grid.ColumnModel(this.cm);
5391         this.cm = this.colModel;
5392         this.cm.xmodule = this.xmodule || false;
5393     }
5394     if (this.store) {
5395         this.store= Roo.factory(this.store, Roo.data);
5396         this.ds = this.store;
5397         this.ds.xmodule = this.xmodule || false;
5398          
5399     }
5400     if (this.footer && this.store) {
5401         this.footer.dataSource = this.ds;
5402         this.footer = Roo.factory(this.footer);
5403     }
5404     
5405     /** @private */
5406     this.addEvents({
5407         /**
5408          * @event cellclick
5409          * Fires when a cell is clicked
5410          * @param {Roo.bootstrap.Table} this
5411          * @param {Roo.Element} el
5412          * @param {Number} rowIndex
5413          * @param {Number} columnIndex
5414          * @param {Roo.EventObject} e
5415          */
5416         "cellclick" : true,
5417         /**
5418          * @event celldblclick
5419          * Fires when a cell is double clicked
5420          * @param {Roo.bootstrap.Table} this
5421          * @param {Roo.Element} el
5422          * @param {Number} rowIndex
5423          * @param {Number} columnIndex
5424          * @param {Roo.EventObject} e
5425          */
5426         "celldblclick" : true,
5427         /**
5428          * @event rowclick
5429          * Fires when a row is clicked
5430          * @param {Roo.bootstrap.Table} this
5431          * @param {Roo.Element} el
5432          * @param {Number} rowIndex
5433          * @param {Roo.EventObject} e
5434          */
5435         "rowclick" : true,
5436         /**
5437          * @event rowdblclick
5438          * Fires when a row is double clicked
5439          * @param {Roo.bootstrap.Table} this
5440          * @param {Roo.Element} el
5441          * @param {Number} rowIndex
5442          * @param {Roo.EventObject} e
5443          */
5444         "rowdblclick" : true,
5445         /**
5446          * @event mouseover
5447          * Fires when a mouseover occur
5448          * @param {Roo.bootstrap.Table} this
5449          * @param {Roo.Element} el
5450          * @param {Number} rowIndex
5451          * @param {Number} columnIndex
5452          * @param {Roo.EventObject} e
5453          */
5454         "mouseover" : true,
5455         /**
5456          * @event mouseout
5457          * Fires when a mouseout occur
5458          * @param {Roo.bootstrap.Table} this
5459          * @param {Roo.Element} el
5460          * @param {Number} rowIndex
5461          * @param {Number} columnIndex
5462          * @param {Roo.EventObject} e
5463          */
5464         "mouseout" : true,
5465         /**
5466          * @event rowclass
5467          * Fires when a row is rendered, so you can change add a style to it.
5468          * @param {Roo.bootstrap.Table} this
5469          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5470          */
5471         'rowclass' : true,
5472           /**
5473          * @event rowsrendered
5474          * Fires when all the  rows have been rendered
5475          * @param {Roo.bootstrap.Table} this
5476          */
5477         'rowsrendered' : true
5478         
5479     });
5480 };
5481
5482 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5483     
5484     cls: false,
5485     align: false,
5486     bgcolor: false,
5487     border: false,
5488     cellpadding: false,
5489     cellspacing: false,
5490     frame: false,
5491     rules: false,
5492     sortable: false,
5493     summary: false,
5494     width: false,
5495     striped : false,
5496     bordered: false,
5497     hover:  false,
5498     condensed : false,
5499     responsive : false,
5500     sm : false,
5501     cm : false,
5502     store : false,
5503     loadMask : false,
5504     tfoot : true,
5505     thead : true,
5506     RowSelection : false,
5507     CellSelection : false,
5508     layout : false,
5509     
5510     // Roo.Element - the tbody
5511     mainBody: false, 
5512     
5513     getAutoCreate : function(){
5514         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5515         
5516         cfg = {
5517             tag: 'table',
5518             cls : 'table',
5519             cn : []
5520         }
5521             
5522         if (this.striped) {
5523             cfg.cls += ' table-striped';
5524         }
5525         
5526         if (this.hover) {
5527             cfg.cls += ' table-hover';
5528         }
5529         if (this.bordered) {
5530             cfg.cls += ' table-bordered';
5531         }
5532         if (this.condensed) {
5533             cfg.cls += ' table-condensed';
5534         }
5535         if (this.responsive) {
5536             cfg.cls += ' table-responsive';
5537         }
5538         
5539         if (this.cls) {
5540             cfg.cls+=  ' ' +this.cls;
5541         }
5542         
5543         // this lot should be simplifed...
5544         
5545         if (this.align) {
5546             cfg.align=this.align;
5547         }
5548         if (this.bgcolor) {
5549             cfg.bgcolor=this.bgcolor;
5550         }
5551         if (this.border) {
5552             cfg.border=this.border;
5553         }
5554         if (this.cellpadding) {
5555             cfg.cellpadding=this.cellpadding;
5556         }
5557         if (this.cellspacing) {
5558             cfg.cellspacing=this.cellspacing;
5559         }
5560         if (this.frame) {
5561             cfg.frame=this.frame;
5562         }
5563         if (this.rules) {
5564             cfg.rules=this.rules;
5565         }
5566         if (this.sortable) {
5567             cfg.sortable=this.sortable;
5568         }
5569         if (this.summary) {
5570             cfg.summary=this.summary;
5571         }
5572         if (this.width) {
5573             cfg.width=this.width;
5574         }
5575         if (this.layout) {
5576             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5577         }
5578         
5579         if(this.store || this.cm){
5580             if(this.thead){
5581                 cfg.cn.push(this.renderHeader());
5582             }
5583             
5584             cfg.cn.push(this.renderBody());
5585             
5586             if(this.tfoot){
5587                 cfg.cn.push(this.renderFooter());
5588             }
5589             
5590             cfg.cls+=  ' TableGrid';
5591         }
5592         
5593         return { cn : [ cfg ] };
5594     },
5595     
5596     initEvents : function()
5597     {   
5598         if(!this.store || !this.cm){
5599             return;
5600         }
5601         
5602         //Roo.log('initEvents with ds!!!!');
5603         
5604         this.mainBody = this.el.select('tbody', true).first();
5605         
5606         
5607         var _this = this;
5608         
5609         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5610             e.on('click', _this.sort, _this);
5611         });
5612         
5613         this.el.on("click", this.onClick, this);
5614         this.el.on("dblclick", this.onDblClick, this);
5615         
5616         // why is this done????? = it breaks dialogs??
5617         //this.parent().el.setStyle('position', 'relative');
5618         
5619         
5620         if (this.footer) {
5621             this.footer.parentId = this.id;
5622             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5623         }
5624         
5625         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5626         
5627         this.store.on('load', this.onLoad, this);
5628         this.store.on('beforeload', this.onBeforeLoad, this);
5629         this.store.on('update', this.onUpdate, this);
5630         this.store.on('add', this.onAdd, this);
5631         
5632     },
5633     
5634     onMouseover : function(e, el)
5635     {
5636         var cell = Roo.get(el);
5637         
5638         if(!cell){
5639             return;
5640         }
5641         
5642         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5643             cell = cell.findParent('td', false, true);
5644         }
5645         
5646         var row = cell.findParent('tr', false, true);
5647         var cellIndex = cell.dom.cellIndex;
5648         var rowIndex = row.dom.rowIndex - 1; // start from 0
5649         
5650         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5651         
5652     },
5653     
5654     onMouseout : function(e, el)
5655     {
5656         var cell = Roo.get(el);
5657         
5658         if(!cell){
5659             return;
5660         }
5661         
5662         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5663             cell = cell.findParent('td', false, true);
5664         }
5665         
5666         var row = cell.findParent('tr', false, true);
5667         var cellIndex = cell.dom.cellIndex;
5668         var rowIndex = row.dom.rowIndex - 1; // start from 0
5669         
5670         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5671         
5672     },
5673     
5674     onClick : function(e, el)
5675     {
5676         var cell = Roo.get(el);
5677         
5678         if(!cell || (!this.CellSelection && !this.RowSelection)){
5679             return;
5680         }
5681         
5682         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5683             cell = cell.findParent('td', false, true);
5684         }
5685         
5686         if(!cell || typeof(cell) == 'undefined'){
5687             return;
5688         }
5689         
5690         var row = cell.findParent('tr', false, true);
5691         
5692         if(!row || typeof(row) == 'undefined'){
5693             return;
5694         }
5695         
5696         var cellIndex = cell.dom.cellIndex;
5697         var rowIndex = this.getRowIndex(row);
5698         
5699         if(this.CellSelection){
5700             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5701         }
5702         
5703         if(this.RowSelection){
5704             this.fireEvent('rowclick', this, row, rowIndex, e);
5705         }
5706         
5707         
5708     },
5709     
5710     onDblClick : function(e,el)
5711     {
5712         var cell = Roo.get(el);
5713         
5714         if(!cell || (!this.CellSelection && !this.RowSelection)){
5715             return;
5716         }
5717         
5718         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5719             cell = cell.findParent('td', false, true);
5720         }
5721         
5722         if(!cell || typeof(cell) == 'undefined'){
5723             return;
5724         }
5725         
5726         var row = cell.findParent('tr', false, true);
5727         
5728         if(!row || typeof(row) == 'undefined'){
5729             return;
5730         }
5731         
5732         var cellIndex = cell.dom.cellIndex;
5733         var rowIndex = this.getRowIndex(row);
5734         
5735         if(this.CellSelection){
5736             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5737         }
5738         
5739         if(this.RowSelection){
5740             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5741         }
5742     },
5743     
5744     sort : function(e,el)
5745     {
5746         var col = Roo.get(el);
5747         
5748         if(!col.hasClass('sortable')){
5749             return;
5750         }
5751         
5752         var sort = col.attr('sort');
5753         var dir = 'ASC';
5754         
5755         if(col.hasClass('glyphicon-arrow-up')){
5756             dir = 'DESC';
5757         }
5758         
5759         this.store.sortInfo = {field : sort, direction : dir};
5760         
5761         if (this.footer) {
5762             Roo.log("calling footer first");
5763             this.footer.onClick('first');
5764         } else {
5765         
5766             this.store.load({ params : { start : 0 } });
5767         }
5768     },
5769     
5770     renderHeader : function()
5771     {
5772         var header = {
5773             tag: 'thead',
5774             cn : []
5775         };
5776         
5777         var cm = this.cm;
5778         
5779         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5780             
5781             var config = cm.config[i];
5782                     
5783             var c = {
5784                 tag: 'th',
5785                 style : '',
5786                 html: cm.getColumnHeader(i)
5787             };
5788             
5789             if(typeof(config.tooltip) != 'undefined'){
5790                 c.tooltip = config.tooltip;
5791             }
5792             
5793             if(typeof(config.colspan) != 'undefined'){
5794                 c.colspan = config.colspan;
5795             }
5796             
5797             if(typeof(config.hidden) != 'undefined' && config.hidden){
5798                 c.style += ' display:none;';
5799             }
5800             
5801             if(typeof(config.dataIndex) != 'undefined'){
5802                 c.sort = config.dataIndex;
5803             }
5804             
5805             if(typeof(config.sortable) != 'undefined' && config.sortable){
5806                 c.cls = 'sortable';
5807             }
5808             
5809             if(typeof(config.align) != 'undefined' && config.align.length){
5810                 c.style += ' text-align:' + config.align + ';';
5811             }
5812             
5813             if(typeof(config.width) != 'undefined'){
5814                 c.style += ' width:' + config.width + 'px;';
5815             }
5816             
5817             if(typeof(config.cls) != 'undefined'){
5818                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
5819             }
5820             
5821             header.cn.push(c)
5822         }
5823         
5824         return header;
5825     },
5826     
5827     renderBody : function()
5828     {
5829         var body = {
5830             tag: 'tbody',
5831             cn : [
5832                 {
5833                     tag: 'tr',
5834                     cn : [
5835                         {
5836                             tag : 'td',
5837                             colspan :  this.cm.getColumnCount()
5838                         }
5839                     ]
5840                 }
5841             ]
5842         };
5843         
5844         return body;
5845     },
5846     
5847     renderFooter : function()
5848     {
5849         var footer = {
5850             tag: 'tfoot',
5851             cn : [
5852                 {
5853                     tag: 'tr',
5854                     cn : [
5855                         {
5856                             tag : 'td',
5857                             colspan :  this.cm.getColumnCount()
5858                         }
5859                     ]
5860                 }
5861             ]
5862         };
5863         
5864         return footer;
5865     },
5866     
5867     
5868     
5869     onLoad : function()
5870     {
5871         Roo.log('ds onload');
5872         this.clear();
5873         
5874         var _this = this;
5875         var cm = this.cm;
5876         var ds = this.store;
5877         
5878         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5879             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5880             
5881             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5882                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5883             }
5884             
5885             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5886                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5887             }
5888         });
5889         
5890         var tbody =  this.mainBody;
5891               
5892         if(ds.getCount() > 0){
5893             ds.data.each(function(d,rowIndex){
5894                 var row =  this.renderRow(cm, ds, rowIndex);
5895                 
5896                 tbody.createChild(row);
5897                 
5898                 var _this = this;
5899                 
5900                 if(row.cellObjects.length){
5901                     Roo.each(row.cellObjects, function(r){
5902                         _this.renderCellObject(r);
5903                     })
5904                 }
5905                 
5906             }, this);
5907         }
5908         
5909         Roo.each(this.el.select('tbody td', true).elements, function(e){
5910             e.on('mouseover', _this.onMouseover, _this);
5911         });
5912         
5913         Roo.each(this.el.select('tbody td', true).elements, function(e){
5914             e.on('mouseout', _this.onMouseout, _this);
5915         });
5916         this.fireEvent('rowsrendered', this);
5917         //if(this.loadMask){
5918         //    this.maskEl.hide();
5919         //}
5920     },
5921     
5922     
5923     onUpdate : function(ds,record)
5924     {
5925         this.refreshRow(record);
5926     },
5927     
5928     onRemove : function(ds, record, index, isUpdate){
5929         if(isUpdate !== true){
5930             this.fireEvent("beforerowremoved", this, index, record);
5931         }
5932         var bt = this.mainBody.dom;
5933         
5934         var rows = this.el.select('tbody > tr', true).elements;
5935         
5936         if(typeof(rows[index]) != 'undefined'){
5937             bt.removeChild(rows[index].dom);
5938         }
5939         
5940 //        if(bt.rows[index]){
5941 //            bt.removeChild(bt.rows[index]);
5942 //        }
5943         
5944         if(isUpdate !== true){
5945             //this.stripeRows(index);
5946             //this.syncRowHeights(index, index);
5947             //this.layout();
5948             this.fireEvent("rowremoved", this, index, record);
5949         }
5950     },
5951     
5952     onAdd : function(ds, records, rowIndex)
5953     {
5954         //Roo.log('on Add called');
5955         // - note this does not handle multiple adding very well..
5956         var bt = this.mainBody.dom;
5957         for (var i =0 ; i < records.length;i++) {
5958             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
5959             //Roo.log(records[i]);
5960             //Roo.log(this.store.getAt(rowIndex+i));
5961             this.insertRow(this.store, rowIndex + i, false);
5962             return;
5963         }
5964         
5965     },
5966     
5967     
5968     refreshRow : function(record){
5969         var ds = this.store, index;
5970         if(typeof record == 'number'){
5971             index = record;
5972             record = ds.getAt(index);
5973         }else{
5974             index = ds.indexOf(record);
5975         }
5976         this.insertRow(ds, index, true);
5977         this.onRemove(ds, record, index+1, true);
5978         //this.syncRowHeights(index, index);
5979         //this.layout();
5980         this.fireEvent("rowupdated", this, index, record);
5981     },
5982     
5983     insertRow : function(dm, rowIndex, isUpdate){
5984         
5985         if(!isUpdate){
5986             this.fireEvent("beforerowsinserted", this, rowIndex);
5987         }
5988             //var s = this.getScrollState();
5989         var row = this.renderRow(this.cm, this.store, rowIndex);
5990         // insert before rowIndex..
5991         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5992         
5993         var _this = this;
5994                 
5995         if(row.cellObjects.length){
5996             Roo.each(row.cellObjects, function(r){
5997                 _this.renderCellObject(r);
5998             })
5999         }
6000             
6001         if(!isUpdate){
6002             this.fireEvent("rowsinserted", this, rowIndex);
6003             //this.syncRowHeights(firstRow, lastRow);
6004             //this.stripeRows(firstRow);
6005             //this.layout();
6006         }
6007         
6008     },
6009     
6010     
6011     getRowDom : function(rowIndex)
6012     {
6013         var rows = this.el.select('tbody > tr', true).elements;
6014         
6015         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6016         
6017     },
6018     // returns the object tree for a tr..
6019   
6020     
6021     renderRow : function(cm, ds, rowIndex) 
6022     {
6023         
6024         var d = ds.getAt(rowIndex);
6025         
6026         var row = {
6027             tag : 'tr',
6028             cn : []
6029         };
6030             
6031         var cellObjects = [];
6032         
6033         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6034             var config = cm.config[i];
6035             
6036             var renderer = cm.getRenderer(i);
6037             var value = '';
6038             var id = false;
6039             
6040             if(typeof(renderer) !== 'undefined'){
6041                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6042             }
6043             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6044             // and are rendered into the cells after the row is rendered - using the id for the element.
6045             
6046             if(typeof(value) === 'object'){
6047                 id = Roo.id();
6048                 cellObjects.push({
6049                     container : id,
6050                     cfg : value 
6051                 })
6052             }
6053             
6054             var rowcfg = {
6055                 record: d,
6056                 rowIndex : rowIndex,
6057                 colIndex : i,
6058                 rowClass : ''
6059             }
6060
6061             this.fireEvent('rowclass', this, rowcfg);
6062             
6063             var td = {
6064                 tag: 'td',
6065                 cls : rowcfg.rowClass,
6066                 style: '',
6067                 html: (typeof(value) === 'object') ? '' : value
6068             };
6069             
6070             if (id) {
6071                 td.id = id;
6072             }
6073             
6074             if(typeof(config.colspan) != 'undefined'){
6075                 td.colspan = config.colspan;
6076             }
6077             
6078             if(typeof(config.hidden) != 'undefined' && config.hidden){
6079                 td.style += ' display:none;';
6080             }
6081             
6082             if(typeof(config.align) != 'undefined' && config.align.length){
6083                 td.style += ' text-align:' + config.align + ';';
6084             }
6085             
6086             if(typeof(config.width) != 'undefined'){
6087                 td.style += ' width:' +  config.width + 'px;';
6088             }
6089             
6090             if(typeof(config.cursor) != 'undefined'){
6091                 td.style += ' cursor:' +  config.cursor + ';';
6092             }
6093             
6094             if(typeof(config.cls) != 'undefined'){
6095                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6096             }
6097              
6098             row.cn.push(td);
6099            
6100         }
6101         
6102         row.cellObjects = cellObjects;
6103         
6104         return row;
6105           
6106     },
6107     
6108     
6109     
6110     onBeforeLoad : function()
6111     {
6112         //Roo.log('ds onBeforeLoad');
6113         
6114         //this.clear();
6115         
6116         //if(this.loadMask){
6117         //    this.maskEl.show();
6118         //}
6119     },
6120      /**
6121      * Remove all rows
6122      */
6123     clear : function()
6124     {
6125         this.el.select('tbody', true).first().dom.innerHTML = '';
6126     },
6127     /**
6128      * Show or hide a row.
6129      * @param {Number} rowIndex to show or hide
6130      * @param {Boolean} state hide
6131      */
6132     setRowVisibility : function(rowIndex, state)
6133     {
6134         var bt = this.mainBody.dom;
6135         
6136         var rows = this.el.select('tbody > tr', true).elements;
6137         
6138         if(typeof(rows[rowIndex]) == 'undefined'){
6139             return;
6140         }
6141         rows[rowIndex].dom.style.display = state ? '' : 'none';
6142     },
6143     
6144     
6145     getSelectionModel : function(){
6146         if(!this.selModel){
6147             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6148         }
6149         return this.selModel;
6150     },
6151     /*
6152      * Render the Roo.bootstrap object from renderder
6153      */
6154     renderCellObject : function(r)
6155     {
6156         var _this = this;
6157         
6158         var t = r.cfg.render(r.container);
6159         
6160         if(r.cfg.cn){
6161             Roo.each(r.cfg.cn, function(c){
6162                 var child = {
6163                     container: t.getChildContainer(),
6164                     cfg: c
6165                 }
6166                 _this.renderCellObject(child);
6167             })
6168         }
6169     },
6170     
6171     getRowIndex : function(row)
6172     {
6173         var rowIndex = -1;
6174         
6175         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6176             if(el != row){
6177                 return;
6178             }
6179             
6180             rowIndex = index;
6181         });
6182         
6183         return rowIndex;
6184     }
6185    
6186 });
6187
6188  
6189
6190  /*
6191  * - LGPL
6192  *
6193  * table cell
6194  * 
6195  */
6196
6197 /**
6198  * @class Roo.bootstrap.TableCell
6199  * @extends Roo.bootstrap.Component
6200  * Bootstrap TableCell class
6201  * @cfg {String} html cell contain text
6202  * @cfg {String} cls cell class
6203  * @cfg {String} tag cell tag (td|th) default td
6204  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6205  * @cfg {String} align Aligns the content in a cell
6206  * @cfg {String} axis Categorizes cells
6207  * @cfg {String} bgcolor Specifies the background color of a cell
6208  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6209  * @cfg {Number} colspan Specifies the number of columns a cell should span
6210  * @cfg {String} headers Specifies one or more header cells a cell is related to
6211  * @cfg {Number} height Sets the height of a cell
6212  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6213  * @cfg {Number} rowspan Sets the number of rows a cell should span
6214  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6215  * @cfg {String} valign Vertical aligns the content in a cell
6216  * @cfg {Number} width Specifies the width of a cell
6217  * 
6218  * @constructor
6219  * Create a new TableCell
6220  * @param {Object} config The config object
6221  */
6222
6223 Roo.bootstrap.TableCell = function(config){
6224     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6225 };
6226
6227 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6228     
6229     html: false,
6230     cls: false,
6231     tag: false,
6232     abbr: false,
6233     align: false,
6234     axis: false,
6235     bgcolor: false,
6236     charoff: false,
6237     colspan: false,
6238     headers: false,
6239     height: false,
6240     nowrap: false,
6241     rowspan: false,
6242     scope: false,
6243     valign: false,
6244     width: false,
6245     
6246     
6247     getAutoCreate : function(){
6248         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6249         
6250         cfg = {
6251             tag: 'td'
6252         }
6253         
6254         if(this.tag){
6255             cfg.tag = this.tag;
6256         }
6257         
6258         if (this.html) {
6259             cfg.html=this.html
6260         }
6261         if (this.cls) {
6262             cfg.cls=this.cls
6263         }
6264         if (this.abbr) {
6265             cfg.abbr=this.abbr
6266         }
6267         if (this.align) {
6268             cfg.align=this.align
6269         }
6270         if (this.axis) {
6271             cfg.axis=this.axis
6272         }
6273         if (this.bgcolor) {
6274             cfg.bgcolor=this.bgcolor
6275         }
6276         if (this.charoff) {
6277             cfg.charoff=this.charoff
6278         }
6279         if (this.colspan) {
6280             cfg.colspan=this.colspan
6281         }
6282         if (this.headers) {
6283             cfg.headers=this.headers
6284         }
6285         if (this.height) {
6286             cfg.height=this.height
6287         }
6288         if (this.nowrap) {
6289             cfg.nowrap=this.nowrap
6290         }
6291         if (this.rowspan) {
6292             cfg.rowspan=this.rowspan
6293         }
6294         if (this.scope) {
6295             cfg.scope=this.scope
6296         }
6297         if (this.valign) {
6298             cfg.valign=this.valign
6299         }
6300         if (this.width) {
6301             cfg.width=this.width
6302         }
6303         
6304         
6305         return cfg;
6306     }
6307    
6308 });
6309
6310  
6311
6312  /*
6313  * - LGPL
6314  *
6315  * table row
6316  * 
6317  */
6318
6319 /**
6320  * @class Roo.bootstrap.TableRow
6321  * @extends Roo.bootstrap.Component
6322  * Bootstrap TableRow class
6323  * @cfg {String} cls row class
6324  * @cfg {String} align Aligns the content in a table row
6325  * @cfg {String} bgcolor Specifies a background color for a table row
6326  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6327  * @cfg {String} valign Vertical aligns the content in a table row
6328  * 
6329  * @constructor
6330  * Create a new TableRow
6331  * @param {Object} config The config object
6332  */
6333
6334 Roo.bootstrap.TableRow = function(config){
6335     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6336 };
6337
6338 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6339     
6340     cls: false,
6341     align: false,
6342     bgcolor: false,
6343     charoff: false,
6344     valign: false,
6345     
6346     getAutoCreate : function(){
6347         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6348         
6349         cfg = {
6350             tag: 'tr'
6351         }
6352             
6353         if(this.cls){
6354             cfg.cls = this.cls;
6355         }
6356         if(this.align){
6357             cfg.align = this.align;
6358         }
6359         if(this.bgcolor){
6360             cfg.bgcolor = this.bgcolor;
6361         }
6362         if(this.charoff){
6363             cfg.charoff = this.charoff;
6364         }
6365         if(this.valign){
6366             cfg.valign = this.valign;
6367         }
6368         
6369         return cfg;
6370     }
6371    
6372 });
6373
6374  
6375
6376  /*
6377  * - LGPL
6378  *
6379  * table body
6380  * 
6381  */
6382
6383 /**
6384  * @class Roo.bootstrap.TableBody
6385  * @extends Roo.bootstrap.Component
6386  * Bootstrap TableBody class
6387  * @cfg {String} cls element class
6388  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6389  * @cfg {String} align Aligns the content inside the element
6390  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6391  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6392  * 
6393  * @constructor
6394  * Create a new TableBody
6395  * @param {Object} config The config object
6396  */
6397
6398 Roo.bootstrap.TableBody = function(config){
6399     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6400 };
6401
6402 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6403     
6404     cls: false,
6405     tag: false,
6406     align: false,
6407     charoff: false,
6408     valign: false,
6409     
6410     getAutoCreate : function(){
6411         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6412         
6413         cfg = {
6414             tag: 'tbody'
6415         }
6416             
6417         if (this.cls) {
6418             cfg.cls=this.cls
6419         }
6420         if(this.tag){
6421             cfg.tag = this.tag;
6422         }
6423         
6424         if(this.align){
6425             cfg.align = this.align;
6426         }
6427         if(this.charoff){
6428             cfg.charoff = this.charoff;
6429         }
6430         if(this.valign){
6431             cfg.valign = this.valign;
6432         }
6433         
6434         return cfg;
6435     }
6436     
6437     
6438 //    initEvents : function()
6439 //    {
6440 //        
6441 //        if(!this.store){
6442 //            return;
6443 //        }
6444 //        
6445 //        this.store = Roo.factory(this.store, Roo.data);
6446 //        this.store.on('load', this.onLoad, this);
6447 //        
6448 //        this.store.load();
6449 //        
6450 //    },
6451 //    
6452 //    onLoad: function () 
6453 //    {   
6454 //        this.fireEvent('load', this);
6455 //    }
6456 //    
6457 //   
6458 });
6459
6460  
6461
6462  /*
6463  * Based on:
6464  * Ext JS Library 1.1.1
6465  * Copyright(c) 2006-2007, Ext JS, LLC.
6466  *
6467  * Originally Released Under LGPL - original licence link has changed is not relivant.
6468  *
6469  * Fork - LGPL
6470  * <script type="text/javascript">
6471  */
6472
6473 // as we use this in bootstrap.
6474 Roo.namespace('Roo.form');
6475  /**
6476  * @class Roo.form.Action
6477  * Internal Class used to handle form actions
6478  * @constructor
6479  * @param {Roo.form.BasicForm} el The form element or its id
6480  * @param {Object} config Configuration options
6481  */
6482
6483  
6484  
6485 // define the action interface
6486 Roo.form.Action = function(form, options){
6487     this.form = form;
6488     this.options = options || {};
6489 };
6490 /**
6491  * Client Validation Failed
6492  * @const 
6493  */
6494 Roo.form.Action.CLIENT_INVALID = 'client';
6495 /**
6496  * Server Validation Failed
6497  * @const 
6498  */
6499 Roo.form.Action.SERVER_INVALID = 'server';
6500  /**
6501  * Connect to Server Failed
6502  * @const 
6503  */
6504 Roo.form.Action.CONNECT_FAILURE = 'connect';
6505 /**
6506  * Reading Data from Server Failed
6507  * @const 
6508  */
6509 Roo.form.Action.LOAD_FAILURE = 'load';
6510
6511 Roo.form.Action.prototype = {
6512     type : 'default',
6513     failureType : undefined,
6514     response : undefined,
6515     result : undefined,
6516
6517     // interface method
6518     run : function(options){
6519
6520     },
6521
6522     // interface method
6523     success : function(response){
6524
6525     },
6526
6527     // interface method
6528     handleResponse : function(response){
6529
6530     },
6531
6532     // default connection failure
6533     failure : function(response){
6534         
6535         this.response = response;
6536         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6537         this.form.afterAction(this, false);
6538     },
6539
6540     processResponse : function(response){
6541         this.response = response;
6542         if(!response.responseText){
6543             return true;
6544         }
6545         this.result = this.handleResponse(response);
6546         return this.result;
6547     },
6548
6549     // utility functions used internally
6550     getUrl : function(appendParams){
6551         var url = this.options.url || this.form.url || this.form.el.dom.action;
6552         if(appendParams){
6553             var p = this.getParams();
6554             if(p){
6555                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6556             }
6557         }
6558         return url;
6559     },
6560
6561     getMethod : function(){
6562         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6563     },
6564
6565     getParams : function(){
6566         var bp = this.form.baseParams;
6567         var p = this.options.params;
6568         if(p){
6569             if(typeof p == "object"){
6570                 p = Roo.urlEncode(Roo.applyIf(p, bp));
6571             }else if(typeof p == 'string' && bp){
6572                 p += '&' + Roo.urlEncode(bp);
6573             }
6574         }else if(bp){
6575             p = Roo.urlEncode(bp);
6576         }
6577         return p;
6578     },
6579
6580     createCallback : function(){
6581         return {
6582             success: this.success,
6583             failure: this.failure,
6584             scope: this,
6585             timeout: (this.form.timeout*1000),
6586             upload: this.form.fileUpload ? this.success : undefined
6587         };
6588     }
6589 };
6590
6591 Roo.form.Action.Submit = function(form, options){
6592     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6593 };
6594
6595 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6596     type : 'submit',
6597
6598     haveProgress : false,
6599     uploadComplete : false,
6600     
6601     // uploadProgress indicator.
6602     uploadProgress : function()
6603     {
6604         if (!this.form.progressUrl) {
6605             return;
6606         }
6607         
6608         if (!this.haveProgress) {
6609             Roo.MessageBox.progress("Uploading", "Uploading");
6610         }
6611         if (this.uploadComplete) {
6612            Roo.MessageBox.hide();
6613            return;
6614         }
6615         
6616         this.haveProgress = true;
6617    
6618         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6619         
6620         var c = new Roo.data.Connection();
6621         c.request({
6622             url : this.form.progressUrl,
6623             params: {
6624                 id : uid
6625             },
6626             method: 'GET',
6627             success : function(req){
6628                //console.log(data);
6629                 var rdata = false;
6630                 var edata;
6631                 try  {
6632                    rdata = Roo.decode(req.responseText)
6633                 } catch (e) {
6634                     Roo.log("Invalid data from server..");
6635                     Roo.log(edata);
6636                     return;
6637                 }
6638                 if (!rdata || !rdata.success) {
6639                     Roo.log(rdata);
6640                     Roo.MessageBox.alert(Roo.encode(rdata));
6641                     return;
6642                 }
6643                 var data = rdata.data;
6644                 
6645                 if (this.uploadComplete) {
6646                    Roo.MessageBox.hide();
6647                    return;
6648                 }
6649                    
6650                 if (data){
6651                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6652                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6653                     );
6654                 }
6655                 this.uploadProgress.defer(2000,this);
6656             },
6657        
6658             failure: function(data) {
6659                 Roo.log('progress url failed ');
6660                 Roo.log(data);
6661             },
6662             scope : this
6663         });
6664            
6665     },
6666     
6667     
6668     run : function()
6669     {
6670         // run get Values on the form, so it syncs any secondary forms.
6671         this.form.getValues();
6672         
6673         var o = this.options;
6674         var method = this.getMethod();
6675         var isPost = method == 'POST';
6676         if(o.clientValidation === false || this.form.isValid()){
6677             
6678             if (this.form.progressUrl) {
6679                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6680                     (new Date() * 1) + '' + Math.random());
6681                     
6682             } 
6683             
6684             
6685             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6686                 form:this.form.el.dom,
6687                 url:this.getUrl(!isPost),
6688                 method: method,
6689                 params:isPost ? this.getParams() : null,
6690                 isUpload: this.form.fileUpload
6691             }));
6692             
6693             this.uploadProgress();
6694
6695         }else if (o.clientValidation !== false){ // client validation failed
6696             this.failureType = Roo.form.Action.CLIENT_INVALID;
6697             this.form.afterAction(this, false);
6698         }
6699     },
6700
6701     success : function(response)
6702     {
6703         this.uploadComplete= true;
6704         if (this.haveProgress) {
6705             Roo.MessageBox.hide();
6706         }
6707         
6708         
6709         var result = this.processResponse(response);
6710         if(result === true || result.success){
6711             this.form.afterAction(this, true);
6712             return;
6713         }
6714         if(result.errors){
6715             this.form.markInvalid(result.errors);
6716             this.failureType = Roo.form.Action.SERVER_INVALID;
6717         }
6718         this.form.afterAction(this, false);
6719     },
6720     failure : function(response)
6721     {
6722         this.uploadComplete= true;
6723         if (this.haveProgress) {
6724             Roo.MessageBox.hide();
6725         }
6726         
6727         this.response = response;
6728         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6729         this.form.afterAction(this, false);
6730     },
6731     
6732     handleResponse : function(response){
6733         if(this.form.errorReader){
6734             var rs = this.form.errorReader.read(response);
6735             var errors = [];
6736             if(rs.records){
6737                 for(var i = 0, len = rs.records.length; i < len; i++) {
6738                     var r = rs.records[i];
6739                     errors[i] = r.data;
6740                 }
6741             }
6742             if(errors.length < 1){
6743                 errors = null;
6744             }
6745             return {
6746                 success : rs.success,
6747                 errors : errors
6748             };
6749         }
6750         var ret = false;
6751         try {
6752             ret = Roo.decode(response.responseText);
6753         } catch (e) {
6754             ret = {
6755                 success: false,
6756                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6757                 errors : []
6758             };
6759         }
6760         return ret;
6761         
6762     }
6763 });
6764
6765
6766 Roo.form.Action.Load = function(form, options){
6767     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6768     this.reader = this.form.reader;
6769 };
6770
6771 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6772     type : 'load',
6773
6774     run : function(){
6775         
6776         Roo.Ajax.request(Roo.apply(
6777                 this.createCallback(), {
6778                     method:this.getMethod(),
6779                     url:this.getUrl(false),
6780                     params:this.getParams()
6781         }));
6782     },
6783
6784     success : function(response){
6785         
6786         var result = this.processResponse(response);
6787         if(result === true || !result.success || !result.data){
6788             this.failureType = Roo.form.Action.LOAD_FAILURE;
6789             this.form.afterAction(this, false);
6790             return;
6791         }
6792         this.form.clearInvalid();
6793         this.form.setValues(result.data);
6794         this.form.afterAction(this, true);
6795     },
6796
6797     handleResponse : function(response){
6798         if(this.form.reader){
6799             var rs = this.form.reader.read(response);
6800             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6801             return {
6802                 success : rs.success,
6803                 data : data
6804             };
6805         }
6806         return Roo.decode(response.responseText);
6807     }
6808 });
6809
6810 Roo.form.Action.ACTION_TYPES = {
6811     'load' : Roo.form.Action.Load,
6812     'submit' : Roo.form.Action.Submit
6813 };/*
6814  * - LGPL
6815  *
6816  * form
6817  * 
6818  */
6819
6820 /**
6821  * @class Roo.bootstrap.Form
6822  * @extends Roo.bootstrap.Component
6823  * Bootstrap Form class
6824  * @cfg {String} method  GET | POST (default POST)
6825  * @cfg {String} labelAlign top | left (default top)
6826  * @cfg {String} align left  | right - for navbars
6827  * @cfg {Boolean} loadMask load mask when submit (default true)
6828
6829  * 
6830  * @constructor
6831  * Create a new Form
6832  * @param {Object} config The config object
6833  */
6834
6835
6836 Roo.bootstrap.Form = function(config){
6837     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6838     this.addEvents({
6839         /**
6840          * @event clientvalidation
6841          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6842          * @param {Form} this
6843          * @param {Boolean} valid true if the form has passed client-side validation
6844          */
6845         clientvalidation: true,
6846         /**
6847          * @event beforeaction
6848          * Fires before any action is performed. Return false to cancel the action.
6849          * @param {Form} this
6850          * @param {Action} action The action to be performed
6851          */
6852         beforeaction: true,
6853         /**
6854          * @event actionfailed
6855          * Fires when an action fails.
6856          * @param {Form} this
6857          * @param {Action} action The action that failed
6858          */
6859         actionfailed : true,
6860         /**
6861          * @event actioncomplete
6862          * Fires when an action is completed.
6863          * @param {Form} this
6864          * @param {Action} action The action that completed
6865          */
6866         actioncomplete : true
6867     });
6868     
6869 };
6870
6871 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6872       
6873      /**
6874      * @cfg {String} method
6875      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6876      */
6877     method : 'POST',
6878     /**
6879      * @cfg {String} url
6880      * The URL to use for form actions if one isn't supplied in the action options.
6881      */
6882     /**
6883      * @cfg {Boolean} fileUpload
6884      * Set to true if this form is a file upload.
6885      */
6886      
6887     /**
6888      * @cfg {Object} baseParams
6889      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6890      */
6891       
6892     /**
6893      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6894      */
6895     timeout: 30,
6896     /**
6897      * @cfg {Sting} align (left|right) for navbar forms
6898      */
6899     align : 'left',
6900
6901     // private
6902     activeAction : null,
6903  
6904     /**
6905      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6906      * element by passing it or its id or mask the form itself by passing in true.
6907      * @type Mixed
6908      */
6909     waitMsgTarget : false,
6910     
6911     loadMask : true,
6912     
6913     getAutoCreate : function(){
6914         
6915         var cfg = {
6916             tag: 'form',
6917             method : this.method || 'POST',
6918             id : this.id || Roo.id(),
6919             cls : ''
6920         }
6921         if (this.parent().xtype.match(/^Nav/)) {
6922             cfg.cls = 'navbar-form navbar-' + this.align;
6923             
6924         }
6925         
6926         if (this.labelAlign == 'left' ) {
6927             cfg.cls += ' form-horizontal';
6928         }
6929         
6930         
6931         return cfg;
6932     },
6933     initEvents : function()
6934     {
6935         this.el.on('submit', this.onSubmit, this);
6936         // this was added as random key presses on the form where triggering form submit.
6937         this.el.on('keypress', function(e) {
6938             if (e.getCharCode() != 13) {
6939                 return true;
6940             }
6941             // we might need to allow it for textareas.. and some other items.
6942             // check e.getTarget().
6943             
6944             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6945                 return true;
6946             }
6947         
6948             Roo.log("keypress blocked");
6949             
6950             e.preventDefault();
6951             return false;
6952         });
6953         
6954     },
6955     // private
6956     onSubmit : function(e){
6957         e.stopEvent();
6958     },
6959     
6960      /**
6961      * Returns true if client-side validation on the form is successful.
6962      * @return Boolean
6963      */
6964     isValid : function(){
6965         var items = this.getItems();
6966         var valid = true;
6967         items.each(function(f){
6968            if(!f.validate()){
6969                valid = false;
6970                
6971            }
6972         });
6973         return valid;
6974     },
6975     /**
6976      * Returns true if any fields in this form have changed since their original load.
6977      * @return Boolean
6978      */
6979     isDirty : function(){
6980         var dirty = false;
6981         var items = this.getItems();
6982         items.each(function(f){
6983            if(f.isDirty()){
6984                dirty = true;
6985                return false;
6986            }
6987            return true;
6988         });
6989         return dirty;
6990     },
6991      /**
6992      * Performs a predefined action (submit or load) or custom actions you define on this form.
6993      * @param {String} actionName The name of the action type
6994      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6995      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6996      * accept other config options):
6997      * <pre>
6998 Property          Type             Description
6999 ----------------  ---------------  ----------------------------------------------------------------------------------
7000 url               String           The url for the action (defaults to the form's url)
7001 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7002 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7003 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7004                                    validate the form on the client (defaults to false)
7005      * </pre>
7006      * @return {BasicForm} this
7007      */
7008     doAction : function(action, options){
7009         if(typeof action == 'string'){
7010             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7011         }
7012         if(this.fireEvent('beforeaction', this, action) !== false){
7013             this.beforeAction(action);
7014             action.run.defer(100, action);
7015         }
7016         return this;
7017     },
7018     
7019     // private
7020     beforeAction : function(action){
7021         var o = action.options;
7022         
7023         if(this.loadMask){
7024             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7025         }
7026         // not really supported yet.. ??
7027         
7028         //if(this.waitMsgTarget === true){
7029         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7030         //}else if(this.waitMsgTarget){
7031         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7032         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7033         //}else {
7034         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7035        // }
7036          
7037     },
7038
7039     // private
7040     afterAction : function(action, success){
7041         this.activeAction = null;
7042         var o = action.options;
7043         
7044         //if(this.waitMsgTarget === true){
7045             this.el.unmask();
7046         //}else if(this.waitMsgTarget){
7047         //    this.waitMsgTarget.unmask();
7048         //}else{
7049         //    Roo.MessageBox.updateProgress(1);
7050         //    Roo.MessageBox.hide();
7051        // }
7052         // 
7053         if(success){
7054             if(o.reset){
7055                 this.reset();
7056             }
7057             Roo.callback(o.success, o.scope, [this, action]);
7058             this.fireEvent('actioncomplete', this, action);
7059             
7060         }else{
7061             
7062             // failure condition..
7063             // we have a scenario where updates need confirming.
7064             // eg. if a locking scenario exists..
7065             // we look for { errors : { needs_confirm : true }} in the response.
7066             if (
7067                 (typeof(action.result) != 'undefined')  &&
7068                 (typeof(action.result.errors) != 'undefined')  &&
7069                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7070            ){
7071                 var _t = this;
7072                 Roo.log("not supported yet");
7073                  /*
7074                 
7075                 Roo.MessageBox.confirm(
7076                     "Change requires confirmation",
7077                     action.result.errorMsg,
7078                     function(r) {
7079                         if (r != 'yes') {
7080                             return;
7081                         }
7082                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7083                     }
7084                     
7085                 );
7086                 */
7087                 
7088                 
7089                 return;
7090             }
7091             
7092             Roo.callback(o.failure, o.scope, [this, action]);
7093             // show an error message if no failed handler is set..
7094             if (!this.hasListener('actionfailed')) {
7095                 Roo.log("need to add dialog support");
7096                 /*
7097                 Roo.MessageBox.alert("Error",
7098                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7099                         action.result.errorMsg :
7100                         "Saving Failed, please check your entries or try again"
7101                 );
7102                 */
7103             }
7104             
7105             this.fireEvent('actionfailed', this, action);
7106         }
7107         
7108     },
7109     /**
7110      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7111      * @param {String} id The value to search for
7112      * @return Field
7113      */
7114     findField : function(id){
7115         var items = this.getItems();
7116         var field = items.get(id);
7117         if(!field){
7118              items.each(function(f){
7119                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7120                     field = f;
7121                     return false;
7122                 }
7123                 return true;
7124             });
7125         }
7126         return field || null;
7127     },
7128      /**
7129      * Mark fields in this form invalid in bulk.
7130      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7131      * @return {BasicForm} this
7132      */
7133     markInvalid : function(errors){
7134         if(errors instanceof Array){
7135             for(var i = 0, len = errors.length; i < len; i++){
7136                 var fieldError = errors[i];
7137                 var f = this.findField(fieldError.id);
7138                 if(f){
7139                     f.markInvalid(fieldError.msg);
7140                 }
7141             }
7142         }else{
7143             var field, id;
7144             for(id in errors){
7145                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7146                     field.markInvalid(errors[id]);
7147                 }
7148             }
7149         }
7150         //Roo.each(this.childForms || [], function (f) {
7151         //    f.markInvalid(errors);
7152         //});
7153         
7154         return this;
7155     },
7156
7157     /**
7158      * Set values for fields in this form in bulk.
7159      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7160      * @return {BasicForm} this
7161      */
7162     setValues : function(values){
7163         if(values instanceof Array){ // array of objects
7164             for(var i = 0, len = values.length; i < len; i++){
7165                 var v = values[i];
7166                 var f = this.findField(v.id);
7167                 if(f){
7168                     f.setValue(v.value);
7169                     if(this.trackResetOnLoad){
7170                         f.originalValue = f.getValue();
7171                     }
7172                 }
7173             }
7174         }else{ // object hash
7175             var field, id;
7176             for(id in values){
7177                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7178                     
7179                     if (field.setFromData && 
7180                         field.valueField && 
7181                         field.displayField &&
7182                         // combos' with local stores can 
7183                         // be queried via setValue()
7184                         // to set their value..
7185                         (field.store && !field.store.isLocal)
7186                         ) {
7187                         // it's a combo
7188                         var sd = { };
7189                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7190                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7191                         field.setFromData(sd);
7192                         
7193                     } else {
7194                         field.setValue(values[id]);
7195                     }
7196                     
7197                     
7198                     if(this.trackResetOnLoad){
7199                         field.originalValue = field.getValue();
7200                     }
7201                 }
7202             }
7203         }
7204          
7205         //Roo.each(this.childForms || [], function (f) {
7206         //    f.setValues(values);
7207         //});
7208                 
7209         return this;
7210     },
7211
7212     /**
7213      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7214      * they are returned as an array.
7215      * @param {Boolean} asString
7216      * @return {Object}
7217      */
7218     getValues : function(asString){
7219         //if (this.childForms) {
7220             // copy values from the child forms
7221         //    Roo.each(this.childForms, function (f) {
7222         //        this.setValues(f.getValues());
7223         //    }, this);
7224         //}
7225         
7226         
7227         
7228         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7229         if(asString === true){
7230             return fs;
7231         }
7232         return Roo.urlDecode(fs);
7233     },
7234     
7235     /**
7236      * Returns the fields in this form as an object with key/value pairs. 
7237      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7238      * @return {Object}
7239      */
7240     getFieldValues : function(with_hidden)
7241     {
7242         var items = this.getItems();
7243         var ret = {};
7244         items.each(function(f){
7245             if (!f.getName()) {
7246                 return;
7247             }
7248             var v = f.getValue();
7249             if (f.inputType =='radio') {
7250                 if (typeof(ret[f.getName()]) == 'undefined') {
7251                     ret[f.getName()] = ''; // empty..
7252                 }
7253                 
7254                 if (!f.el.dom.checked) {
7255                     return;
7256                     
7257                 }
7258                 v = f.el.dom.value;
7259                 
7260             }
7261             
7262             // not sure if this supported any more..
7263             if ((typeof(v) == 'object') && f.getRawValue) {
7264                 v = f.getRawValue() ; // dates..
7265             }
7266             // combo boxes where name != hiddenName...
7267             if (f.name != f.getName()) {
7268                 ret[f.name] = f.getRawValue();
7269             }
7270             ret[f.getName()] = v;
7271         });
7272         
7273         return ret;
7274     },
7275
7276     /**
7277      * Clears all invalid messages in this form.
7278      * @return {BasicForm} this
7279      */
7280     clearInvalid : function(){
7281         var items = this.getItems();
7282         
7283         items.each(function(f){
7284            f.clearInvalid();
7285         });
7286         
7287         
7288         
7289         return this;
7290     },
7291
7292     /**
7293      * Resets this form.
7294      * @return {BasicForm} this
7295      */
7296     reset : function(){
7297         var items = this.getItems();
7298         items.each(function(f){
7299             f.reset();
7300         });
7301         
7302         Roo.each(this.childForms || [], function (f) {
7303             f.reset();
7304         });
7305        
7306         
7307         return this;
7308     },
7309     getItems : function()
7310     {
7311         var r=new Roo.util.MixedCollection(false, function(o){
7312             return o.id || (o.id = Roo.id());
7313         });
7314         var iter = function(el) {
7315             if (el.inputEl) {
7316                 r.add(el);
7317             }
7318             if (!el.items) {
7319                 return;
7320             }
7321             Roo.each(el.items,function(e) {
7322                 iter(e);
7323             });
7324             
7325             
7326         };
7327         
7328         iter(this);
7329         return r;
7330         
7331         
7332         
7333         
7334     }
7335     
7336 });
7337
7338  
7339 /*
7340  * Based on:
7341  * Ext JS Library 1.1.1
7342  * Copyright(c) 2006-2007, Ext JS, LLC.
7343  *
7344  * Originally Released Under LGPL - original licence link has changed is not relivant.
7345  *
7346  * Fork - LGPL
7347  * <script type="text/javascript">
7348  */
7349 /**
7350  * @class Roo.form.VTypes
7351  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7352  * @singleton
7353  */
7354 Roo.form.VTypes = function(){
7355     // closure these in so they are only created once.
7356     var alpha = /^[a-zA-Z_]+$/;
7357     var alphanum = /^[a-zA-Z0-9_]+$/;
7358     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7359     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7360
7361     // All these messages and functions are configurable
7362     return {
7363         /**
7364          * The function used to validate email addresses
7365          * @param {String} value The email address
7366          */
7367         'email' : function(v){
7368             return email.test(v);
7369         },
7370         /**
7371          * The error text to display when the email validation function returns false
7372          * @type String
7373          */
7374         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7375         /**
7376          * The keystroke filter mask to be applied on email input
7377          * @type RegExp
7378          */
7379         'emailMask' : /[a-z0-9_\.\-@]/i,
7380
7381         /**
7382          * The function used to validate URLs
7383          * @param {String} value The URL
7384          */
7385         'url' : function(v){
7386             return url.test(v);
7387         },
7388         /**
7389          * The error text to display when the url validation function returns false
7390          * @type String
7391          */
7392         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7393         
7394         /**
7395          * The function used to validate alpha values
7396          * @param {String} value The value
7397          */
7398         'alpha' : function(v){
7399             return alpha.test(v);
7400         },
7401         /**
7402          * The error text to display when the alpha validation function returns false
7403          * @type String
7404          */
7405         'alphaText' : 'This field should only contain letters and _',
7406         /**
7407          * The keystroke filter mask to be applied on alpha input
7408          * @type RegExp
7409          */
7410         'alphaMask' : /[a-z_]/i,
7411
7412         /**
7413          * The function used to validate alphanumeric values
7414          * @param {String} value The value
7415          */
7416         'alphanum' : function(v){
7417             return alphanum.test(v);
7418         },
7419         /**
7420          * The error text to display when the alphanumeric validation function returns false
7421          * @type String
7422          */
7423         'alphanumText' : 'This field should only contain letters, numbers and _',
7424         /**
7425          * The keystroke filter mask to be applied on alphanumeric input
7426          * @type RegExp
7427          */
7428         'alphanumMask' : /[a-z0-9_]/i
7429     };
7430 }();/*
7431  * - LGPL
7432  *
7433  * Input
7434  * 
7435  */
7436
7437 /**
7438  * @class Roo.bootstrap.Input
7439  * @extends Roo.bootstrap.Component
7440  * Bootstrap Input class
7441  * @cfg {Boolean} disabled is it disabled
7442  * @cfg {String} fieldLabel - the label associated
7443  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7444  * @cfg {String} name name of the input
7445  * @cfg {string} fieldLabel - the label associated
7446  * @cfg {string}  inputType - input / file submit ...
7447  * @cfg {string} placeholder - placeholder to put in text.
7448  * @cfg {string}  before - input group add on before
7449  * @cfg {string} after - input group add on after
7450  * @cfg {string} size - (lg|sm) or leave empty..
7451  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7452  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7453  * @cfg {Number} md colspan out of 12 for computer-sized screens
7454  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7455  * @cfg {string} value default value of the input
7456  * @cfg {Number} labelWidth set the width of label (0-12)
7457  * @cfg {String} labelAlign (top|left)
7458  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7459  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7460
7461  * @cfg {String} align (left|center|right) Default left
7462  * 
7463  * 
7464  * 
7465  * @constructor
7466  * Create a new Input
7467  * @param {Object} config The config object
7468  */
7469
7470 Roo.bootstrap.Input = function(config){
7471     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7472    
7473         this.addEvents({
7474             /**
7475              * @event focus
7476              * Fires when this field receives input focus.
7477              * @param {Roo.form.Field} this
7478              */
7479             focus : true,
7480             /**
7481              * @event blur
7482              * Fires when this field loses input focus.
7483              * @param {Roo.form.Field} this
7484              */
7485             blur : true,
7486             /**
7487              * @event specialkey
7488              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7489              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7490              * @param {Roo.form.Field} this
7491              * @param {Roo.EventObject} e The event object
7492              */
7493             specialkey : true,
7494             /**
7495              * @event change
7496              * Fires just before the field blurs if the field value has changed.
7497              * @param {Roo.form.Field} this
7498              * @param {Mixed} newValue The new value
7499              * @param {Mixed} oldValue The original value
7500              */
7501             change : true,
7502             /**
7503              * @event invalid
7504              * Fires after the field has been marked as invalid.
7505              * @param {Roo.form.Field} this
7506              * @param {String} msg The validation message
7507              */
7508             invalid : true,
7509             /**
7510              * @event valid
7511              * Fires after the field has been validated with no errors.
7512              * @param {Roo.form.Field} this
7513              */
7514             valid : true,
7515              /**
7516              * @event keyup
7517              * Fires after the key up
7518              * @param {Roo.form.Field} this
7519              * @param {Roo.EventObject}  e The event Object
7520              */
7521             keyup : true
7522         });
7523 };
7524
7525 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
7526      /**
7527      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7528       automatic validation (defaults to "keyup").
7529      */
7530     validationEvent : "keyup",
7531      /**
7532      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7533      */
7534     validateOnBlur : true,
7535     /**
7536      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7537      */
7538     validationDelay : 250,
7539      /**
7540      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7541      */
7542     focusClass : "x-form-focus",  // not needed???
7543     
7544        
7545     /**
7546      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7547      */
7548     invalidClass : "has-warning",
7549     
7550     /**
7551      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7552      */
7553     validClass : "has-success",
7554     
7555     /**
7556      * @cfg {Boolean} hasFeedback (true|false) default true
7557      */
7558     hasFeedback : true,
7559     
7560     /**
7561      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7562      */
7563     invalidFeedbackClass : "glyphicon-warning-sign",
7564     
7565     /**
7566      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7567      */
7568     validFeedbackClass : "glyphicon-ok",
7569     
7570     /**
7571      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7572      */
7573     selectOnFocus : false,
7574     
7575      /**
7576      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7577      */
7578     maskRe : null,
7579        /**
7580      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7581      */
7582     vtype : null,
7583     
7584       /**
7585      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7586      */
7587     disableKeyFilter : false,
7588     
7589        /**
7590      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7591      */
7592     disabled : false,
7593      /**
7594      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7595      */
7596     allowBlank : true,
7597     /**
7598      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7599      */
7600     blankText : "This field is required",
7601     
7602      /**
7603      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7604      */
7605     minLength : 0,
7606     /**
7607      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7608      */
7609     maxLength : Number.MAX_VALUE,
7610     /**
7611      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7612      */
7613     minLengthText : "The minimum length for this field is {0}",
7614     /**
7615      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7616      */
7617     maxLengthText : "The maximum length for this field is {0}",
7618   
7619     
7620     /**
7621      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7622      * If available, this function will be called only after the basic validators all return true, and will be passed the
7623      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7624      */
7625     validator : null,
7626     /**
7627      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7628      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7629      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7630      */
7631     regex : null,
7632     /**
7633      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7634      */
7635     regexText : "",
7636     
7637     autocomplete: false,
7638     
7639     
7640     fieldLabel : '',
7641     inputType : 'text',
7642     
7643     name : false,
7644     placeholder: false,
7645     before : false,
7646     after : false,
7647     size : false,
7648     hasFocus : false,
7649     preventMark: false,
7650     isFormField : true,
7651     value : '',
7652     labelWidth : 2,
7653     labelAlign : false,
7654     readOnly : false,
7655     align : false,
7656     formatedValue : false,
7657     
7658     parentLabelAlign : function()
7659     {
7660         var parent = this;
7661         while (parent.parent()) {
7662             parent = parent.parent();
7663             if (typeof(parent.labelAlign) !='undefined') {
7664                 return parent.labelAlign;
7665             }
7666         }
7667         return 'left';
7668         
7669     },
7670     
7671     getAutoCreate : function(){
7672         
7673         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7674         
7675         var id = Roo.id();
7676         
7677         var cfg = {};
7678         
7679         if(this.inputType != 'hidden'){
7680             cfg.cls = 'form-group' //input-group
7681         }
7682         
7683         var input =  {
7684             tag: 'input',
7685             id : id,
7686             type : this.inputType,
7687             value : this.value,
7688             cls : 'form-control',
7689             placeholder : this.placeholder || '',
7690             autocomplete : this.autocomplete || 'new-password'
7691         };
7692         
7693         
7694         if(this.align){
7695             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7696         }
7697         
7698         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7699             input.maxLength = this.maxLength;
7700         }
7701         
7702         if (this.disabled) {
7703             input.disabled=true;
7704         }
7705         
7706         if (this.readOnly) {
7707             input.readonly=true;
7708         }
7709         
7710         if (this.name) {
7711             input.name = this.name;
7712         }
7713         if (this.size) {
7714             input.cls += ' input-' + this.size;
7715         }
7716         var settings=this;
7717         ['xs','sm','md','lg'].map(function(size){
7718             if (settings[size]) {
7719                 cfg.cls += ' col-' + size + '-' + settings[size];
7720             }
7721         });
7722         
7723         var inputblock = input;
7724         
7725         var feedback = {
7726             tag: 'span',
7727             cls: 'glyphicon form-control-feedback'
7728         };
7729             
7730         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7731             
7732             inputblock = {
7733                 cls : 'has-feedback',
7734                 cn :  [
7735                     input,
7736                     feedback
7737                 ] 
7738             };  
7739         }
7740         
7741         if (this.before || this.after) {
7742             
7743             inputblock = {
7744                 cls : 'input-group',
7745                 cn :  [] 
7746             };
7747             
7748             if (this.before && typeof(this.before) == 'string') {
7749                 
7750                 inputblock.cn.push({
7751                     tag :'span',
7752                     cls : 'roo-input-before input-group-addon',
7753                     html : this.before
7754                 });
7755             }
7756             if (this.before && typeof(this.before) == 'object') {
7757                 this.before = Roo.factory(this.before);
7758                 Roo.log(this.before);
7759                 inputblock.cn.push({
7760                     tag :'span',
7761                     cls : 'roo-input-before input-group-' +
7762                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7763                 });
7764             }
7765             
7766             inputblock.cn.push(input);
7767             
7768             if (this.after && typeof(this.after) == 'string') {
7769                 inputblock.cn.push({
7770                     tag :'span',
7771                     cls : 'roo-input-after input-group-addon',
7772                     html : this.after
7773                 });
7774             }
7775             if (this.after && typeof(this.after) == 'object') {
7776                 this.after = Roo.factory(this.after);
7777                 Roo.log(this.after);
7778                 inputblock.cn.push({
7779                     tag :'span',
7780                     cls : 'roo-input-after input-group-' +
7781                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7782                 });
7783             }
7784             
7785             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7786                 inputblock.cls += ' has-feedback';
7787                 inputblock.cn.push(feedback);
7788             }
7789         };
7790         
7791         if (align ==='left' && this.fieldLabel.length) {
7792                 Roo.log("left and has label");
7793                 cfg.cn = [
7794                     
7795                     {
7796                         tag: 'label',
7797                         'for' :  id,
7798                         cls : 'control-label col-sm-' + this.labelWidth,
7799                         html : this.fieldLabel
7800                         
7801                     },
7802                     {
7803                         cls : "col-sm-" + (12 - this.labelWidth), 
7804                         cn: [
7805                             inputblock
7806                         ]
7807                     }
7808                     
7809                 ];
7810         } else if ( this.fieldLabel.length) {
7811                 Roo.log(" label");
7812                  cfg.cn = [
7813                    
7814                     {
7815                         tag: 'label',
7816                         //cls : 'input-group-addon',
7817                         html : this.fieldLabel
7818                         
7819                     },
7820                     
7821                     inputblock
7822                     
7823                 ];
7824
7825         } else {
7826             
7827                 Roo.log(" no label && no align");
7828                 cfg.cn = [
7829                     
7830                         inputblock
7831                     
7832                 ];
7833                 
7834                 
7835         };
7836         Roo.log('input-parentType: ' + this.parentType);
7837         
7838         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7839            cfg.cls += ' navbar-form';
7840            Roo.log(cfg);
7841         }
7842         
7843         return cfg;
7844         
7845     },
7846     /**
7847      * return the real input element.
7848      */
7849     inputEl: function ()
7850     {
7851         return this.el.select('input.form-control',true).first();
7852     },
7853     
7854     tooltipEl : function()
7855     {
7856         return this.inputEl();
7857     },
7858     
7859     setDisabled : function(v)
7860     {
7861         var i  = this.inputEl().dom;
7862         if (!v) {
7863             i.removeAttribute('disabled');
7864             return;
7865             
7866         }
7867         i.setAttribute('disabled','true');
7868     },
7869     initEvents : function()
7870     {
7871           
7872         this.inputEl().on("keydown" , this.fireKey,  this);
7873         this.inputEl().on("focus", this.onFocus,  this);
7874         this.inputEl().on("blur", this.onBlur,  this);
7875         
7876         this.inputEl().relayEvent('keyup', this);
7877
7878         // reference to original value for reset
7879         this.originalValue = this.getValue();
7880         //Roo.form.TextField.superclass.initEvents.call(this);
7881         if(this.validationEvent == 'keyup'){
7882             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7883             this.inputEl().on('keyup', this.filterValidation, this);
7884         }
7885         else if(this.validationEvent !== false){
7886             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7887         }
7888         
7889         if(this.selectOnFocus){
7890             this.on("focus", this.preFocus, this);
7891             
7892         }
7893         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7894             this.inputEl().on("keypress", this.filterKeys, this);
7895         }
7896        /* if(this.grow){
7897             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7898             this.el.on("click", this.autoSize,  this);
7899         }
7900         */
7901         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7902             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7903         }
7904         
7905         if (typeof(this.before) == 'object') {
7906             this.before.render(this.el.select('.roo-input-before',true).first());
7907         }
7908         if (typeof(this.after) == 'object') {
7909             this.after.render(this.el.select('.roo-input-after',true).first());
7910         }
7911         
7912         
7913     },
7914     filterValidation : function(e){
7915         if(!e.isNavKeyPress()){
7916             this.validationTask.delay(this.validationDelay);
7917         }
7918     },
7919      /**
7920      * Validates the field value
7921      * @return {Boolean} True if the value is valid, else false
7922      */
7923     validate : function(){
7924         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7925         if(this.disabled || this.validateValue(this.getRawValue())){
7926             this.markValid();
7927             return true;
7928         }
7929         
7930         this.markInvalid();
7931         return false;
7932     },
7933     
7934     
7935     /**
7936      * Validates a value according to the field's validation rules and marks the field as invalid
7937      * if the validation fails
7938      * @param {Mixed} value The value to validate
7939      * @return {Boolean} True if the value is valid, else false
7940      */
7941     validateValue : function(value){
7942         if(value.length < 1)  { // if it's blank
7943             if(this.allowBlank){
7944                 return true;
7945             }
7946             return false;
7947         }
7948         
7949         if(value.length < this.minLength){
7950             return false;
7951         }
7952         if(value.length > this.maxLength){
7953             return false;
7954         }
7955         if(this.vtype){
7956             var vt = Roo.form.VTypes;
7957             if(!vt[this.vtype](value, this)){
7958                 return false;
7959             }
7960         }
7961         if(typeof this.validator == "function"){
7962             var msg = this.validator(value);
7963             if(msg !== true){
7964                 return false;
7965             }
7966         }
7967         
7968         if(this.regex && !this.regex.test(value)){
7969             return false;
7970         }
7971         
7972         return true;
7973     },
7974
7975     
7976     
7977      // private
7978     fireKey : function(e){
7979         //Roo.log('field ' + e.getKey());
7980         if(e.isNavKeyPress()){
7981             this.fireEvent("specialkey", this, e);
7982         }
7983     },
7984     focus : function (selectText){
7985         if(this.rendered){
7986             this.inputEl().focus();
7987             if(selectText === true){
7988                 this.inputEl().dom.select();
7989             }
7990         }
7991         return this;
7992     } ,
7993     
7994     onFocus : function(){
7995         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7996            // this.el.addClass(this.focusClass);
7997         }
7998         if(!this.hasFocus){
7999             this.hasFocus = true;
8000             this.startValue = this.getValue();
8001             this.fireEvent("focus", this);
8002         }
8003     },
8004     
8005     beforeBlur : Roo.emptyFn,
8006
8007     
8008     // private
8009     onBlur : function(){
8010         this.beforeBlur();
8011         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8012             //this.el.removeClass(this.focusClass);
8013         }
8014         this.hasFocus = false;
8015         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8016             this.validate();
8017         }
8018         var v = this.getValue();
8019         if(String(v) !== String(this.startValue)){
8020             this.fireEvent('change', this, v, this.startValue);
8021         }
8022         this.fireEvent("blur", this);
8023     },
8024     
8025     /**
8026      * Resets the current field value to the originally loaded value and clears any validation messages
8027      */
8028     reset : function(){
8029         this.setValue(this.originalValue);
8030         this.validate();
8031     },
8032      /**
8033      * Returns the name of the field
8034      * @return {Mixed} name The name field
8035      */
8036     getName: function(){
8037         return this.name;
8038     },
8039      /**
8040      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8041      * @return {Mixed} value The field value
8042      */
8043     getValue : function(){
8044         
8045         var v = this.inputEl().getValue();
8046         
8047         return v;
8048     },
8049     /**
8050      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8051      * @return {Mixed} value The field value
8052      */
8053     getRawValue : function(){
8054         var v = this.inputEl().getValue();
8055         
8056         return v;
8057     },
8058     
8059     /**
8060      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8061      * @param {Mixed} value The value to set
8062      */
8063     setRawValue : function(v){
8064         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8065     },
8066     
8067     selectText : function(start, end){
8068         var v = this.getRawValue();
8069         if(v.length > 0){
8070             start = start === undefined ? 0 : start;
8071             end = end === undefined ? v.length : end;
8072             var d = this.inputEl().dom;
8073             if(d.setSelectionRange){
8074                 d.setSelectionRange(start, end);
8075             }else if(d.createTextRange){
8076                 var range = d.createTextRange();
8077                 range.moveStart("character", start);
8078                 range.moveEnd("character", v.length-end);
8079                 range.select();
8080             }
8081         }
8082     },
8083     
8084     /**
8085      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8086      * @param {Mixed} value The value to set
8087      */
8088     setValue : function(v){
8089         this.value = v;
8090         if(this.rendered){
8091             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8092             this.validate();
8093         }
8094     },
8095     
8096     /*
8097     processValue : function(value){
8098         if(this.stripCharsRe){
8099             var newValue = value.replace(this.stripCharsRe, '');
8100             if(newValue !== value){
8101                 this.setRawValue(newValue);
8102                 return newValue;
8103             }
8104         }
8105         return value;
8106     },
8107   */
8108     preFocus : function(){
8109         
8110         if(this.selectOnFocus){
8111             this.inputEl().dom.select();
8112         }
8113     },
8114     filterKeys : function(e){
8115         var k = e.getKey();
8116         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8117             return;
8118         }
8119         var c = e.getCharCode(), cc = String.fromCharCode(c);
8120         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8121             return;
8122         }
8123         if(!this.maskRe.test(cc)){
8124             e.stopEvent();
8125         }
8126     },
8127      /**
8128      * Clear any invalid styles/messages for this field
8129      */
8130     clearInvalid : function(){
8131         
8132         if(!this.el || this.preventMark){ // not rendered
8133             return;
8134         }
8135         this.el.removeClass(this.invalidClass);
8136         
8137         this.fireEvent('valid', this);
8138     },
8139     
8140      /**
8141      * Mark this field as valid
8142      */
8143     markValid : function(){
8144         if(!this.el  || this.preventMark){ // not rendered
8145             return;
8146         }
8147         
8148         this.el.removeClass([this.invalidClass, this.validClass]);
8149         
8150         if(this.disabled || this.allowBlank){
8151             return;
8152         }
8153         
8154         this.el.addClass(this.validClass);
8155         
8156         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && this.getValue().length){
8157             
8158             var feedback = this.el.select('.form-control-feedback', true).first();
8159             
8160             if(feedback){
8161                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8162                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8163             }
8164             
8165         }
8166         
8167         this.fireEvent('valid', this);
8168     },
8169     
8170      /**
8171      * Mark this field as invalid
8172      * @param {String} msg The validation message
8173      */
8174     markInvalid : function(msg){
8175         if(!this.el  || this.preventMark){ // not rendered
8176             return;
8177         }
8178         
8179         this.el.removeClass([this.invalidClass, this.validClass]);
8180         
8181         if(this.disabled || this.allowBlank){
8182             return;
8183         }
8184         
8185         this.el.addClass(this.invalidClass);
8186         
8187         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8188             
8189             var feedback = this.el.select('.form-control-feedback', true).first();
8190             
8191             if(feedback){
8192                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8193                 
8194                 if(this.getValue().length){
8195                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8196                 }
8197                 
8198             }
8199             
8200         }
8201         
8202         this.fireEvent('invalid', this, msg);
8203     },
8204     // private
8205     SafariOnKeyDown : function(event)
8206     {
8207         // this is a workaround for a password hang bug on chrome/ webkit.
8208         
8209         var isSelectAll = false;
8210         
8211         if(this.inputEl().dom.selectionEnd > 0){
8212             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8213         }
8214         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8215             event.preventDefault();
8216             this.setValue('');
8217             return;
8218         }
8219         
8220         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8221             
8222             event.preventDefault();
8223             // this is very hacky as keydown always get's upper case.
8224             //
8225             var cc = String.fromCharCode(event.getCharCode());
8226             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8227             
8228         }
8229     },
8230     adjustWidth : function(tag, w){
8231         tag = tag.toLowerCase();
8232         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8233             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8234                 if(tag == 'input'){
8235                     return w + 2;
8236                 }
8237                 if(tag == 'textarea'){
8238                     return w-2;
8239                 }
8240             }else if(Roo.isOpera){
8241                 if(tag == 'input'){
8242                     return w + 2;
8243                 }
8244                 if(tag == 'textarea'){
8245                     return w-2;
8246                 }
8247             }
8248         }
8249         return w;
8250     }
8251     
8252 });
8253
8254  
8255 /*
8256  * - LGPL
8257  *
8258  * Input
8259  * 
8260  */
8261
8262 /**
8263  * @class Roo.bootstrap.TextArea
8264  * @extends Roo.bootstrap.Input
8265  * Bootstrap TextArea class
8266  * @cfg {Number} cols Specifies the visible width of a text area
8267  * @cfg {Number} rows Specifies the visible number of lines in a text area
8268  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8269  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8270  * @cfg {string} html text
8271  * 
8272  * @constructor
8273  * Create a new TextArea
8274  * @param {Object} config The config object
8275  */
8276
8277 Roo.bootstrap.TextArea = function(config){
8278     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8279    
8280 };
8281
8282 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8283      
8284     cols : false,
8285     rows : 5,
8286     readOnly : false,
8287     warp : 'soft',
8288     resize : false,
8289     value: false,
8290     html: false,
8291     
8292     getAutoCreate : function(){
8293         
8294         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8295         
8296         var id = Roo.id();
8297         
8298         var cfg = {};
8299         
8300         var input =  {
8301             tag: 'textarea',
8302             id : id,
8303             warp : this.warp,
8304             rows : this.rows,
8305             value : this.value || '',
8306             html: this.html || '',
8307             cls : 'form-control',
8308             placeholder : this.placeholder || '' 
8309             
8310         };
8311         
8312         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8313             input.maxLength = this.maxLength;
8314         }
8315         
8316         if(this.resize){
8317             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8318         }
8319         
8320         if(this.cols){
8321             input.cols = this.cols;
8322         }
8323         
8324         if (this.readOnly) {
8325             input.readonly = true;
8326         }
8327         
8328         if (this.name) {
8329             input.name = this.name;
8330         }
8331         
8332         if (this.size) {
8333             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8334         }
8335         
8336         var settings=this;
8337         ['xs','sm','md','lg'].map(function(size){
8338             if (settings[size]) {
8339                 cfg.cls += ' col-' + size + '-' + settings[size];
8340             }
8341         });
8342         
8343         var inputblock = input;
8344         
8345         if(this.hasFeedback && !this.allowBlank){
8346             
8347             var feedback = {
8348                 tag: 'span',
8349                 cls: 'glyphicon form-control-feedback'
8350             };
8351
8352             inputblock = {
8353                 cls : 'has-feedback',
8354                 cn :  [
8355                     input,
8356                     feedback
8357                 ] 
8358             };  
8359         }
8360         
8361         
8362         if (this.before || this.after) {
8363             
8364             inputblock = {
8365                 cls : 'input-group',
8366                 cn :  [] 
8367             };
8368             if (this.before) {
8369                 inputblock.cn.push({
8370                     tag :'span',
8371                     cls : 'input-group-addon',
8372                     html : this.before
8373                 });
8374             }
8375             
8376             inputblock.cn.push(input);
8377             
8378             if(this.hasFeedback && !this.allowBlank){
8379                 inputblock.cls += ' has-feedback';
8380                 inputblock.cn.push(feedback);
8381             }
8382             
8383             if (this.after) {
8384                 inputblock.cn.push({
8385                     tag :'span',
8386                     cls : 'input-group-addon',
8387                     html : this.after
8388                 });
8389             }
8390             
8391         }
8392         
8393         if (align ==='left' && this.fieldLabel.length) {
8394                 Roo.log("left and has label");
8395                 cfg.cn = [
8396                     
8397                     {
8398                         tag: 'label',
8399                         'for' :  id,
8400                         cls : 'control-label col-sm-' + this.labelWidth,
8401                         html : this.fieldLabel
8402                         
8403                     },
8404                     {
8405                         cls : "col-sm-" + (12 - this.labelWidth), 
8406                         cn: [
8407                             inputblock
8408                         ]
8409                     }
8410                     
8411                 ];
8412         } else if ( this.fieldLabel.length) {
8413                 Roo.log(" label");
8414                  cfg.cn = [
8415                    
8416                     {
8417                         tag: 'label',
8418                         //cls : 'input-group-addon',
8419                         html : this.fieldLabel
8420                         
8421                     },
8422                     
8423                     inputblock
8424                     
8425                 ];
8426
8427         } else {
8428             
8429                    Roo.log(" no label && no align");
8430                 cfg.cn = [
8431                     
8432                         inputblock
8433                     
8434                 ];
8435                 
8436                 
8437         }
8438         
8439         if (this.disabled) {
8440             input.disabled=true;
8441         }
8442         
8443         return cfg;
8444         
8445     },
8446     /**
8447      * return the real textarea element.
8448      */
8449     inputEl: function ()
8450     {
8451         return this.el.select('textarea.form-control',true).first();
8452     }
8453 });
8454
8455  
8456 /*
8457  * - LGPL
8458  *
8459  * trigger field - base class for combo..
8460  * 
8461  */
8462  
8463 /**
8464  * @class Roo.bootstrap.TriggerField
8465  * @extends Roo.bootstrap.Input
8466  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8467  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8468  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8469  * for which you can provide a custom implementation.  For example:
8470  * <pre><code>
8471 var trigger = new Roo.bootstrap.TriggerField();
8472 trigger.onTriggerClick = myTriggerFn;
8473 trigger.applyTo('my-field');
8474 </code></pre>
8475  *
8476  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8477  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8478  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
8479  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8480  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8481
8482  * @constructor
8483  * Create a new TriggerField.
8484  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8485  * to the base TextField)
8486  */
8487 Roo.bootstrap.TriggerField = function(config){
8488     this.mimicing = false;
8489     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8490 };
8491
8492 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
8493     /**
8494      * @cfg {String} triggerClass A CSS class to apply to the trigger
8495      */
8496      /**
8497      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8498      */
8499     hideTrigger:false,
8500
8501     /**
8502      * @cfg {Boolean} removable (true|false) special filter default false
8503      */
8504     removable : false,
8505     
8506     /** @cfg {Boolean} grow @hide */
8507     /** @cfg {Number} growMin @hide */
8508     /** @cfg {Number} growMax @hide */
8509
8510     /**
8511      * @hide 
8512      * @method
8513      */
8514     autoSize: Roo.emptyFn,
8515     // private
8516     monitorTab : true,
8517     // private
8518     deferHeight : true,
8519
8520     
8521     actionMode : 'wrap',
8522     
8523     caret : false,
8524     
8525     
8526     getAutoCreate : function(){
8527        
8528         var align = this.labelAlign || this.parentLabelAlign();
8529         
8530         var id = Roo.id();
8531         
8532         var cfg = {
8533             cls: 'form-group' //input-group
8534         };
8535         
8536         
8537         var input =  {
8538             tag: 'input',
8539             id : id,
8540             type : this.inputType,
8541             cls : 'form-control',
8542             autocomplete: 'new-password',
8543             placeholder : this.placeholder || '' 
8544             
8545         };
8546         if (this.name) {
8547             input.name = this.name;
8548         }
8549         if (this.size) {
8550             input.cls += ' input-' + this.size;
8551         }
8552         
8553         if (this.disabled) {
8554             input.disabled=true;
8555         }
8556         
8557         var inputblock = input;
8558         
8559         if(this.hasFeedback && !this.allowBlank){
8560             
8561             var feedback = {
8562                 tag: 'span',
8563                 cls: 'glyphicon form-control-feedback'
8564             };
8565             
8566             if(this.removable && !this.editable && !this.tickable){
8567                 inputblock = {
8568                     cls : 'has-feedback',
8569                     cn :  [
8570                         inputblock,
8571                         {
8572                             tag: 'button',
8573                             html : 'x',
8574                             cls : 'roo-combo-removable-btn close'
8575                         },
8576                         feedback
8577                     ] 
8578                 };
8579             } else {
8580                 inputblock = {
8581                     cls : 'has-feedback',
8582                     cn :  [
8583                         inputblock,
8584                         feedback
8585                     ] 
8586                 };
8587             }
8588               
8589         } else {
8590             if(this.removable && !this.editable && !this.tickable){
8591                 inputblock = {
8592                     cls : 'roo-removable',
8593                     cn :  [
8594                         inputblock,
8595                         {
8596                             tag: 'button',
8597                             html : 'x',
8598                             cls : 'roo-combo-removable-btn close'
8599                         }
8600                     ] 
8601                 };
8602             }
8603         }
8604         
8605         if (this.before || this.after) {
8606             
8607             inputblock = {
8608                 cls : 'input-group',
8609                 cn :  [] 
8610             };
8611             if (this.before) {
8612                 inputblock.cn.push({
8613                     tag :'span',
8614                     cls : 'input-group-addon',
8615                     html : this.before
8616                 });
8617             }
8618             
8619             inputblock.cn.push(input);
8620             
8621             if(this.hasFeedback && !this.allowBlank){
8622                 inputblock.cls += ' has-feedback';
8623                 inputblock.cn.push(feedback);
8624             }
8625             
8626             if (this.after) {
8627                 inputblock.cn.push({
8628                     tag :'span',
8629                     cls : 'input-group-addon',
8630                     html : this.after
8631                 });
8632             }
8633             
8634         };
8635         
8636         var box = {
8637             tag: 'div',
8638             cn: [
8639                 {
8640                     tag: 'input',
8641                     type : 'hidden',
8642                     cls: 'form-hidden-field'
8643                 },
8644                 inputblock
8645             ]
8646             
8647         };
8648         
8649         if(this.multiple){
8650             Roo.log('multiple');
8651             
8652             box = {
8653                 tag: 'div',
8654                 cn: [
8655                     {
8656                         tag: 'input',
8657                         type : 'hidden',
8658                         cls: 'form-hidden-field'
8659                     },
8660                     {
8661                         tag: 'ul',
8662                         cls: 'select2-choices',
8663                         cn:[
8664                             {
8665                                 tag: 'li',
8666                                 cls: 'select2-search-field',
8667                                 cn: [
8668
8669                                     inputblock
8670                                 ]
8671                             }
8672                         ]
8673                     }
8674                 ]
8675             }
8676         };
8677         
8678         var combobox = {
8679             cls: 'select2-container input-group',
8680             cn: [
8681                 box
8682 //                {
8683 //                    tag: 'ul',
8684 //                    cls: 'typeahead typeahead-long dropdown-menu',
8685 //                    style: 'display:none'
8686 //                }
8687             ]
8688         };
8689         
8690         if(!this.multiple && this.showToggleBtn){
8691             
8692             var caret = {
8693                         tag: 'span',
8694                         cls: 'caret'
8695              };
8696             if (this.caret != false) {
8697                 caret = {
8698                      tag: 'i',
8699                      cls: 'fa fa-' + this.caret
8700                 };
8701                 
8702             }
8703             
8704             combobox.cn.push({
8705                 tag :'span',
8706                 cls : 'input-group-addon btn dropdown-toggle',
8707                 cn : [
8708                     caret,
8709                     {
8710                         tag: 'span',
8711                         cls: 'combobox-clear',
8712                         cn  : [
8713                             {
8714                                 tag : 'i',
8715                                 cls: 'icon-remove'
8716                             }
8717                         ]
8718                     }
8719                 ]
8720
8721             })
8722         }
8723         
8724         if(this.multiple){
8725             combobox.cls += ' select2-container-multi';
8726         }
8727         
8728         if (align ==='left' && this.fieldLabel.length) {
8729             
8730                 Roo.log("left and has label");
8731                 cfg.cn = [
8732                     
8733                     {
8734                         tag: 'label',
8735                         'for' :  id,
8736                         cls : 'control-label col-sm-' + this.labelWidth,
8737                         html : this.fieldLabel
8738                         
8739                     },
8740                     {
8741                         cls : "col-sm-" + (12 - this.labelWidth), 
8742                         cn: [
8743                             combobox
8744                         ]
8745                     }
8746                     
8747                 ];
8748         } else if ( this.fieldLabel.length) {
8749                 Roo.log(" label");
8750                  cfg.cn = [
8751                    
8752                     {
8753                         tag: 'label',
8754                         //cls : 'input-group-addon',
8755                         html : this.fieldLabel
8756                         
8757                     },
8758                     
8759                     combobox
8760                     
8761                 ];
8762
8763         } else {
8764             
8765                 Roo.log(" no label && no align");
8766                 cfg = combobox
8767                      
8768                 
8769         }
8770          
8771         var settings=this;
8772         ['xs','sm','md','lg'].map(function(size){
8773             if (settings[size]) {
8774                 cfg.cls += ' col-' + size + '-' + settings[size];
8775             }
8776         });
8777         
8778         return cfg;
8779         
8780     },
8781     
8782     
8783     
8784     // private
8785     onResize : function(w, h){
8786 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8787 //        if(typeof w == 'number'){
8788 //            var x = w - this.trigger.getWidth();
8789 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8790 //            this.trigger.setStyle('left', x+'px');
8791 //        }
8792     },
8793
8794     // private
8795     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8796
8797     // private
8798     getResizeEl : function(){
8799         return this.inputEl();
8800     },
8801
8802     // private
8803     getPositionEl : function(){
8804         return this.inputEl();
8805     },
8806
8807     // private
8808     alignErrorIcon : function(){
8809         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8810     },
8811
8812     // private
8813     initEvents : function(){
8814         
8815         this.createList();
8816         
8817         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8818         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8819         if(!this.multiple && this.showToggleBtn){
8820             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8821             if(this.hideTrigger){
8822                 this.trigger.setDisplayed(false);
8823             }
8824             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8825         }
8826         
8827         if(this.multiple){
8828             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8829         }
8830         
8831         if(this.removable && !this.editable && !this.tickable){
8832             var close = this.closeTriggerEl();
8833             
8834             if(close){
8835                 close.setVisibilityMode(Roo.Element.DISPALY).hide();
8836                 close.on('click', this.removeBtnClick, this, close);
8837             }
8838         }
8839         
8840         //this.trigger.addClassOnOver('x-form-trigger-over');
8841         //this.trigger.addClassOnClick('x-form-trigger-click');
8842         
8843         //if(!this.width){
8844         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8845         //}
8846     },
8847     
8848     closeTriggerEl : function()
8849     {
8850         var close = this.el.select('.roo-combo-removable-btn', true).first();
8851         return close ? close : false;
8852     },
8853     
8854     removeBtnClick : function(e, h, el)
8855     {
8856         e.preventDefault();
8857         
8858         this.fireEvent("remove", this);
8859     },
8860     
8861     createList : function()
8862     {
8863         this.list = Roo.get(document.body).createChild({
8864             tag: 'ul',
8865             cls: 'typeahead typeahead-long dropdown-menu',
8866             style: 'display:none'
8867         });
8868         
8869         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8870         
8871     },
8872
8873     // private
8874     initTrigger : function(){
8875        
8876     },
8877
8878     // private
8879     onDestroy : function(){
8880         if(this.trigger){
8881             this.trigger.removeAllListeners();
8882           //  this.trigger.remove();
8883         }
8884         //if(this.wrap){
8885         //    this.wrap.remove();
8886         //}
8887         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8888     },
8889
8890     // private
8891     onFocus : function(){
8892         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8893         /*
8894         if(!this.mimicing){
8895             this.wrap.addClass('x-trigger-wrap-focus');
8896             this.mimicing = true;
8897             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8898             if(this.monitorTab){
8899                 this.el.on("keydown", this.checkTab, this);
8900             }
8901         }
8902         */
8903     },
8904
8905     // private
8906     checkTab : function(e){
8907         if(e.getKey() == e.TAB){
8908             this.triggerBlur();
8909         }
8910     },
8911
8912     // private
8913     onBlur : function(){
8914         // do nothing
8915     },
8916
8917     // private
8918     mimicBlur : function(e, t){
8919         /*
8920         if(!this.wrap.contains(t) && this.validateBlur()){
8921             this.triggerBlur();
8922         }
8923         */
8924     },
8925
8926     // private
8927     triggerBlur : function(){
8928         this.mimicing = false;
8929         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8930         if(this.monitorTab){
8931             this.el.un("keydown", this.checkTab, this);
8932         }
8933         //this.wrap.removeClass('x-trigger-wrap-focus');
8934         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8935     },
8936
8937     // private
8938     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8939     validateBlur : function(e, t){
8940         return true;
8941     },
8942
8943     // private
8944     onDisable : function(){
8945         this.inputEl().dom.disabled = true;
8946         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8947         //if(this.wrap){
8948         //    this.wrap.addClass('x-item-disabled');
8949         //}
8950     },
8951
8952     // private
8953     onEnable : function(){
8954         this.inputEl().dom.disabled = false;
8955         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8956         //if(this.wrap){
8957         //    this.el.removeClass('x-item-disabled');
8958         //}
8959     },
8960
8961     // private
8962     onShow : function(){
8963         var ae = this.getActionEl();
8964         
8965         if(ae){
8966             ae.dom.style.display = '';
8967             ae.dom.style.visibility = 'visible';
8968         }
8969     },
8970
8971     // private
8972     
8973     onHide : function(){
8974         var ae = this.getActionEl();
8975         ae.dom.style.display = 'none';
8976     },
8977
8978     /**
8979      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8980      * by an implementing function.
8981      * @method
8982      * @param {EventObject} e
8983      */
8984     onTriggerClick : Roo.emptyFn
8985 });
8986  /*
8987  * Based on:
8988  * Ext JS Library 1.1.1
8989  * Copyright(c) 2006-2007, Ext JS, LLC.
8990  *
8991  * Originally Released Under LGPL - original licence link has changed is not relivant.
8992  *
8993  * Fork - LGPL
8994  * <script type="text/javascript">
8995  */
8996
8997
8998 /**
8999  * @class Roo.data.SortTypes
9000  * @singleton
9001  * Defines the default sorting (casting?) comparison functions used when sorting data.
9002  */
9003 Roo.data.SortTypes = {
9004     /**
9005      * Default sort that does nothing
9006      * @param {Mixed} s The value being converted
9007      * @return {Mixed} The comparison value
9008      */
9009     none : function(s){
9010         return s;
9011     },
9012     
9013     /**
9014      * The regular expression used to strip tags
9015      * @type {RegExp}
9016      * @property
9017      */
9018     stripTagsRE : /<\/?[^>]+>/gi,
9019     
9020     /**
9021      * Strips all HTML tags to sort on text only
9022      * @param {Mixed} s The value being converted
9023      * @return {String} The comparison value
9024      */
9025     asText : function(s){
9026         return String(s).replace(this.stripTagsRE, "");
9027     },
9028     
9029     /**
9030      * Strips all HTML tags to sort on text only - Case insensitive
9031      * @param {Mixed} s The value being converted
9032      * @return {String} The comparison value
9033      */
9034     asUCText : function(s){
9035         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9036     },
9037     
9038     /**
9039      * Case insensitive string
9040      * @param {Mixed} s The value being converted
9041      * @return {String} The comparison value
9042      */
9043     asUCString : function(s) {
9044         return String(s).toUpperCase();
9045     },
9046     
9047     /**
9048      * Date sorting
9049      * @param {Mixed} s The value being converted
9050      * @return {Number} The comparison value
9051      */
9052     asDate : function(s) {
9053         if(!s){
9054             return 0;
9055         }
9056         if(s instanceof Date){
9057             return s.getTime();
9058         }
9059         return Date.parse(String(s));
9060     },
9061     
9062     /**
9063      * Float sorting
9064      * @param {Mixed} s The value being converted
9065      * @return {Float} The comparison value
9066      */
9067     asFloat : function(s) {
9068         var val = parseFloat(String(s).replace(/,/g, ""));
9069         if(isNaN(val)) val = 0;
9070         return val;
9071     },
9072     
9073     /**
9074      * Integer sorting
9075      * @param {Mixed} s The value being converted
9076      * @return {Number} The comparison value
9077      */
9078     asInt : function(s) {
9079         var val = parseInt(String(s).replace(/,/g, ""));
9080         if(isNaN(val)) val = 0;
9081         return val;
9082     }
9083 };/*
9084  * Based on:
9085  * Ext JS Library 1.1.1
9086  * Copyright(c) 2006-2007, Ext JS, LLC.
9087  *
9088  * Originally Released Under LGPL - original licence link has changed is not relivant.
9089  *
9090  * Fork - LGPL
9091  * <script type="text/javascript">
9092  */
9093
9094 /**
9095 * @class Roo.data.Record
9096  * Instances of this class encapsulate both record <em>definition</em> information, and record
9097  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9098  * to access Records cached in an {@link Roo.data.Store} object.<br>
9099  * <p>
9100  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9101  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9102  * objects.<br>
9103  * <p>
9104  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9105  * @constructor
9106  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9107  * {@link #create}. The parameters are the same.
9108  * @param {Array} data An associative Array of data values keyed by the field name.
9109  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9110  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9111  * not specified an integer id is generated.
9112  */
9113 Roo.data.Record = function(data, id){
9114     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9115     this.data = data;
9116 };
9117
9118 /**
9119  * Generate a constructor for a specific record layout.
9120  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9121  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9122  * Each field definition object may contain the following properties: <ul>
9123  * <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,
9124  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9125  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9126  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9127  * is being used, then this is a string containing the javascript expression to reference the data relative to 
9128  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9129  * to the data item relative to the record element. If the mapping expression is the same as the field name,
9130  * this may be omitted.</p></li>
9131  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9132  * <ul><li>auto (Default, implies no conversion)</li>
9133  * <li>string</li>
9134  * <li>int</li>
9135  * <li>float</li>
9136  * <li>boolean</li>
9137  * <li>date</li></ul></p></li>
9138  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9139  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9140  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9141  * by the Reader into an object that will be stored in the Record. It is passed the
9142  * following parameters:<ul>
9143  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9144  * </ul></p></li>
9145  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9146  * </ul>
9147  * <br>usage:<br><pre><code>
9148 var TopicRecord = Roo.data.Record.create(
9149     {name: 'title', mapping: 'topic_title'},
9150     {name: 'author', mapping: 'username'},
9151     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9152     {name: 'lastPost', mapping: 'post_time', type: 'date'},
9153     {name: 'lastPoster', mapping: 'user2'},
9154     {name: 'excerpt', mapping: 'post_text'}
9155 );
9156
9157 var myNewRecord = new TopicRecord({
9158     title: 'Do my job please',
9159     author: 'noobie',
9160     totalPosts: 1,
9161     lastPost: new Date(),
9162     lastPoster: 'Animal',
9163     excerpt: 'No way dude!'
9164 });
9165 myStore.add(myNewRecord);
9166 </code></pre>
9167  * @method create
9168  * @static
9169  */
9170 Roo.data.Record.create = function(o){
9171     var f = function(){
9172         f.superclass.constructor.apply(this, arguments);
9173     };
9174     Roo.extend(f, Roo.data.Record);
9175     var p = f.prototype;
9176     p.fields = new Roo.util.MixedCollection(false, function(field){
9177         return field.name;
9178     });
9179     for(var i = 0, len = o.length; i < len; i++){
9180         p.fields.add(new Roo.data.Field(o[i]));
9181     }
9182     f.getField = function(name){
9183         return p.fields.get(name);  
9184     };
9185     return f;
9186 };
9187
9188 Roo.data.Record.AUTO_ID = 1000;
9189 Roo.data.Record.EDIT = 'edit';
9190 Roo.data.Record.REJECT = 'reject';
9191 Roo.data.Record.COMMIT = 'commit';
9192
9193 Roo.data.Record.prototype = {
9194     /**
9195      * Readonly flag - true if this record has been modified.
9196      * @type Boolean
9197      */
9198     dirty : false,
9199     editing : false,
9200     error: null,
9201     modified: null,
9202
9203     // private
9204     join : function(store){
9205         this.store = store;
9206     },
9207
9208     /**
9209      * Set the named field to the specified value.
9210      * @param {String} name The name of the field to set.
9211      * @param {Object} value The value to set the field to.
9212      */
9213     set : function(name, value){
9214         if(this.data[name] == value){
9215             return;
9216         }
9217         this.dirty = true;
9218         if(!this.modified){
9219             this.modified = {};
9220         }
9221         if(typeof this.modified[name] == 'undefined'){
9222             this.modified[name] = this.data[name];
9223         }
9224         this.data[name] = value;
9225         if(!this.editing && this.store){
9226             this.store.afterEdit(this);
9227         }       
9228     },
9229
9230     /**
9231      * Get the value of the named field.
9232      * @param {String} name The name of the field to get the value of.
9233      * @return {Object} The value of the field.
9234      */
9235     get : function(name){
9236         return this.data[name]; 
9237     },
9238
9239     // private
9240     beginEdit : function(){
9241         this.editing = true;
9242         this.modified = {}; 
9243     },
9244
9245     // private
9246     cancelEdit : function(){
9247         this.editing = false;
9248         delete this.modified;
9249     },
9250
9251     // private
9252     endEdit : function(){
9253         this.editing = false;
9254         if(this.dirty && this.store){
9255             this.store.afterEdit(this);
9256         }
9257     },
9258
9259     /**
9260      * Usually called by the {@link Roo.data.Store} which owns the Record.
9261      * Rejects all changes made to the Record since either creation, or the last commit operation.
9262      * Modified fields are reverted to their original values.
9263      * <p>
9264      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9265      * of reject operations.
9266      */
9267     reject : function(){
9268         var m = this.modified;
9269         for(var n in m){
9270             if(typeof m[n] != "function"){
9271                 this.data[n] = m[n];
9272             }
9273         }
9274         this.dirty = false;
9275         delete this.modified;
9276         this.editing = false;
9277         if(this.store){
9278             this.store.afterReject(this);
9279         }
9280     },
9281
9282     /**
9283      * Usually called by the {@link Roo.data.Store} which owns the Record.
9284      * Commits all changes made to the Record since either creation, or the last commit operation.
9285      * <p>
9286      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9287      * of commit operations.
9288      */
9289     commit : function(){
9290         this.dirty = false;
9291         delete this.modified;
9292         this.editing = false;
9293         if(this.store){
9294             this.store.afterCommit(this);
9295         }
9296     },
9297
9298     // private
9299     hasError : function(){
9300         return this.error != null;
9301     },
9302
9303     // private
9304     clearError : function(){
9305         this.error = null;
9306     },
9307
9308     /**
9309      * Creates a copy of this record.
9310      * @param {String} id (optional) A new record id if you don't want to use this record's id
9311      * @return {Record}
9312      */
9313     copy : function(newId) {
9314         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9315     }
9316 };/*
9317  * Based on:
9318  * Ext JS Library 1.1.1
9319  * Copyright(c) 2006-2007, Ext JS, LLC.
9320  *
9321  * Originally Released Under LGPL - original licence link has changed is not relivant.
9322  *
9323  * Fork - LGPL
9324  * <script type="text/javascript">
9325  */
9326
9327
9328
9329 /**
9330  * @class Roo.data.Store
9331  * @extends Roo.util.Observable
9332  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9333  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9334  * <p>
9335  * 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
9336  * has no knowledge of the format of the data returned by the Proxy.<br>
9337  * <p>
9338  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9339  * instances from the data object. These records are cached and made available through accessor functions.
9340  * @constructor
9341  * Creates a new Store.
9342  * @param {Object} config A config object containing the objects needed for the Store to access data,
9343  * and read the data into Records.
9344  */
9345 Roo.data.Store = function(config){
9346     this.data = new Roo.util.MixedCollection(false);
9347     this.data.getKey = function(o){
9348         return o.id;
9349     };
9350     this.baseParams = {};
9351     // private
9352     this.paramNames = {
9353         "start" : "start",
9354         "limit" : "limit",
9355         "sort" : "sort",
9356         "dir" : "dir",
9357         "multisort" : "_multisort"
9358     };
9359
9360     if(config && config.data){
9361         this.inlineData = config.data;
9362         delete config.data;
9363     }
9364
9365     Roo.apply(this, config);
9366     
9367     if(this.reader){ // reader passed
9368         this.reader = Roo.factory(this.reader, Roo.data);
9369         this.reader.xmodule = this.xmodule || false;
9370         if(!this.recordType){
9371             this.recordType = this.reader.recordType;
9372         }
9373         if(this.reader.onMetaChange){
9374             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9375         }
9376     }
9377
9378     if(this.recordType){
9379         this.fields = this.recordType.prototype.fields;
9380     }
9381     this.modified = [];
9382
9383     this.addEvents({
9384         /**
9385          * @event datachanged
9386          * Fires when the data cache has changed, and a widget which is using this Store
9387          * as a Record cache should refresh its view.
9388          * @param {Store} this
9389          */
9390         datachanged : true,
9391         /**
9392          * @event metachange
9393          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9394          * @param {Store} this
9395          * @param {Object} meta The JSON metadata
9396          */
9397         metachange : true,
9398         /**
9399          * @event add
9400          * Fires when Records have been added to the Store
9401          * @param {Store} this
9402          * @param {Roo.data.Record[]} records The array of Records added
9403          * @param {Number} index The index at which the record(s) were added
9404          */
9405         add : true,
9406         /**
9407          * @event remove
9408          * Fires when a Record has been removed from the Store
9409          * @param {Store} this
9410          * @param {Roo.data.Record} record The Record that was removed
9411          * @param {Number} index The index at which the record was removed
9412          */
9413         remove : true,
9414         /**
9415          * @event update
9416          * Fires when a Record has been updated
9417          * @param {Store} this
9418          * @param {Roo.data.Record} record The Record that was updated
9419          * @param {String} operation The update operation being performed.  Value may be one of:
9420          * <pre><code>
9421  Roo.data.Record.EDIT
9422  Roo.data.Record.REJECT
9423  Roo.data.Record.COMMIT
9424          * </code></pre>
9425          */
9426         update : true,
9427         /**
9428          * @event clear
9429          * Fires when the data cache has been cleared.
9430          * @param {Store} this
9431          */
9432         clear : true,
9433         /**
9434          * @event beforeload
9435          * Fires before a request is made for a new data object.  If the beforeload handler returns false
9436          * the load action will be canceled.
9437          * @param {Store} this
9438          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9439          */
9440         beforeload : true,
9441         /**
9442          * @event beforeloadadd
9443          * Fires after a new set of Records has been loaded.
9444          * @param {Store} this
9445          * @param {Roo.data.Record[]} records The Records that were loaded
9446          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9447          */
9448         beforeloadadd : true,
9449         /**
9450          * @event load
9451          * Fires after a new set of Records has been loaded, before they are added to the store.
9452          * @param {Store} this
9453          * @param {Roo.data.Record[]} records The Records that were loaded
9454          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9455          * @params {Object} return from reader
9456          */
9457         load : true,
9458         /**
9459          * @event loadexception
9460          * Fires if an exception occurs in the Proxy during loading.
9461          * Called with the signature of the Proxy's "loadexception" event.
9462          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9463          * 
9464          * @param {Proxy} 
9465          * @param {Object} return from JsonData.reader() - success, totalRecords, records
9466          * @param {Object} load options 
9467          * @param {Object} jsonData from your request (normally this contains the Exception)
9468          */
9469         loadexception : true
9470     });
9471     
9472     if(this.proxy){
9473         this.proxy = Roo.factory(this.proxy, Roo.data);
9474         this.proxy.xmodule = this.xmodule || false;
9475         this.relayEvents(this.proxy,  ["loadexception"]);
9476     }
9477     this.sortToggle = {};
9478     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9479
9480     Roo.data.Store.superclass.constructor.call(this);
9481
9482     if(this.inlineData){
9483         this.loadData(this.inlineData);
9484         delete this.inlineData;
9485     }
9486 };
9487
9488 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9489      /**
9490     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
9491     * without a remote query - used by combo/forms at present.
9492     */
9493     
9494     /**
9495     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9496     */
9497     /**
9498     * @cfg {Array} data Inline data to be loaded when the store is initialized.
9499     */
9500     /**
9501     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9502     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9503     */
9504     /**
9505     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9506     * on any HTTP request
9507     */
9508     /**
9509     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9510     */
9511     /**
9512     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9513     */
9514     multiSort: false,
9515     /**
9516     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9517     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9518     */
9519     remoteSort : false,
9520
9521     /**
9522     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9523      * loaded or when a record is removed. (defaults to false).
9524     */
9525     pruneModifiedRecords : false,
9526
9527     // private
9528     lastOptions : null,
9529
9530     /**
9531      * Add Records to the Store and fires the add event.
9532      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9533      */
9534     add : function(records){
9535         records = [].concat(records);
9536         for(var i = 0, len = records.length; i < len; i++){
9537             records[i].join(this);
9538         }
9539         var index = this.data.length;
9540         this.data.addAll(records);
9541         this.fireEvent("add", this, records, index);
9542     },
9543
9544     /**
9545      * Remove a Record from the Store and fires the remove event.
9546      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9547      */
9548     remove : function(record){
9549         var index = this.data.indexOf(record);
9550         this.data.removeAt(index);
9551         if(this.pruneModifiedRecords){
9552             this.modified.remove(record);
9553         }
9554         this.fireEvent("remove", this, record, index);
9555     },
9556
9557     /**
9558      * Remove all Records from the Store and fires the clear event.
9559      */
9560     removeAll : function(){
9561         this.data.clear();
9562         if(this.pruneModifiedRecords){
9563             this.modified = [];
9564         }
9565         this.fireEvent("clear", this);
9566     },
9567
9568     /**
9569      * Inserts Records to the Store at the given index and fires the add event.
9570      * @param {Number} index The start index at which to insert the passed Records.
9571      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9572      */
9573     insert : function(index, records){
9574         records = [].concat(records);
9575         for(var i = 0, len = records.length; i < len; i++){
9576             this.data.insert(index, records[i]);
9577             records[i].join(this);
9578         }
9579         this.fireEvent("add", this, records, index);
9580     },
9581
9582     /**
9583      * Get the index within the cache of the passed Record.
9584      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9585      * @return {Number} The index of the passed Record. Returns -1 if not found.
9586      */
9587     indexOf : function(record){
9588         return this.data.indexOf(record);
9589     },
9590
9591     /**
9592      * Get the index within the cache of the Record with the passed id.
9593      * @param {String} id The id of the Record to find.
9594      * @return {Number} The index of the Record. Returns -1 if not found.
9595      */
9596     indexOfId : function(id){
9597         return this.data.indexOfKey(id);
9598     },
9599
9600     /**
9601      * Get the Record with the specified id.
9602      * @param {String} id The id of the Record to find.
9603      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9604      */
9605     getById : function(id){
9606         return this.data.key(id);
9607     },
9608
9609     /**
9610      * Get the Record at the specified index.
9611      * @param {Number} index The index of the Record to find.
9612      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9613      */
9614     getAt : function(index){
9615         return this.data.itemAt(index);
9616     },
9617
9618     /**
9619      * Returns a range of Records between specified indices.
9620      * @param {Number} startIndex (optional) The starting index (defaults to 0)
9621      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9622      * @return {Roo.data.Record[]} An array of Records
9623      */
9624     getRange : function(start, end){
9625         return this.data.getRange(start, end);
9626     },
9627
9628     // private
9629     storeOptions : function(o){
9630         o = Roo.apply({}, o);
9631         delete o.callback;
9632         delete o.scope;
9633         this.lastOptions = o;
9634     },
9635
9636     /**
9637      * Loads the Record cache from the configured Proxy using the configured Reader.
9638      * <p>
9639      * If using remote paging, then the first load call must specify the <em>start</em>
9640      * and <em>limit</em> properties in the options.params property to establish the initial
9641      * position within the dataset, and the number of Records to cache on each read from the Proxy.
9642      * <p>
9643      * <strong>It is important to note that for remote data sources, loading is asynchronous,
9644      * and this call will return before the new data has been loaded. Perform any post-processing
9645      * in a callback function, or in a "load" event handler.</strong>
9646      * <p>
9647      * @param {Object} options An object containing properties which control loading options:<ul>
9648      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9649      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9650      * passed the following arguments:<ul>
9651      * <li>r : Roo.data.Record[]</li>
9652      * <li>options: Options object from the load call</li>
9653      * <li>success: Boolean success indicator</li></ul></li>
9654      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9655      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9656      * </ul>
9657      */
9658     load : function(options){
9659         options = options || {};
9660         if(this.fireEvent("beforeload", this, options) !== false){
9661             this.storeOptions(options);
9662             var p = Roo.apply(options.params || {}, this.baseParams);
9663             // if meta was not loaded from remote source.. try requesting it.
9664             if (!this.reader.metaFromRemote) {
9665                 p._requestMeta = 1;
9666             }
9667             if(this.sortInfo && this.remoteSort){
9668                 var pn = this.paramNames;
9669                 p[pn["sort"]] = this.sortInfo.field;
9670                 p[pn["dir"]] = this.sortInfo.direction;
9671             }
9672             if (this.multiSort) {
9673                 var pn = this.paramNames;
9674                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9675             }
9676             
9677             this.proxy.load(p, this.reader, this.loadRecords, this, options);
9678         }
9679     },
9680
9681     /**
9682      * Reloads the Record cache from the configured Proxy using the configured Reader and
9683      * the options from the last load operation performed.
9684      * @param {Object} options (optional) An object containing properties which may override the options
9685      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9686      * the most recently used options are reused).
9687      */
9688     reload : function(options){
9689         this.load(Roo.applyIf(options||{}, this.lastOptions));
9690     },
9691
9692     // private
9693     // Called as a callback by the Reader during a load operation.
9694     loadRecords : function(o, options, success){
9695         if(!o || success === false){
9696             if(success !== false){
9697                 this.fireEvent("load", this, [], options, o);
9698             }
9699             if(options.callback){
9700                 options.callback.call(options.scope || this, [], options, false);
9701             }
9702             return;
9703         }
9704         // if data returned failure - throw an exception.
9705         if (o.success === false) {
9706             // show a message if no listener is registered.
9707             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9708                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9709             }
9710             // loadmask wil be hooked into this..
9711             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9712             return;
9713         }
9714         var r = o.records, t = o.totalRecords || r.length;
9715         
9716         this.fireEvent("beforeloadadd", this, r, options, o);
9717         
9718         if(!options || options.add !== true){
9719             if(this.pruneModifiedRecords){
9720                 this.modified = [];
9721             }
9722             for(var i = 0, len = r.length; i < len; i++){
9723                 r[i].join(this);
9724             }
9725             if(this.snapshot){
9726                 this.data = this.snapshot;
9727                 delete this.snapshot;
9728             }
9729             this.data.clear();
9730             this.data.addAll(r);
9731             this.totalLength = t;
9732             this.applySort();
9733             this.fireEvent("datachanged", this);
9734         }else{
9735             this.totalLength = Math.max(t, this.data.length+r.length);
9736             this.add(r);
9737         }
9738         this.fireEvent("load", this, r, options, o);
9739         if(options.callback){
9740             options.callback.call(options.scope || this, r, options, true);
9741         }
9742     },
9743
9744
9745     /**
9746      * Loads data from a passed data block. A Reader which understands the format of the data
9747      * must have been configured in the constructor.
9748      * @param {Object} data The data block from which to read the Records.  The format of the data expected
9749      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9750      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9751      */
9752     loadData : function(o, append){
9753         var r = this.reader.readRecords(o);
9754         this.loadRecords(r, {add: append}, true);
9755     },
9756
9757     /**
9758      * Gets the number of cached records.
9759      * <p>
9760      * <em>If using paging, this may not be the total size of the dataset. If the data object
9761      * used by the Reader contains the dataset size, then the getTotalCount() function returns
9762      * the data set size</em>
9763      */
9764     getCount : function(){
9765         return this.data.length || 0;
9766     },
9767
9768     /**
9769      * Gets the total number of records in the dataset as returned by the server.
9770      * <p>
9771      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9772      * the dataset size</em>
9773      */
9774     getTotalCount : function(){
9775         return this.totalLength || 0;
9776     },
9777
9778     /**
9779      * Returns the sort state of the Store as an object with two properties:
9780      * <pre><code>
9781  field {String} The name of the field by which the Records are sorted
9782  direction {String} The sort order, "ASC" or "DESC"
9783      * </code></pre>
9784      */
9785     getSortState : function(){
9786         return this.sortInfo;
9787     },
9788
9789     // private
9790     applySort : function(){
9791         if(this.sortInfo && !this.remoteSort){
9792             var s = this.sortInfo, f = s.field;
9793             var st = this.fields.get(f).sortType;
9794             var fn = function(r1, r2){
9795                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9796                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9797             };
9798             this.data.sort(s.direction, fn);
9799             if(this.snapshot && this.snapshot != this.data){
9800                 this.snapshot.sort(s.direction, fn);
9801             }
9802         }
9803     },
9804
9805     /**
9806      * Sets the default sort column and order to be used by the next load operation.
9807      * @param {String} fieldName The name of the field to sort by.
9808      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9809      */
9810     setDefaultSort : function(field, dir){
9811         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9812     },
9813
9814     /**
9815      * Sort the Records.
9816      * If remote sorting is used, the sort is performed on the server, and the cache is
9817      * reloaded. If local sorting is used, the cache is sorted internally.
9818      * @param {String} fieldName The name of the field to sort by.
9819      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9820      */
9821     sort : function(fieldName, dir){
9822         var f = this.fields.get(fieldName);
9823         if(!dir){
9824             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9825             
9826             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9827                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9828             }else{
9829                 dir = f.sortDir;
9830             }
9831         }
9832         this.sortToggle[f.name] = dir;
9833         this.sortInfo = {field: f.name, direction: dir};
9834         if(!this.remoteSort){
9835             this.applySort();
9836             this.fireEvent("datachanged", this);
9837         }else{
9838             this.load(this.lastOptions);
9839         }
9840     },
9841
9842     /**
9843      * Calls the specified function for each of the Records in the cache.
9844      * @param {Function} fn The function to call. The Record is passed as the first parameter.
9845      * Returning <em>false</em> aborts and exits the iteration.
9846      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9847      */
9848     each : function(fn, scope){
9849         this.data.each(fn, scope);
9850     },
9851
9852     /**
9853      * Gets all records modified since the last commit.  Modified records are persisted across load operations
9854      * (e.g., during paging).
9855      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9856      */
9857     getModifiedRecords : function(){
9858         return this.modified;
9859     },
9860
9861     // private
9862     createFilterFn : function(property, value, anyMatch){
9863         if(!value.exec){ // not a regex
9864             value = String(value);
9865             if(value.length == 0){
9866                 return false;
9867             }
9868             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9869         }
9870         return function(r){
9871             return value.test(r.data[property]);
9872         };
9873     },
9874
9875     /**
9876      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9877      * @param {String} property A field on your records
9878      * @param {Number} start The record index to start at (defaults to 0)
9879      * @param {Number} end The last record index to include (defaults to length - 1)
9880      * @return {Number} The sum
9881      */
9882     sum : function(property, start, end){
9883         var rs = this.data.items, v = 0;
9884         start = start || 0;
9885         end = (end || end === 0) ? end : rs.length-1;
9886
9887         for(var i = start; i <= end; i++){
9888             v += (rs[i].data[property] || 0);
9889         }
9890         return v;
9891     },
9892
9893     /**
9894      * Filter the records by a specified property.
9895      * @param {String} field A field on your records
9896      * @param {String/RegExp} value Either a string that the field
9897      * should start with or a RegExp to test against the field
9898      * @param {Boolean} anyMatch True to match any part not just the beginning
9899      */
9900     filter : function(property, value, anyMatch){
9901         var fn = this.createFilterFn(property, value, anyMatch);
9902         return fn ? this.filterBy(fn) : this.clearFilter();
9903     },
9904
9905     /**
9906      * Filter by a function. The specified function will be called with each
9907      * record in this data source. If the function returns true the record is included,
9908      * otherwise it is filtered.
9909      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9910      * @param {Object} scope (optional) The scope of the function (defaults to this)
9911      */
9912     filterBy : function(fn, scope){
9913         this.snapshot = this.snapshot || this.data;
9914         this.data = this.queryBy(fn, scope||this);
9915         this.fireEvent("datachanged", this);
9916     },
9917
9918     /**
9919      * Query the records by a specified property.
9920      * @param {String} field A field on your records
9921      * @param {String/RegExp} value Either a string that the field
9922      * should start with or a RegExp to test against the field
9923      * @param {Boolean} anyMatch True to match any part not just the beginning
9924      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9925      */
9926     query : function(property, value, anyMatch){
9927         var fn = this.createFilterFn(property, value, anyMatch);
9928         return fn ? this.queryBy(fn) : this.data.clone();
9929     },
9930
9931     /**
9932      * Query by a function. The specified function will be called with each
9933      * record in this data source. If the function returns true the record is included
9934      * in the results.
9935      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9936      * @param {Object} scope (optional) The scope of the function (defaults to this)
9937       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9938      **/
9939     queryBy : function(fn, scope){
9940         var data = this.snapshot || this.data;
9941         return data.filterBy(fn, scope||this);
9942     },
9943
9944     /**
9945      * Collects unique values for a particular dataIndex from this store.
9946      * @param {String} dataIndex The property to collect
9947      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9948      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9949      * @return {Array} An array of the unique values
9950      **/
9951     collect : function(dataIndex, allowNull, bypassFilter){
9952         var d = (bypassFilter === true && this.snapshot) ?
9953                 this.snapshot.items : this.data.items;
9954         var v, sv, r = [], l = {};
9955         for(var i = 0, len = d.length; i < len; i++){
9956             v = d[i].data[dataIndex];
9957             sv = String(v);
9958             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9959                 l[sv] = true;
9960                 r[r.length] = v;
9961             }
9962         }
9963         return r;
9964     },
9965
9966     /**
9967      * Revert to a view of the Record cache with no filtering applied.
9968      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9969      */
9970     clearFilter : function(suppressEvent){
9971         if(this.snapshot && this.snapshot != this.data){
9972             this.data = this.snapshot;
9973             delete this.snapshot;
9974             if(suppressEvent !== true){
9975                 this.fireEvent("datachanged", this);
9976             }
9977         }
9978     },
9979
9980     // private
9981     afterEdit : function(record){
9982         if(this.modified.indexOf(record) == -1){
9983             this.modified.push(record);
9984         }
9985         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9986     },
9987     
9988     // private
9989     afterReject : function(record){
9990         this.modified.remove(record);
9991         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9992     },
9993
9994     // private
9995     afterCommit : function(record){
9996         this.modified.remove(record);
9997         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9998     },
9999
10000     /**
10001      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10002      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10003      */
10004     commitChanges : function(){
10005         var m = this.modified.slice(0);
10006         this.modified = [];
10007         for(var i = 0, len = m.length; i < len; i++){
10008             m[i].commit();
10009         }
10010     },
10011
10012     /**
10013      * Cancel outstanding changes on all changed records.
10014      */
10015     rejectChanges : function(){
10016         var m = this.modified.slice(0);
10017         this.modified = [];
10018         for(var i = 0, len = m.length; i < len; i++){
10019             m[i].reject();
10020         }
10021     },
10022
10023     onMetaChange : function(meta, rtype, o){
10024         this.recordType = rtype;
10025         this.fields = rtype.prototype.fields;
10026         delete this.snapshot;
10027         this.sortInfo = meta.sortInfo || this.sortInfo;
10028         this.modified = [];
10029         this.fireEvent('metachange', this, this.reader.meta);
10030     },
10031     
10032     moveIndex : function(data, type)
10033     {
10034         var index = this.indexOf(data);
10035         
10036         var newIndex = index + type;
10037         
10038         this.remove(data);
10039         
10040         this.insert(newIndex, data);
10041         
10042     }
10043 });/*
10044  * Based on:
10045  * Ext JS Library 1.1.1
10046  * Copyright(c) 2006-2007, Ext JS, LLC.
10047  *
10048  * Originally Released Under LGPL - original licence link has changed is not relivant.
10049  *
10050  * Fork - LGPL
10051  * <script type="text/javascript">
10052  */
10053
10054 /**
10055  * @class Roo.data.SimpleStore
10056  * @extends Roo.data.Store
10057  * Small helper class to make creating Stores from Array data easier.
10058  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10059  * @cfg {Array} fields An array of field definition objects, or field name strings.
10060  * @cfg {Array} data The multi-dimensional array of data
10061  * @constructor
10062  * @param {Object} config
10063  */
10064 Roo.data.SimpleStore = function(config){
10065     Roo.data.SimpleStore.superclass.constructor.call(this, {
10066         isLocal : true,
10067         reader: new Roo.data.ArrayReader({
10068                 id: config.id
10069             },
10070             Roo.data.Record.create(config.fields)
10071         ),
10072         proxy : new Roo.data.MemoryProxy(config.data)
10073     });
10074     this.load();
10075 };
10076 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10077  * Based on:
10078  * Ext JS Library 1.1.1
10079  * Copyright(c) 2006-2007, Ext JS, LLC.
10080  *
10081  * Originally Released Under LGPL - original licence link has changed is not relivant.
10082  *
10083  * Fork - LGPL
10084  * <script type="text/javascript">
10085  */
10086
10087 /**
10088 /**
10089  * @extends Roo.data.Store
10090  * @class Roo.data.JsonStore
10091  * Small helper class to make creating Stores for JSON data easier. <br/>
10092 <pre><code>
10093 var store = new Roo.data.JsonStore({
10094     url: 'get-images.php',
10095     root: 'images',
10096     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10097 });
10098 </code></pre>
10099  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10100  * JsonReader and HttpProxy (unless inline data is provided).</b>
10101  * @cfg {Array} fields An array of field definition objects, or field name strings.
10102  * @constructor
10103  * @param {Object} config
10104  */
10105 Roo.data.JsonStore = function(c){
10106     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10107         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10108         reader: new Roo.data.JsonReader(c, c.fields)
10109     }));
10110 };
10111 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10112  * Based on:
10113  * Ext JS Library 1.1.1
10114  * Copyright(c) 2006-2007, Ext JS, LLC.
10115  *
10116  * Originally Released Under LGPL - original licence link has changed is not relivant.
10117  *
10118  * Fork - LGPL
10119  * <script type="text/javascript">
10120  */
10121
10122  
10123 Roo.data.Field = function(config){
10124     if(typeof config == "string"){
10125         config = {name: config};
10126     }
10127     Roo.apply(this, config);
10128     
10129     if(!this.type){
10130         this.type = "auto";
10131     }
10132     
10133     var st = Roo.data.SortTypes;
10134     // named sortTypes are supported, here we look them up
10135     if(typeof this.sortType == "string"){
10136         this.sortType = st[this.sortType];
10137     }
10138     
10139     // set default sortType for strings and dates
10140     if(!this.sortType){
10141         switch(this.type){
10142             case "string":
10143                 this.sortType = st.asUCString;
10144                 break;
10145             case "date":
10146                 this.sortType = st.asDate;
10147                 break;
10148             default:
10149                 this.sortType = st.none;
10150         }
10151     }
10152
10153     // define once
10154     var stripRe = /[\$,%]/g;
10155
10156     // prebuilt conversion function for this field, instead of
10157     // switching every time we're reading a value
10158     if(!this.convert){
10159         var cv, dateFormat = this.dateFormat;
10160         switch(this.type){
10161             case "":
10162             case "auto":
10163             case undefined:
10164                 cv = function(v){ return v; };
10165                 break;
10166             case "string":
10167                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10168                 break;
10169             case "int":
10170                 cv = function(v){
10171                     return v !== undefined && v !== null && v !== '' ?
10172                            parseInt(String(v).replace(stripRe, ""), 10) : '';
10173                     };
10174                 break;
10175             case "float":
10176                 cv = function(v){
10177                     return v !== undefined && v !== null && v !== '' ?
10178                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
10179                     };
10180                 break;
10181             case "bool":
10182             case "boolean":
10183                 cv = function(v){ return v === true || v === "true" || v == 1; };
10184                 break;
10185             case "date":
10186                 cv = function(v){
10187                     if(!v){
10188                         return '';
10189                     }
10190                     if(v instanceof Date){
10191                         return v;
10192                     }
10193                     if(dateFormat){
10194                         if(dateFormat == "timestamp"){
10195                             return new Date(v*1000);
10196                         }
10197                         return Date.parseDate(v, dateFormat);
10198                     }
10199                     var parsed = Date.parse(v);
10200                     return parsed ? new Date(parsed) : null;
10201                 };
10202              break;
10203             
10204         }
10205         this.convert = cv;
10206     }
10207 };
10208
10209 Roo.data.Field.prototype = {
10210     dateFormat: null,
10211     defaultValue: "",
10212     mapping: null,
10213     sortType : null,
10214     sortDir : "ASC"
10215 };/*
10216  * Based on:
10217  * Ext JS Library 1.1.1
10218  * Copyright(c) 2006-2007, Ext JS, LLC.
10219  *
10220  * Originally Released Under LGPL - original licence link has changed is not relivant.
10221  *
10222  * Fork - LGPL
10223  * <script type="text/javascript">
10224  */
10225  
10226 // Base class for reading structured data from a data source.  This class is intended to be
10227 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10228
10229 /**
10230  * @class Roo.data.DataReader
10231  * Base class for reading structured data from a data source.  This class is intended to be
10232  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10233  */
10234
10235 Roo.data.DataReader = function(meta, recordType){
10236     
10237     this.meta = meta;
10238     
10239     this.recordType = recordType instanceof Array ? 
10240         Roo.data.Record.create(recordType) : recordType;
10241 };
10242
10243 Roo.data.DataReader.prototype = {
10244      /**
10245      * Create an empty record
10246      * @param {Object} data (optional) - overlay some values
10247      * @return {Roo.data.Record} record created.
10248      */
10249     newRow :  function(d) {
10250         var da =  {};
10251         this.recordType.prototype.fields.each(function(c) {
10252             switch( c.type) {
10253                 case 'int' : da[c.name] = 0; break;
10254                 case 'date' : da[c.name] = new Date(); break;
10255                 case 'float' : da[c.name] = 0.0; break;
10256                 case 'boolean' : da[c.name] = false; break;
10257                 default : da[c.name] = ""; break;
10258             }
10259             
10260         });
10261         return new this.recordType(Roo.apply(da, d));
10262     }
10263     
10264 };/*
10265  * Based on:
10266  * Ext JS Library 1.1.1
10267  * Copyright(c) 2006-2007, Ext JS, LLC.
10268  *
10269  * Originally Released Under LGPL - original licence link has changed is not relivant.
10270  *
10271  * Fork - LGPL
10272  * <script type="text/javascript">
10273  */
10274
10275 /**
10276  * @class Roo.data.DataProxy
10277  * @extends Roo.data.Observable
10278  * This class is an abstract base class for implementations which provide retrieval of
10279  * unformatted data objects.<br>
10280  * <p>
10281  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10282  * (of the appropriate type which knows how to parse the data object) to provide a block of
10283  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10284  * <p>
10285  * Custom implementations must implement the load method as described in
10286  * {@link Roo.data.HttpProxy#load}.
10287  */
10288 Roo.data.DataProxy = function(){
10289     this.addEvents({
10290         /**
10291          * @event beforeload
10292          * Fires before a network request is made to retrieve a data object.
10293          * @param {Object} This DataProxy object.
10294          * @param {Object} params The params parameter to the load function.
10295          */
10296         beforeload : true,
10297         /**
10298          * @event load
10299          * Fires before the load method's callback is called.
10300          * @param {Object} This DataProxy object.
10301          * @param {Object} o The data object.
10302          * @param {Object} arg The callback argument object passed to the load function.
10303          */
10304         load : true,
10305         /**
10306          * @event loadexception
10307          * Fires if an Exception occurs during data retrieval.
10308          * @param {Object} This DataProxy object.
10309          * @param {Object} o The data object.
10310          * @param {Object} arg The callback argument object passed to the load function.
10311          * @param {Object} e The Exception.
10312          */
10313         loadexception : true
10314     });
10315     Roo.data.DataProxy.superclass.constructor.call(this);
10316 };
10317
10318 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10319
10320     /**
10321      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10322      */
10323 /*
10324  * Based on:
10325  * Ext JS Library 1.1.1
10326  * Copyright(c) 2006-2007, Ext JS, LLC.
10327  *
10328  * Originally Released Under LGPL - original licence link has changed is not relivant.
10329  *
10330  * Fork - LGPL
10331  * <script type="text/javascript">
10332  */
10333 /**
10334  * @class Roo.data.MemoryProxy
10335  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10336  * to the Reader when its load method is called.
10337  * @constructor
10338  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10339  */
10340 Roo.data.MemoryProxy = function(data){
10341     if (data.data) {
10342         data = data.data;
10343     }
10344     Roo.data.MemoryProxy.superclass.constructor.call(this);
10345     this.data = data;
10346 };
10347
10348 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10349     /**
10350      * Load data from the requested source (in this case an in-memory
10351      * data object passed to the constructor), read the data object into
10352      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10353      * process that block using the passed callback.
10354      * @param {Object} params This parameter is not used by the MemoryProxy class.
10355      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10356      * object into a block of Roo.data.Records.
10357      * @param {Function} callback The function into which to pass the block of Roo.data.records.
10358      * The function must be passed <ul>
10359      * <li>The Record block object</li>
10360      * <li>The "arg" argument from the load function</li>
10361      * <li>A boolean success indicator</li>
10362      * </ul>
10363      * @param {Object} scope The scope in which to call the callback
10364      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10365      */
10366     load : function(params, reader, callback, scope, arg){
10367         params = params || {};
10368         var result;
10369         try {
10370             result = reader.readRecords(this.data);
10371         }catch(e){
10372             this.fireEvent("loadexception", this, arg, null, e);
10373             callback.call(scope, null, arg, false);
10374             return;
10375         }
10376         callback.call(scope, result, arg, true);
10377     },
10378     
10379     // private
10380     update : function(params, records){
10381         
10382     }
10383 });/*
10384  * Based on:
10385  * Ext JS Library 1.1.1
10386  * Copyright(c) 2006-2007, Ext JS, LLC.
10387  *
10388  * Originally Released Under LGPL - original licence link has changed is not relivant.
10389  *
10390  * Fork - LGPL
10391  * <script type="text/javascript">
10392  */
10393 /**
10394  * @class Roo.data.HttpProxy
10395  * @extends Roo.data.DataProxy
10396  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10397  * configured to reference a certain URL.<br><br>
10398  * <p>
10399  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10400  * from which the running page was served.<br><br>
10401  * <p>
10402  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10403  * <p>
10404  * Be aware that to enable the browser to parse an XML document, the server must set
10405  * the Content-Type header in the HTTP response to "text/xml".
10406  * @constructor
10407  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10408  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
10409  * will be used to make the request.
10410  */
10411 Roo.data.HttpProxy = function(conn){
10412     Roo.data.HttpProxy.superclass.constructor.call(this);
10413     // is conn a conn config or a real conn?
10414     this.conn = conn;
10415     this.useAjax = !conn || !conn.events;
10416   
10417 };
10418
10419 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10420     // thse are take from connection...
10421     
10422     /**
10423      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10424      */
10425     /**
10426      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10427      * extra parameters to each request made by this object. (defaults to undefined)
10428      */
10429     /**
10430      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10431      *  to each request made by this object. (defaults to undefined)
10432      */
10433     /**
10434      * @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)
10435      */
10436     /**
10437      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10438      */
10439      /**
10440      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10441      * @type Boolean
10442      */
10443   
10444
10445     /**
10446      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10447      * @type Boolean
10448      */
10449     /**
10450      * Return the {@link Roo.data.Connection} object being used by this Proxy.
10451      * @return {Connection} The Connection object. This object may be used to subscribe to events on
10452      * a finer-grained basis than the DataProxy events.
10453      */
10454     getConnection : function(){
10455         return this.useAjax ? Roo.Ajax : this.conn;
10456     },
10457
10458     /**
10459      * Load data from the configured {@link Roo.data.Connection}, read the data object into
10460      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10461      * process that block using the passed callback.
10462      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10463      * for the request to the remote server.
10464      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10465      * object into a block of Roo.data.Records.
10466      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10467      * The function must be passed <ul>
10468      * <li>The Record block object</li>
10469      * <li>The "arg" argument from the load function</li>
10470      * <li>A boolean success indicator</li>
10471      * </ul>
10472      * @param {Object} scope The scope in which to call the callback
10473      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10474      */
10475     load : function(params, reader, callback, scope, arg){
10476         if(this.fireEvent("beforeload", this, params) !== false){
10477             var  o = {
10478                 params : params || {},
10479                 request: {
10480                     callback : callback,
10481                     scope : scope,
10482                     arg : arg
10483                 },
10484                 reader: reader,
10485                 callback : this.loadResponse,
10486                 scope: this
10487             };
10488             if(this.useAjax){
10489                 Roo.applyIf(o, this.conn);
10490                 if(this.activeRequest){
10491                     Roo.Ajax.abort(this.activeRequest);
10492                 }
10493                 this.activeRequest = Roo.Ajax.request(o);
10494             }else{
10495                 this.conn.request(o);
10496             }
10497         }else{
10498             callback.call(scope||this, null, arg, false);
10499         }
10500     },
10501
10502     // private
10503     loadResponse : function(o, success, response){
10504         delete this.activeRequest;
10505         if(!success){
10506             this.fireEvent("loadexception", this, o, response);
10507             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10508             return;
10509         }
10510         var result;
10511         try {
10512             result = o.reader.read(response);
10513         }catch(e){
10514             this.fireEvent("loadexception", this, o, response, e);
10515             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10516             return;
10517         }
10518         
10519         this.fireEvent("load", this, o, o.request.arg);
10520         o.request.callback.call(o.request.scope, result, o.request.arg, true);
10521     },
10522
10523     // private
10524     update : function(dataSet){
10525
10526     },
10527
10528     // private
10529     updateResponse : function(dataSet){
10530
10531     }
10532 });/*
10533  * Based on:
10534  * Ext JS Library 1.1.1
10535  * Copyright(c) 2006-2007, Ext JS, LLC.
10536  *
10537  * Originally Released Under LGPL - original licence link has changed is not relivant.
10538  *
10539  * Fork - LGPL
10540  * <script type="text/javascript">
10541  */
10542
10543 /**
10544  * @class Roo.data.ScriptTagProxy
10545  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10546  * other than the originating domain of the running page.<br><br>
10547  * <p>
10548  * <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
10549  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10550  * <p>
10551  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10552  * source code that is used as the source inside a &lt;script> tag.<br><br>
10553  * <p>
10554  * In order for the browser to process the returned data, the server must wrap the data object
10555  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10556  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10557  * depending on whether the callback name was passed:
10558  * <p>
10559  * <pre><code>
10560 boolean scriptTag = false;
10561 String cb = request.getParameter("callback");
10562 if (cb != null) {
10563     scriptTag = true;
10564     response.setContentType("text/javascript");
10565 } else {
10566     response.setContentType("application/x-json");
10567 }
10568 Writer out = response.getWriter();
10569 if (scriptTag) {
10570     out.write(cb + "(");
10571 }
10572 out.print(dataBlock.toJsonString());
10573 if (scriptTag) {
10574     out.write(");");
10575 }
10576 </pre></code>
10577  *
10578  * @constructor
10579  * @param {Object} config A configuration object.
10580  */
10581 Roo.data.ScriptTagProxy = function(config){
10582     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10583     Roo.apply(this, config);
10584     this.head = document.getElementsByTagName("head")[0];
10585 };
10586
10587 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10588
10589 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10590     /**
10591      * @cfg {String} url The URL from which to request the data object.
10592      */
10593     /**
10594      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10595      */
10596     timeout : 30000,
10597     /**
10598      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10599      * the server the name of the callback function set up by the load call to process the returned data object.
10600      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10601      * javascript output which calls this named function passing the data object as its only parameter.
10602      */
10603     callbackParam : "callback",
10604     /**
10605      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10606      * name to the request.
10607      */
10608     nocache : true,
10609
10610     /**
10611      * Load data from the configured URL, read the data object into
10612      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10613      * process that block using the passed callback.
10614      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10615      * for the request to the remote server.
10616      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10617      * object into a block of Roo.data.Records.
10618      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10619      * The function must be passed <ul>
10620      * <li>The Record block object</li>
10621      * <li>The "arg" argument from the load function</li>
10622      * <li>A boolean success indicator</li>
10623      * </ul>
10624      * @param {Object} scope The scope in which to call the callback
10625      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10626      */
10627     load : function(params, reader, callback, scope, arg){
10628         if(this.fireEvent("beforeload", this, params) !== false){
10629
10630             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10631
10632             var url = this.url;
10633             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10634             if(this.nocache){
10635                 url += "&_dc=" + (new Date().getTime());
10636             }
10637             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10638             var trans = {
10639                 id : transId,
10640                 cb : "stcCallback"+transId,
10641                 scriptId : "stcScript"+transId,
10642                 params : params,
10643                 arg : arg,
10644                 url : url,
10645                 callback : callback,
10646                 scope : scope,
10647                 reader : reader
10648             };
10649             var conn = this;
10650
10651             window[trans.cb] = function(o){
10652                 conn.handleResponse(o, trans);
10653             };
10654
10655             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10656
10657             if(this.autoAbort !== false){
10658                 this.abort();
10659             }
10660
10661             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10662
10663             var script = document.createElement("script");
10664             script.setAttribute("src", url);
10665             script.setAttribute("type", "text/javascript");
10666             script.setAttribute("id", trans.scriptId);
10667             this.head.appendChild(script);
10668
10669             this.trans = trans;
10670         }else{
10671             callback.call(scope||this, null, arg, false);
10672         }
10673     },
10674
10675     // private
10676     isLoading : function(){
10677         return this.trans ? true : false;
10678     },
10679
10680     /**
10681      * Abort the current server request.
10682      */
10683     abort : function(){
10684         if(this.isLoading()){
10685             this.destroyTrans(this.trans);
10686         }
10687     },
10688
10689     // private
10690     destroyTrans : function(trans, isLoaded){
10691         this.head.removeChild(document.getElementById(trans.scriptId));
10692         clearTimeout(trans.timeoutId);
10693         if(isLoaded){
10694             window[trans.cb] = undefined;
10695             try{
10696                 delete window[trans.cb];
10697             }catch(e){}
10698         }else{
10699             // if hasn't been loaded, wait for load to remove it to prevent script error
10700             window[trans.cb] = function(){
10701                 window[trans.cb] = undefined;
10702                 try{
10703                     delete window[trans.cb];
10704                 }catch(e){}
10705             };
10706         }
10707     },
10708
10709     // private
10710     handleResponse : function(o, trans){
10711         this.trans = false;
10712         this.destroyTrans(trans, true);
10713         var result;
10714         try {
10715             result = trans.reader.readRecords(o);
10716         }catch(e){
10717             this.fireEvent("loadexception", this, o, trans.arg, e);
10718             trans.callback.call(trans.scope||window, null, trans.arg, false);
10719             return;
10720         }
10721         this.fireEvent("load", this, o, trans.arg);
10722         trans.callback.call(trans.scope||window, result, trans.arg, true);
10723     },
10724
10725     // private
10726     handleFailure : function(trans){
10727         this.trans = false;
10728         this.destroyTrans(trans, false);
10729         this.fireEvent("loadexception", this, null, trans.arg);
10730         trans.callback.call(trans.scope||window, null, trans.arg, false);
10731     }
10732 });/*
10733  * Based on:
10734  * Ext JS Library 1.1.1
10735  * Copyright(c) 2006-2007, Ext JS, LLC.
10736  *
10737  * Originally Released Under LGPL - original licence link has changed is not relivant.
10738  *
10739  * Fork - LGPL
10740  * <script type="text/javascript">
10741  */
10742
10743 /**
10744  * @class Roo.data.JsonReader
10745  * @extends Roo.data.DataReader
10746  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10747  * based on mappings in a provided Roo.data.Record constructor.
10748  * 
10749  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10750  * in the reply previously. 
10751  * 
10752  * <p>
10753  * Example code:
10754  * <pre><code>
10755 var RecordDef = Roo.data.Record.create([
10756     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
10757     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
10758 ]);
10759 var myReader = new Roo.data.JsonReader({
10760     totalProperty: "results",    // The property which contains the total dataset size (optional)
10761     root: "rows",                // The property which contains an Array of row objects
10762     id: "id"                     // The property within each row object that provides an ID for the record (optional)
10763 }, RecordDef);
10764 </code></pre>
10765  * <p>
10766  * This would consume a JSON file like this:
10767  * <pre><code>
10768 { 'results': 2, 'rows': [
10769     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10770     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10771 }
10772 </code></pre>
10773  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10774  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10775  * paged from the remote server.
10776  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10777  * @cfg {String} root name of the property which contains the Array of row objects.
10778  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10779  * @cfg {Array} fields Array of field definition objects
10780  * @constructor
10781  * Create a new JsonReader
10782  * @param {Object} meta Metadata configuration options
10783  * @param {Object} recordType Either an Array of field definition objects,
10784  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10785  */
10786 Roo.data.JsonReader = function(meta, recordType){
10787     
10788     meta = meta || {};
10789     // set some defaults:
10790     Roo.applyIf(meta, {
10791         totalProperty: 'total',
10792         successProperty : 'success',
10793         root : 'data',
10794         id : 'id'
10795     });
10796     
10797     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10798 };
10799 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10800     
10801     /**
10802      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
10803      * Used by Store query builder to append _requestMeta to params.
10804      * 
10805      */
10806     metaFromRemote : false,
10807     /**
10808      * This method is only used by a DataProxy which has retrieved data from a remote server.
10809      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10810      * @return {Object} data A data block which is used by an Roo.data.Store object as
10811      * a cache of Roo.data.Records.
10812      */
10813     read : function(response){
10814         var json = response.responseText;
10815        
10816         var o = /* eval:var:o */ eval("("+json+")");
10817         if(!o) {
10818             throw {message: "JsonReader.read: Json object not found"};
10819         }
10820         
10821         if(o.metaData){
10822             
10823             delete this.ef;
10824             this.metaFromRemote = true;
10825             this.meta = o.metaData;
10826             this.recordType = Roo.data.Record.create(o.metaData.fields);
10827             this.onMetaChange(this.meta, this.recordType, o);
10828         }
10829         return this.readRecords(o);
10830     },
10831
10832     // private function a store will implement
10833     onMetaChange : function(meta, recordType, o){
10834
10835     },
10836
10837     /**
10838          * @ignore
10839          */
10840     simpleAccess: function(obj, subsc) {
10841         return obj[subsc];
10842     },
10843
10844         /**
10845          * @ignore
10846          */
10847     getJsonAccessor: function(){
10848         var re = /[\[\.]/;
10849         return function(expr) {
10850             try {
10851                 return(re.test(expr))
10852                     ? new Function("obj", "return obj." + expr)
10853                     : function(obj){
10854                         return obj[expr];
10855                     };
10856             } catch(e){}
10857             return Roo.emptyFn;
10858         };
10859     }(),
10860
10861     /**
10862      * Create a data block containing Roo.data.Records from an XML document.
10863      * @param {Object} o An object which contains an Array of row objects in the property specified
10864      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10865      * which contains the total size of the dataset.
10866      * @return {Object} data A data block which is used by an Roo.data.Store object as
10867      * a cache of Roo.data.Records.
10868      */
10869     readRecords : function(o){
10870         /**
10871          * After any data loads, the raw JSON data is available for further custom processing.
10872          * @type Object
10873          */
10874         this.o = o;
10875         var s = this.meta, Record = this.recordType,
10876             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
10877
10878 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10879         if (!this.ef) {
10880             if(s.totalProperty) {
10881                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10882                 }
10883                 if(s.successProperty) {
10884                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10885                 }
10886                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10887                 if (s.id) {
10888                         var g = this.getJsonAccessor(s.id);
10889                         this.getId = function(rec) {
10890                                 var r = g(rec);  
10891                                 return (r === undefined || r === "") ? null : r;
10892                         };
10893                 } else {
10894                         this.getId = function(){return null;};
10895                 }
10896             this.ef = [];
10897             for(var jj = 0; jj < fl; jj++){
10898                 f = fi[jj];
10899                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10900                 this.ef[jj] = this.getJsonAccessor(map);
10901             }
10902         }
10903
10904         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10905         if(s.totalProperty){
10906             var vt = parseInt(this.getTotal(o), 10);
10907             if(!isNaN(vt)){
10908                 totalRecords = vt;
10909             }
10910         }
10911         if(s.successProperty){
10912             var vs = this.getSuccess(o);
10913             if(vs === false || vs === 'false'){
10914                 success = false;
10915             }
10916         }
10917         var records = [];
10918         for(var i = 0; i < c; i++){
10919                 var n = root[i];
10920             var values = {};
10921             var id = this.getId(n);
10922             for(var j = 0; j < fl; j++){
10923                 f = fi[j];
10924             var v = this.ef[j](n);
10925             if (!f.convert) {
10926                 Roo.log('missing convert for ' + f.name);
10927                 Roo.log(f);
10928                 continue;
10929             }
10930             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10931             }
10932             var record = new Record(values, id);
10933             record.json = n;
10934             records[i] = record;
10935         }
10936         return {
10937             raw : o,
10938             success : success,
10939             records : records,
10940             totalRecords : totalRecords
10941         };
10942     }
10943 });/*
10944  * Based on:
10945  * Ext JS Library 1.1.1
10946  * Copyright(c) 2006-2007, Ext JS, LLC.
10947  *
10948  * Originally Released Under LGPL - original licence link has changed is not relivant.
10949  *
10950  * Fork - LGPL
10951  * <script type="text/javascript">
10952  */
10953
10954 /**
10955  * @class Roo.data.ArrayReader
10956  * @extends Roo.data.DataReader
10957  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10958  * Each element of that Array represents a row of data fields. The
10959  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10960  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10961  * <p>
10962  * Example code:.
10963  * <pre><code>
10964 var RecordDef = Roo.data.Record.create([
10965     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10966     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10967 ]);
10968 var myReader = new Roo.data.ArrayReader({
10969     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10970 }, RecordDef);
10971 </code></pre>
10972  * <p>
10973  * This would consume an Array like this:
10974  * <pre><code>
10975 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10976   </code></pre>
10977  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10978  * @constructor
10979  * Create a new JsonReader
10980  * @param {Object} meta Metadata configuration options.
10981  * @param {Object} recordType Either an Array of field definition objects
10982  * as specified to {@link Roo.data.Record#create},
10983  * or an {@link Roo.data.Record} object
10984  * created using {@link Roo.data.Record#create}.
10985  */
10986 Roo.data.ArrayReader = function(meta, recordType){
10987     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10988 };
10989
10990 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10991     /**
10992      * Create a data block containing Roo.data.Records from an XML document.
10993      * @param {Object} o An Array of row objects which represents the dataset.
10994      * @return {Object} data A data block which is used by an Roo.data.Store object as
10995      * a cache of Roo.data.Records.
10996      */
10997     readRecords : function(o){
10998         var sid = this.meta ? this.meta.id : null;
10999         var recordType = this.recordType, fields = recordType.prototype.fields;
11000         var records = [];
11001         var root = o;
11002             for(var i = 0; i < root.length; i++){
11003                     var n = root[i];
11004                 var values = {};
11005                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11006                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11007                 var f = fields.items[j];
11008                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11009                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11010                 v = f.convert(v);
11011                 values[f.name] = v;
11012             }
11013                 var record = new recordType(values, id);
11014                 record.json = n;
11015                 records[records.length] = record;
11016             }
11017             return {
11018                 records : records,
11019                 totalRecords : records.length
11020             };
11021     }
11022 });/*
11023  * - LGPL
11024  * * 
11025  */
11026
11027 /**
11028  * @class Roo.bootstrap.ComboBox
11029  * @extends Roo.bootstrap.TriggerField
11030  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11031  * @cfg {Boolean} append (true|false) default false
11032  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11033  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11034  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11035  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11036  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11037  * @constructor
11038  * Create a new ComboBox.
11039  * @param {Object} config Configuration options
11040  */
11041 Roo.bootstrap.ComboBox = function(config){
11042     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11043     this.addEvents({
11044         /**
11045          * @event expand
11046          * Fires when the dropdown list is expanded
11047              * @param {Roo.bootstrap.ComboBox} combo This combo box
11048              */
11049         'expand' : true,
11050         /**
11051          * @event collapse
11052          * Fires when the dropdown list is collapsed
11053              * @param {Roo.bootstrap.ComboBox} combo This combo box
11054              */
11055         'collapse' : true,
11056         /**
11057          * @event beforeselect
11058          * Fires before a list item is selected. Return false to cancel the selection.
11059              * @param {Roo.bootstrap.ComboBox} combo This combo box
11060              * @param {Roo.data.Record} record The data record returned from the underlying store
11061              * @param {Number} index The index of the selected item in the dropdown list
11062              */
11063         'beforeselect' : true,
11064         /**
11065          * @event select
11066          * Fires when a list item is selected
11067              * @param {Roo.bootstrap.ComboBox} combo This combo box
11068              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11069              * @param {Number} index The index of the selected item in the dropdown list
11070              */
11071         'select' : true,
11072         /**
11073          * @event beforequery
11074          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11075          * The event object passed has these properties:
11076              * @param {Roo.bootstrap.ComboBox} combo This combo box
11077              * @param {String} query The query
11078              * @param {Boolean} forceAll true to force "all" query
11079              * @param {Boolean} cancel true to cancel the query
11080              * @param {Object} e The query event object
11081              */
11082         'beforequery': true,
11083          /**
11084          * @event add
11085          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11086              * @param {Roo.bootstrap.ComboBox} combo This combo box
11087              */
11088         'add' : true,
11089         /**
11090          * @event edit
11091          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11092              * @param {Roo.bootstrap.ComboBox} combo This combo box
11093              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11094              */
11095         'edit' : true,
11096         /**
11097          * @event remove
11098          * Fires when the remove value from the combobox array
11099              * @param {Roo.bootstrap.ComboBox} combo This combo box
11100              */
11101         'remove' : true,
11102         /**
11103          * @event specialfilter
11104          * Fires when specialfilter
11105             * @param {Roo.bootstrap.ComboBox} combo This combo box
11106             */
11107         'specialfilter' : true
11108         
11109     });
11110     
11111     this.item = [];
11112     this.tickItems = [];
11113     
11114     this.selectedIndex = -1;
11115     if(this.mode == 'local'){
11116         if(config.queryDelay === undefined){
11117             this.queryDelay = 10;
11118         }
11119         if(config.minChars === undefined){
11120             this.minChars = 0;
11121         }
11122     }
11123 };
11124
11125 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11126      
11127     /**
11128      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11129      * rendering into an Roo.Editor, defaults to false)
11130      */
11131     /**
11132      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11133      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11134      */
11135     /**
11136      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11137      */
11138     /**
11139      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11140      * the dropdown list (defaults to undefined, with no header element)
11141      */
11142
11143      /**
11144      * @cfg {String/Roo.Template} tpl The template to use to render the output
11145      */
11146      
11147      /**
11148      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11149      */
11150     listWidth: undefined,
11151     /**
11152      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11153      * mode = 'remote' or 'text' if mode = 'local')
11154      */
11155     displayField: undefined,
11156     
11157     /**
11158      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11159      * mode = 'remote' or 'value' if mode = 'local'). 
11160      * Note: use of a valueField requires the user make a selection
11161      * in order for a value to be mapped.
11162      */
11163     valueField: undefined,
11164     
11165     
11166     /**
11167      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11168      * field's data value (defaults to the underlying DOM element's name)
11169      */
11170     hiddenName: undefined,
11171     /**
11172      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11173      */
11174     listClass: '',
11175     /**
11176      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11177      */
11178     selectedClass: 'active',
11179     
11180     /**
11181      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11182      */
11183     shadow:'sides',
11184     /**
11185      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11186      * anchor positions (defaults to 'tl-bl')
11187      */
11188     listAlign: 'tl-bl?',
11189     /**
11190      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11191      */
11192     maxHeight: 300,
11193     /**
11194      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
11195      * query specified by the allQuery config option (defaults to 'query')
11196      */
11197     triggerAction: 'query',
11198     /**
11199      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11200      * (defaults to 4, does not apply if editable = false)
11201      */
11202     minChars : 4,
11203     /**
11204      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11205      * delay (typeAheadDelay) if it matches a known value (defaults to false)
11206      */
11207     typeAhead: false,
11208     /**
11209      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11210      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11211      */
11212     queryDelay: 500,
11213     /**
11214      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11215      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
11216      */
11217     pageSize: 0,
11218     /**
11219      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
11220      * when editable = true (defaults to false)
11221      */
11222     selectOnFocus:false,
11223     /**
11224      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11225      */
11226     queryParam: 'query',
11227     /**
11228      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
11229      * when mode = 'remote' (defaults to 'Loading...')
11230      */
11231     loadingText: 'Loading...',
11232     /**
11233      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11234      */
11235     resizable: false,
11236     /**
11237      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11238      */
11239     handleHeight : 8,
11240     /**
11241      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11242      * traditional select (defaults to true)
11243      */
11244     editable: true,
11245     /**
11246      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11247      */
11248     allQuery: '',
11249     /**
11250      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11251      */
11252     mode: 'remote',
11253     /**
11254      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11255      * listWidth has a higher value)
11256      */
11257     minListWidth : 70,
11258     /**
11259      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11260      * allow the user to set arbitrary text into the field (defaults to false)
11261      */
11262     forceSelection:false,
11263     /**
11264      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11265      * if typeAhead = true (defaults to 250)
11266      */
11267     typeAheadDelay : 250,
11268     /**
11269      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11270      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11271      */
11272     valueNotFoundText : undefined,
11273     /**
11274      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11275      */
11276     blockFocus : false,
11277     
11278     /**
11279      * @cfg {Boolean} disableClear Disable showing of clear button.
11280      */
11281     disableClear : false,
11282     /**
11283      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
11284      */
11285     alwaysQuery : false,
11286     
11287     /**
11288      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
11289      */
11290     multiple : false,
11291     
11292     /**
11293      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11294      */
11295     invalidClass : "has-warning",
11296     
11297     /**
11298      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11299      */
11300     validClass : "has-success",
11301     
11302     /**
11303      * @cfg {Boolean} specialFilter (true|false) special filter default false
11304      */
11305     specialFilter : false,
11306     
11307     //private
11308     addicon : false,
11309     editicon: false,
11310     
11311     page: 0,
11312     hasQuery: false,
11313     append: false,
11314     loadNext: false,
11315     autoFocus : true,
11316     tickable : false,
11317     btnPosition : 'right',
11318     triggerList : true,
11319     showToggleBtn : true,
11320     // element that contains real text value.. (when hidden is used..)
11321     
11322     getAutoCreate : function()
11323     {
11324         var cfg = false;
11325         
11326         /*
11327          *  Normal ComboBox
11328          */
11329         if(!this.tickable){
11330             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11331             return cfg;
11332         }
11333         
11334         /*
11335          *  ComboBox with tickable selections
11336          */
11337              
11338         var align = this.labelAlign || this.parentLabelAlign();
11339         
11340         cfg = {
11341             cls : 'form-group roo-combobox-tickable' //input-group
11342         };
11343         
11344         var buttons = {
11345             tag : 'div',
11346             cls : 'tickable-buttons',
11347             cn : [
11348                 {
11349                     tag : 'button',
11350                     type : 'button',
11351                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11352                     html : 'Edit'
11353                 },
11354                 {
11355                     tag : 'button',
11356                     type : 'button',
11357                     name : 'ok',
11358                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11359                     html : 'Done'
11360                 },
11361                 {
11362                     tag : 'button',
11363                     type : 'button',
11364                     name : 'cancel',
11365                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11366                     html : 'Cancel'
11367                 }
11368             ]
11369         };
11370         
11371         if(this.editable){
11372             buttons.cn.unshift({
11373                 tag: 'input',
11374                 cls: 'select2-search-field-input'
11375             });
11376         }
11377         
11378         var _this = this;
11379         
11380         Roo.each(buttons.cn, function(c){
11381             if (_this.size) {
11382                 c.cls += ' btn-' + _this.size;
11383             }
11384
11385             if (_this.disabled) {
11386                 c.disabled = true;
11387             }
11388         });
11389         
11390         var box = {
11391             tag: 'div',
11392             cn: [
11393                 {
11394                     tag: 'input',
11395                     type : 'hidden',
11396                     cls: 'form-hidden-field'
11397                 },
11398                 {
11399                     tag: 'ul',
11400                     cls: 'select2-choices',
11401                     cn:[
11402                         {
11403                             tag: 'li',
11404                             cls: 'select2-search-field',
11405                             cn: [
11406
11407                                 buttons
11408                             ]
11409                         }
11410                     ]
11411                 }
11412             ]
11413         }
11414         
11415         var combobox = {
11416             cls: 'select2-container input-group select2-container-multi',
11417             cn: [
11418                 box
11419 //                {
11420 //                    tag: 'ul',
11421 //                    cls: 'typeahead typeahead-long dropdown-menu',
11422 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
11423 //                }
11424             ]
11425         };
11426         
11427         if(this.hasFeedback && !this.allowBlank){
11428             
11429             var feedback = {
11430                 tag: 'span',
11431                 cls: 'glyphicon form-control-feedback'
11432             };
11433
11434             combobox.cn.push(feedback);
11435         }
11436         
11437         if (align ==='left' && this.fieldLabel.length) {
11438             
11439                 Roo.log("left and has label");
11440                 cfg.cn = [
11441                     
11442                     {
11443                         tag: 'label',
11444                         'for' :  id,
11445                         cls : 'control-label col-sm-' + this.labelWidth,
11446                         html : this.fieldLabel
11447                         
11448                     },
11449                     {
11450                         cls : "col-sm-" + (12 - this.labelWidth), 
11451                         cn: [
11452                             combobox
11453                         ]
11454                     }
11455                     
11456                 ];
11457         } else if ( this.fieldLabel.length) {
11458                 Roo.log(" label");
11459                  cfg.cn = [
11460                    
11461                     {
11462                         tag: 'label',
11463                         //cls : 'input-group-addon',
11464                         html : this.fieldLabel
11465                         
11466                     },
11467                     
11468                     combobox
11469                     
11470                 ];
11471
11472         } else {
11473             
11474                 Roo.log(" no label && no align");
11475                 cfg = combobox
11476                      
11477                 
11478         }
11479          
11480         var settings=this;
11481         ['xs','sm','md','lg'].map(function(size){
11482             if (settings[size]) {
11483                 cfg.cls += ' col-' + size + '-' + settings[size];
11484             }
11485         });
11486         
11487         return cfg;
11488         
11489     },
11490     
11491     // private
11492     initEvents: function()
11493     {
11494         
11495         if (!this.store) {
11496             throw "can not find store for combo";
11497         }
11498         this.store = Roo.factory(this.store, Roo.data);
11499         
11500         if(this.tickable){
11501             this.initTickableEvents();
11502             return;
11503         }
11504         
11505         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11506         
11507         if(this.hiddenName){
11508             
11509             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11510             
11511             this.hiddenField.dom.value =
11512                 this.hiddenValue !== undefined ? this.hiddenValue :
11513                 this.value !== undefined ? this.value : '';
11514
11515             // prevent input submission
11516             this.el.dom.removeAttribute('name');
11517             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11518              
11519              
11520         }
11521         //if(Roo.isGecko){
11522         //    this.el.dom.setAttribute('autocomplete', 'off');
11523         //}
11524         
11525         var cls = 'x-combo-list';
11526         
11527         //this.list = new Roo.Layer({
11528         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11529         //});
11530         
11531         var _this = this;
11532         
11533         (function(){
11534             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11535             _this.list.setWidth(lw);
11536         }).defer(100);
11537         
11538         this.list.on('mouseover', this.onViewOver, this);
11539         this.list.on('mousemove', this.onViewMove, this);
11540         
11541         this.list.on('scroll', this.onViewScroll, this);
11542         
11543         /*
11544         this.list.swallowEvent('mousewheel');
11545         this.assetHeight = 0;
11546
11547         if(this.title){
11548             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
11549             this.assetHeight += this.header.getHeight();
11550         }
11551
11552         this.innerList = this.list.createChild({cls:cls+'-inner'});
11553         this.innerList.on('mouseover', this.onViewOver, this);
11554         this.innerList.on('mousemove', this.onViewMove, this);
11555         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11556         
11557         if(this.allowBlank && !this.pageSize && !this.disableClear){
11558             this.footer = this.list.createChild({cls:cls+'-ft'});
11559             this.pageTb = new Roo.Toolbar(this.footer);
11560            
11561         }
11562         if(this.pageSize){
11563             this.footer = this.list.createChild({cls:cls+'-ft'});
11564             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
11565                     {pageSize: this.pageSize});
11566             
11567         }
11568         
11569         if (this.pageTb && this.allowBlank && !this.disableClear) {
11570             var _this = this;
11571             this.pageTb.add(new Roo.Toolbar.Fill(), {
11572                 cls: 'x-btn-icon x-btn-clear',
11573                 text: '&#160;',
11574                 handler: function()
11575                 {
11576                     _this.collapse();
11577                     _this.clearValue();
11578                     _this.onSelect(false, -1);
11579                 }
11580             });
11581         }
11582         if (this.footer) {
11583             this.assetHeight += this.footer.getHeight();
11584         }
11585         */
11586             
11587         if(!this.tpl){
11588             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11589         }
11590
11591         this.view = new Roo.View(this.list, this.tpl, {
11592             singleSelect:true, store: this.store, selectedClass: this.selectedClass
11593         });
11594         //this.view.wrapEl.setDisplayed(false);
11595         this.view.on('click', this.onViewClick, this);
11596         
11597         
11598         
11599         this.store.on('beforeload', this.onBeforeLoad, this);
11600         this.store.on('load', this.onLoad, this);
11601         this.store.on('loadexception', this.onLoadException, this);
11602         /*
11603         if(this.resizable){
11604             this.resizer = new Roo.Resizable(this.list,  {
11605                pinned:true, handles:'se'
11606             });
11607             this.resizer.on('resize', function(r, w, h){
11608                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11609                 this.listWidth = w;
11610                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11611                 this.restrictHeight();
11612             }, this);
11613             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11614         }
11615         */
11616         if(!this.editable){
11617             this.editable = true;
11618             this.setEditable(false);
11619         }
11620         
11621         /*
11622         
11623         if (typeof(this.events.add.listeners) != 'undefined') {
11624             
11625             this.addicon = this.wrap.createChild(
11626                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
11627        
11628             this.addicon.on('click', function(e) {
11629                 this.fireEvent('add', this);
11630             }, this);
11631         }
11632         if (typeof(this.events.edit.listeners) != 'undefined') {
11633             
11634             this.editicon = this.wrap.createChild(
11635                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
11636             if (this.addicon) {
11637                 this.editicon.setStyle('margin-left', '40px');
11638             }
11639             this.editicon.on('click', function(e) {
11640                 
11641                 // we fire even  if inothing is selected..
11642                 this.fireEvent('edit', this, this.lastData );
11643                 
11644             }, this);
11645         }
11646         */
11647         
11648         this.keyNav = new Roo.KeyNav(this.inputEl(), {
11649             "up" : function(e){
11650                 this.inKeyMode = true;
11651                 this.selectPrev();
11652             },
11653
11654             "down" : function(e){
11655                 if(!this.isExpanded()){
11656                     this.onTriggerClick();
11657                 }else{
11658                     this.inKeyMode = true;
11659                     this.selectNext();
11660                 }
11661             },
11662
11663             "enter" : function(e){
11664 //                this.onViewClick();
11665                 //return true;
11666                 this.collapse();
11667                 
11668                 if(this.fireEvent("specialkey", this, e)){
11669                     this.onViewClick(false);
11670                 }
11671                 
11672                 return true;
11673             },
11674
11675             "esc" : function(e){
11676                 this.collapse();
11677             },
11678
11679             "tab" : function(e){
11680                 this.collapse();
11681                 
11682                 if(this.fireEvent("specialkey", this, e)){
11683                     this.onViewClick(false);
11684                 }
11685                 
11686                 return true;
11687             },
11688
11689             scope : this,
11690
11691             doRelay : function(foo, bar, hname){
11692                 if(hname == 'down' || this.scope.isExpanded()){
11693                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11694                 }
11695                 return true;
11696             },
11697
11698             forceKeyDown: true
11699         });
11700         
11701         
11702         this.queryDelay = Math.max(this.queryDelay || 10,
11703                 this.mode == 'local' ? 10 : 250);
11704         
11705         
11706         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11707         
11708         if(this.typeAhead){
11709             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11710         }
11711         if(this.editable !== false){
11712             this.inputEl().on("keyup", this.onKeyUp, this);
11713         }
11714         if(this.forceSelection){
11715             this.inputEl().on('blur', this.doForce, this);
11716         }
11717         
11718         if(this.multiple){
11719             this.choices = this.el.select('ul.select2-choices', true).first();
11720             this.searchField = this.el.select('ul li.select2-search-field', true).first();
11721         }
11722     },
11723     
11724     initTickableEvents: function()
11725     {   
11726         this.createList();
11727         
11728         if(this.hiddenName){
11729             
11730             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11731             
11732             this.hiddenField.dom.value =
11733                 this.hiddenValue !== undefined ? this.hiddenValue :
11734                 this.value !== undefined ? this.value : '';
11735
11736             // prevent input submission
11737             this.el.dom.removeAttribute('name');
11738             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11739              
11740              
11741         }
11742         
11743 //        this.list = this.el.select('ul.dropdown-menu',true).first();
11744         
11745         this.choices = this.el.select('ul.select2-choices', true).first();
11746         this.searchField = this.el.select('ul li.select2-search-field', true).first();
11747         if(this.triggerList){
11748             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
11749         }
11750          
11751         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
11752         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
11753         
11754         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
11755         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
11756         
11757         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
11758         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
11759         
11760         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
11761         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
11762         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
11763         
11764         this.okBtn.hide();
11765         this.cancelBtn.hide();
11766         
11767         var _this = this;
11768         
11769         (function(){
11770             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11771             _this.list.setWidth(lw);
11772         }).defer(100);
11773         
11774         this.list.on('mouseover', this.onViewOver, this);
11775         this.list.on('mousemove', this.onViewMove, this);
11776         
11777         this.list.on('scroll', this.onViewScroll, this);
11778         
11779         if(!this.tpl){
11780             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>';
11781         }
11782
11783         this.view = new Roo.View(this.list, this.tpl, {
11784             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
11785         });
11786         
11787         //this.view.wrapEl.setDisplayed(false);
11788         this.view.on('click', this.onViewClick, this);
11789         
11790         
11791         
11792         this.store.on('beforeload', this.onBeforeLoad, this);
11793         this.store.on('load', this.onLoad, this);
11794         this.store.on('loadexception', this.onLoadException, this);
11795         
11796         if(this.editable){
11797             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
11798                 "up" : function(e){
11799                     this.inKeyMode = true;
11800                     this.selectPrev();
11801                 },
11802
11803                 "down" : function(e){
11804                     this.inKeyMode = true;
11805                     this.selectNext();
11806                 },
11807
11808                 "enter" : function(e){
11809                     if(this.fireEvent("specialkey", this, e)){
11810                         this.onViewClick(false);
11811                     }
11812                     
11813                     return true;
11814                 },
11815
11816                 "esc" : function(e){
11817                     this.onTickableFooterButtonClick(e, false, false);
11818                 },
11819
11820                 "tab" : function(e){
11821                     this.fireEvent("specialkey", this, e);
11822                     
11823                     this.onTickableFooterButtonClick(e, false, false);
11824                     
11825                     return true;
11826                 },
11827
11828                 scope : this,
11829
11830                 doRelay : function(e, fn, key){
11831                     if(this.scope.isExpanded()){
11832                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11833                     }
11834                     return true;
11835                 },
11836
11837                 forceKeyDown: true
11838             });
11839         }
11840         
11841         this.queryDelay = Math.max(this.queryDelay || 10,
11842                 this.mode == 'local' ? 10 : 250);
11843         
11844         
11845         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11846         
11847         if(this.typeAhead){
11848             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11849         }
11850         
11851         if(this.editable !== false){
11852             this.tickableInputEl().on("keyup", this.onKeyUp, this);
11853         }
11854         
11855     },
11856
11857     onDestroy : function(){
11858         if(this.view){
11859             this.view.setStore(null);
11860             this.view.el.removeAllListeners();
11861             this.view.el.remove();
11862             this.view.purgeListeners();
11863         }
11864         if(this.list){
11865             this.list.dom.innerHTML  = '';
11866         }
11867         
11868         if(this.store){
11869             this.store.un('beforeload', this.onBeforeLoad, this);
11870             this.store.un('load', this.onLoad, this);
11871             this.store.un('loadexception', this.onLoadException, this);
11872         }
11873         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11874     },
11875
11876     // private
11877     fireKey : function(e){
11878         if(e.isNavKeyPress() && !this.list.isVisible()){
11879             this.fireEvent("specialkey", this, e);
11880         }
11881     },
11882
11883     // private
11884     onResize: function(w, h){
11885 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11886 //        
11887 //        if(typeof w != 'number'){
11888 //            // we do not handle it!?!?
11889 //            return;
11890 //        }
11891 //        var tw = this.trigger.getWidth();
11892 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11893 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11894 //        var x = w - tw;
11895 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11896 //            
11897 //        //this.trigger.setStyle('left', x+'px');
11898 //        
11899 //        if(this.list && this.listWidth === undefined){
11900 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11901 //            this.list.setWidth(lw);
11902 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11903 //        }
11904         
11905     
11906         
11907     },
11908
11909     /**
11910      * Allow or prevent the user from directly editing the field text.  If false is passed,
11911      * the user will only be able to select from the items defined in the dropdown list.  This method
11912      * is the runtime equivalent of setting the 'editable' config option at config time.
11913      * @param {Boolean} value True to allow the user to directly edit the field text
11914      */
11915     setEditable : function(value){
11916         if(value == this.editable){
11917             return;
11918         }
11919         this.editable = value;
11920         if(!value){
11921             this.inputEl().dom.setAttribute('readOnly', true);
11922             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11923             this.inputEl().addClass('x-combo-noedit');
11924         }else{
11925             this.inputEl().dom.setAttribute('readOnly', false);
11926             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11927             this.inputEl().removeClass('x-combo-noedit');
11928         }
11929     },
11930
11931     // private
11932     
11933     onBeforeLoad : function(combo,opts){
11934         if(!this.hasFocus){
11935             return;
11936         }
11937          if (!opts.add) {
11938             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11939          }
11940         this.restrictHeight();
11941         this.selectedIndex = -1;
11942     },
11943
11944     // private
11945     onLoad : function(){
11946         
11947         this.hasQuery = false;
11948         
11949         if(!this.hasFocus){
11950             return;
11951         }
11952         
11953         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11954             this.loading.hide();
11955         }
11956              
11957         if(this.store.getCount() > 0){
11958             this.expand();
11959             this.restrictHeight();
11960             if(this.lastQuery == this.allQuery){
11961                 if(this.editable && !this.tickable){
11962                     this.inputEl().dom.select();
11963                 }
11964                 
11965                 if(
11966                     !this.selectByValue(this.value, true) &&
11967                     this.autoFocus && 
11968                     (
11969                         !this.store.lastOptions ||
11970                         typeof(this.store.lastOptions.add) == 'undefined' || 
11971                         this.store.lastOptions.add != true
11972                     )
11973                 ){
11974                     this.select(0, true);
11975                 }
11976             }else{
11977                 if(this.autoFocus){
11978                     this.selectNext();
11979                 }
11980                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11981                     this.taTask.delay(this.typeAheadDelay);
11982                 }
11983             }
11984         }else{
11985             this.onEmptyResults();
11986         }
11987         
11988         //this.el.focus();
11989     },
11990     // private
11991     onLoadException : function()
11992     {
11993         this.hasQuery = false;
11994         
11995         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11996             this.loading.hide();
11997         }
11998         
11999         if(this.tickable && this.editable){
12000             return;
12001         }
12002         
12003         this.collapse();
12004         
12005         Roo.log(this.store.reader.jsonData);
12006         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12007             // fixme
12008             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12009         }
12010         
12011         
12012     },
12013     // private
12014     onTypeAhead : function(){
12015         if(this.store.getCount() > 0){
12016             var r = this.store.getAt(0);
12017             var newValue = r.data[this.displayField];
12018             var len = newValue.length;
12019             var selStart = this.getRawValue().length;
12020             
12021             if(selStart != len){
12022                 this.setRawValue(newValue);
12023                 this.selectText(selStart, newValue.length);
12024             }
12025         }
12026     },
12027
12028     // private
12029     onSelect : function(record, index){
12030         
12031         if(this.fireEvent('beforeselect', this, record, index) !== false){
12032         
12033             this.setFromData(index > -1 ? record.data : false);
12034             
12035             this.collapse();
12036             this.fireEvent('select', this, record, index);
12037         }
12038     },
12039
12040     /**
12041      * Returns the currently selected field value or empty string if no value is set.
12042      * @return {String} value The selected value
12043      */
12044     getValue : function(){
12045         
12046         if(this.multiple){
12047             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12048         }
12049         
12050         if(this.valueField){
12051             return typeof this.value != 'undefined' ? this.value : '';
12052         }else{
12053             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12054         }
12055     },
12056
12057     /**
12058      * Clears any text/value currently set in the field
12059      */
12060     clearValue : function(){
12061         if(this.hiddenField){
12062             this.hiddenField.dom.value = '';
12063         }
12064         this.value = '';
12065         this.setRawValue('');
12066         this.lastSelectionText = '';
12067         this.lastData = false;
12068         
12069         var close = this.closeTriggerEl();
12070         
12071         if(close){
12072             close.hide();
12073         }
12074         
12075     },
12076
12077     /**
12078      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
12079      * will be displayed in the field.  If the value does not match the data value of an existing item,
12080      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12081      * Otherwise the field will be blank (although the value will still be set).
12082      * @param {String} value The value to match
12083      */
12084     setValue : function(v){
12085         if(this.multiple){
12086             this.syncValue();
12087             return;
12088         }
12089         
12090         var text = v;
12091         if(this.valueField){
12092             var r = this.findRecord(this.valueField, v);
12093             if(r){
12094                 text = r.data[this.displayField];
12095             }else if(this.valueNotFoundText !== undefined){
12096                 text = this.valueNotFoundText;
12097             }
12098         }
12099         this.lastSelectionText = text;
12100         if(this.hiddenField){
12101             this.hiddenField.dom.value = v;
12102         }
12103         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12104         this.value = v;
12105         
12106         var close = this.closeTriggerEl();
12107         
12108         if(close){
12109             (v.length || v * 1 > 0) ? close.show() : close.hide();
12110         }
12111     },
12112     /**
12113      * @property {Object} the last set data for the element
12114      */
12115     
12116     lastData : false,
12117     /**
12118      * Sets the value of the field based on a object which is related to the record format for the store.
12119      * @param {Object} value the value to set as. or false on reset?
12120      */
12121     setFromData : function(o){
12122         
12123         if(this.multiple){
12124             this.addItem(o);
12125             return;
12126         }
12127             
12128         var dv = ''; // display value
12129         var vv = ''; // value value..
12130         this.lastData = o;
12131         if (this.displayField) {
12132             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12133         } else {
12134             // this is an error condition!!!
12135             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12136         }
12137         
12138         if(this.valueField){
12139             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12140         }
12141         
12142         var close = this.closeTriggerEl();
12143         
12144         if(close){
12145             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12146         }
12147         
12148         if(this.hiddenField){
12149             this.hiddenField.dom.value = vv;
12150             
12151             this.lastSelectionText = dv;
12152             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12153             this.value = vv;
12154             return;
12155         }
12156         // no hidden field.. - we store the value in 'value', but still display
12157         // display field!!!!
12158         this.lastSelectionText = dv;
12159         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12160         this.value = vv;
12161         
12162         
12163         
12164     },
12165     // private
12166     reset : function(){
12167         // overridden so that last data is reset..
12168         
12169         if(this.multiple){
12170             this.clearItem();
12171             return;
12172         }
12173         
12174         this.setValue(this.originalValue);
12175         this.clearInvalid();
12176         this.lastData = false;
12177         if (this.view) {
12178             this.view.clearSelections();
12179         }
12180     },
12181     // private
12182     findRecord : function(prop, value){
12183         var record;
12184         if(this.store.getCount() > 0){
12185             this.store.each(function(r){
12186                 if(r.data[prop] == value){
12187                     record = r;
12188                     return false;
12189                 }
12190                 return true;
12191             });
12192         }
12193         return record;
12194     },
12195     
12196     getName: function()
12197     {
12198         // returns hidden if it's set..
12199         if (!this.rendered) {return ''};
12200         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
12201         
12202     },
12203     // private
12204     onViewMove : function(e, t){
12205         this.inKeyMode = false;
12206     },
12207
12208     // private
12209     onViewOver : function(e, t){
12210         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12211             return;
12212         }
12213         var item = this.view.findItemFromChild(t);
12214         
12215         if(item){
12216             var index = this.view.indexOf(item);
12217             this.select(index, false);
12218         }
12219     },
12220
12221     // private
12222     onViewClick : function(view, doFocus, el, e)
12223     {
12224         var index = this.view.getSelectedIndexes()[0];
12225         
12226         var r = this.store.getAt(index);
12227         
12228         if(this.tickable){
12229             
12230             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12231                 return;
12232             }
12233             
12234             var rm = false;
12235             var _this = this;
12236             
12237             Roo.each(this.tickItems, function(v,k){
12238                 
12239                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12240                     _this.tickItems.splice(k, 1);
12241                     
12242                     if(typeof(e) == 'undefined' && view == false){
12243                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12244                     }
12245                     
12246                     rm = true;
12247                     return;
12248                 }
12249             });
12250             
12251             if(rm){
12252                 return;
12253             }
12254             
12255             this.tickItems.push(r.data);
12256             
12257             if(typeof(e) == 'undefined' && view == false){
12258                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12259             }
12260                     
12261             return;
12262         }
12263         
12264         if(r){
12265             this.onSelect(r, index);
12266         }
12267         if(doFocus !== false && !this.blockFocus){
12268             this.inputEl().focus();
12269         }
12270     },
12271
12272     // private
12273     restrictHeight : function(){
12274         //this.innerList.dom.style.height = '';
12275         //var inner = this.innerList.dom;
12276         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12277         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12278         //this.list.beginUpdate();
12279         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12280         this.list.alignTo(this.inputEl(), this.listAlign);
12281         this.list.alignTo(this.inputEl(), this.listAlign);
12282         //this.list.endUpdate();
12283     },
12284
12285     // private
12286     onEmptyResults : function(){
12287         
12288         if(this.tickable && this.editable){
12289             this.restrictHeight();
12290             return;
12291         }
12292         
12293         this.collapse();
12294     },
12295
12296     /**
12297      * Returns true if the dropdown list is expanded, else false.
12298      */
12299     isExpanded : function(){
12300         return this.list.isVisible();
12301     },
12302
12303     /**
12304      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12305      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12306      * @param {String} value The data value of the item to select
12307      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12308      * selected item if it is not currently in view (defaults to true)
12309      * @return {Boolean} True if the value matched an item in the list, else false
12310      */
12311     selectByValue : function(v, scrollIntoView){
12312         if(v !== undefined && v !== null){
12313             var r = this.findRecord(this.valueField || this.displayField, v);
12314             if(r){
12315                 this.select(this.store.indexOf(r), scrollIntoView);
12316                 return true;
12317             }
12318         }
12319         return false;
12320     },
12321
12322     /**
12323      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12324      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12325      * @param {Number} index The zero-based index of the list item to select
12326      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12327      * selected item if it is not currently in view (defaults to true)
12328      */
12329     select : function(index, scrollIntoView){
12330         this.selectedIndex = index;
12331         this.view.select(index);
12332         if(scrollIntoView !== false){
12333             var el = this.view.getNode(index);
12334             /*
12335              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12336              */
12337             if(el){
12338                 this.list.scrollChildIntoView(el, false);
12339             }
12340         }
12341     },
12342
12343     // private
12344     selectNext : function(){
12345         var ct = this.store.getCount();
12346         if(ct > 0){
12347             if(this.selectedIndex == -1){
12348                 this.select(0);
12349             }else if(this.selectedIndex < ct-1){
12350                 this.select(this.selectedIndex+1);
12351             }
12352         }
12353     },
12354
12355     // private
12356     selectPrev : function(){
12357         var ct = this.store.getCount();
12358         if(ct > 0){
12359             if(this.selectedIndex == -1){
12360                 this.select(0);
12361             }else if(this.selectedIndex != 0){
12362                 this.select(this.selectedIndex-1);
12363             }
12364         }
12365     },
12366
12367     // private
12368     onKeyUp : function(e){
12369         if(this.editable !== false && !e.isSpecialKey()){
12370             this.lastKey = e.getKey();
12371             this.dqTask.delay(this.queryDelay);
12372         }
12373     },
12374
12375     // private
12376     validateBlur : function(){
12377         return !this.list || !this.list.isVisible();   
12378     },
12379
12380     // private
12381     initQuery : function(){
12382         
12383         var v = this.getRawValue();
12384         
12385         if(this.tickable && this.editable){
12386             v = this.tickableInputEl().getValue();
12387         }
12388         
12389         this.doQuery(v);
12390     },
12391
12392     // private
12393     doForce : function(){
12394         if(this.inputEl().dom.value.length > 0){
12395             this.inputEl().dom.value =
12396                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12397              
12398         }
12399     },
12400
12401     /**
12402      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
12403      * query allowing the query action to be canceled if needed.
12404      * @param {String} query The SQL query to execute
12405      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12406      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
12407      * saved in the current store (defaults to false)
12408      */
12409     doQuery : function(q, forceAll){
12410         
12411         if(q === undefined || q === null){
12412             q = '';
12413         }
12414         var qe = {
12415             query: q,
12416             forceAll: forceAll,
12417             combo: this,
12418             cancel:false
12419         };
12420         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12421             return false;
12422         }
12423         q = qe.query;
12424         
12425         forceAll = qe.forceAll;
12426         if(forceAll === true || (q.length >= this.minChars)){
12427             
12428             this.hasQuery = true;
12429             
12430             if(this.lastQuery != q || this.alwaysQuery){
12431                 this.lastQuery = q;
12432                 if(this.mode == 'local'){
12433                     this.selectedIndex = -1;
12434                     if(forceAll){
12435                         this.store.clearFilter();
12436                     }else{
12437                         
12438                         if(this.specialFilter){
12439                             this.fireEvent('specialfilter', this);
12440                             this.onLoad();
12441                             return;
12442                         }
12443                         
12444                         this.store.filter(this.displayField, q);
12445                     }
12446                     
12447                     this.store.fireEvent("datachanged", this.store);
12448                     
12449                     this.onLoad();
12450                     
12451                     
12452                 }else{
12453                     
12454                     this.store.baseParams[this.queryParam] = q;
12455                     
12456                     var options = {params : this.getParams(q)};
12457                     
12458                     if(this.loadNext){
12459                         options.add = true;
12460                         options.params.start = this.page * this.pageSize;
12461                     }
12462                     
12463                     this.store.load(options);
12464                     
12465                     /*
12466                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
12467                      *  we should expand the list on onLoad
12468                      *  so command out it
12469                      */
12470 //                    this.expand();
12471                 }
12472             }else{
12473                 this.selectedIndex = -1;
12474                 this.onLoad();   
12475             }
12476         }
12477         
12478         this.loadNext = false;
12479     },
12480     
12481     // private
12482     getParams : function(q){
12483         var p = {};
12484         //p[this.queryParam] = q;
12485         
12486         if(this.pageSize){
12487             p.start = 0;
12488             p.limit = this.pageSize;
12489         }
12490         return p;
12491     },
12492
12493     /**
12494      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12495      */
12496     collapse : function(){
12497         if(!this.isExpanded()){
12498             return;
12499         }
12500         
12501         this.list.hide();
12502         
12503         if(this.tickable){
12504             this.hasFocus = false;
12505             this.okBtn.hide();
12506             this.cancelBtn.hide();
12507             this.trigger.show();
12508             
12509             if(this.editable){
12510                 this.tickableInputEl().dom.value = '';
12511                 this.tickableInputEl().blur();
12512             }
12513             
12514         }
12515         
12516         Roo.get(document).un('mousedown', this.collapseIf, this);
12517         Roo.get(document).un('mousewheel', this.collapseIf, this);
12518         if (!this.editable) {
12519             Roo.get(document).un('keydown', this.listKeyPress, this);
12520         }
12521         this.fireEvent('collapse', this);
12522     },
12523
12524     // private
12525     collapseIf : function(e){
12526         var in_combo  = e.within(this.el);
12527         var in_list =  e.within(this.list);
12528         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12529         
12530         if (in_combo || in_list || is_list) {
12531             //e.stopPropagation();
12532             return;
12533         }
12534         
12535         if(this.tickable){
12536             this.onTickableFooterButtonClick(e, false, false);
12537         }
12538
12539         this.collapse();
12540         
12541     },
12542
12543     /**
12544      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
12545      */
12546     expand : function(){
12547        
12548         if(this.isExpanded() || !this.hasFocus){
12549             return;
12550         }
12551         
12552         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
12553         this.list.setWidth(lw);
12554         
12555         
12556          Roo.log('expand');
12557         
12558         this.list.show();
12559         
12560         this.restrictHeight();
12561         
12562         if(this.tickable){
12563             
12564             this.tickItems = Roo.apply([], this.item);
12565             
12566             this.okBtn.show();
12567             this.cancelBtn.show();
12568             this.trigger.hide();
12569             
12570             if(this.editable){
12571                 this.tickableInputEl().focus();
12572             }
12573             
12574         }
12575         
12576         Roo.get(document).on('mousedown', this.collapseIf, this);
12577         Roo.get(document).on('mousewheel', this.collapseIf, this);
12578         if (!this.editable) {
12579             Roo.get(document).on('keydown', this.listKeyPress, this);
12580         }
12581         
12582         this.fireEvent('expand', this);
12583     },
12584
12585     // private
12586     // Implements the default empty TriggerField.onTriggerClick function
12587     onTriggerClick : function(e)
12588     {
12589         Roo.log('trigger click');
12590         
12591         if(this.disabled || !this.triggerList){
12592             return;
12593         }
12594         
12595         this.page = 0;
12596         this.loadNext = false;
12597         
12598         if(this.isExpanded()){
12599             this.collapse();
12600             if (!this.blockFocus) {
12601                 this.inputEl().focus();
12602             }
12603             
12604         }else {
12605             this.hasFocus = true;
12606             if(this.triggerAction == 'all') {
12607                 this.doQuery(this.allQuery, true);
12608             } else {
12609                 this.doQuery(this.getRawValue());
12610             }
12611             if (!this.blockFocus) {
12612                 this.inputEl().focus();
12613             }
12614         }
12615     },
12616     
12617     onTickableTriggerClick : function(e)
12618     {
12619         if(this.disabled){
12620             return;
12621         }
12622         
12623         this.page = 0;
12624         this.loadNext = false;
12625         this.hasFocus = true;
12626         
12627         if(this.triggerAction == 'all') {
12628             this.doQuery(this.allQuery, true);
12629         } else {
12630             this.doQuery(this.getRawValue());
12631         }
12632     },
12633     
12634     onSearchFieldClick : function(e)
12635     {
12636         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
12637             this.onTickableFooterButtonClick(e, false, false);
12638             return;
12639         }
12640         
12641         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
12642             return;
12643         }
12644         
12645         this.page = 0;
12646         this.loadNext = false;
12647         this.hasFocus = true;
12648         
12649         if(this.triggerAction == 'all') {
12650             this.doQuery(this.allQuery, true);
12651         } else {
12652             this.doQuery(this.getRawValue());
12653         }
12654     },
12655     
12656     listKeyPress : function(e)
12657     {
12658         //Roo.log('listkeypress');
12659         // scroll to first matching element based on key pres..
12660         if (e.isSpecialKey()) {
12661             return false;
12662         }
12663         var k = String.fromCharCode(e.getKey()).toUpperCase();
12664         //Roo.log(k);
12665         var match  = false;
12666         var csel = this.view.getSelectedNodes();
12667         var cselitem = false;
12668         if (csel.length) {
12669             var ix = this.view.indexOf(csel[0]);
12670             cselitem  = this.store.getAt(ix);
12671             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
12672                 cselitem = false;
12673             }
12674             
12675         }
12676         
12677         this.store.each(function(v) { 
12678             if (cselitem) {
12679                 // start at existing selection.
12680                 if (cselitem.id == v.id) {
12681                     cselitem = false;
12682                 }
12683                 return true;
12684             }
12685                 
12686             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
12687                 match = this.store.indexOf(v);
12688                 return false;
12689             }
12690             return true;
12691         }, this);
12692         
12693         if (match === false) {
12694             return true; // no more action?
12695         }
12696         // scroll to?
12697         this.view.select(match);
12698         var sn = Roo.get(this.view.getSelectedNodes()[0])
12699         sn.scrollIntoView(sn.dom.parentNode, false);
12700     },
12701     
12702     onViewScroll : function(e, t){
12703         
12704         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){
12705             return;
12706         }
12707         
12708         this.hasQuery = true;
12709         
12710         this.loading = this.list.select('.loading', true).first();
12711         
12712         if(this.loading === null){
12713             this.list.createChild({
12714                 tag: 'div',
12715                 cls: 'loading select2-more-results select2-active',
12716                 html: 'Loading more results...'
12717             })
12718             
12719             this.loading = this.list.select('.loading', true).first();
12720             
12721             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
12722             
12723             this.loading.hide();
12724         }
12725         
12726         this.loading.show();
12727         
12728         var _combo = this;
12729         
12730         this.page++;
12731         this.loadNext = true;
12732         
12733         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
12734         
12735         return;
12736     },
12737     
12738     addItem : function(o)
12739     {   
12740         var dv = ''; // display value
12741         
12742         if (this.displayField) {
12743             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12744         } else {
12745             // this is an error condition!!!
12746             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12747         }
12748         
12749         if(!dv.length){
12750             return;
12751         }
12752         
12753         var choice = this.choices.createChild({
12754             tag: 'li',
12755             cls: 'select2-search-choice',
12756             cn: [
12757                 {
12758                     tag: 'div',
12759                     html: dv
12760                 },
12761                 {
12762                     tag: 'a',
12763                     href: '#',
12764                     cls: 'select2-search-choice-close',
12765                     tabindex: '-1'
12766                 }
12767             ]
12768             
12769         }, this.searchField);
12770         
12771         var close = choice.select('a.select2-search-choice-close', true).first()
12772         
12773         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
12774         
12775         this.item.push(o);
12776         
12777         this.lastData = o;
12778         
12779         this.syncValue();
12780         
12781         this.inputEl().dom.value = '';
12782         
12783         this.validate();
12784     },
12785     
12786     onRemoveItem : function(e, _self, o)
12787     {
12788         e.preventDefault();
12789         
12790         this.lastItem = Roo.apply([], this.item);
12791         
12792         var index = this.item.indexOf(o.data) * 1;
12793         
12794         if( index < 0){
12795             Roo.log('not this item?!');
12796             return;
12797         }
12798         
12799         this.item.splice(index, 1);
12800         o.item.remove();
12801         
12802         this.syncValue();
12803         
12804         this.fireEvent('remove', this, e);
12805         
12806         this.validate();
12807         
12808     },
12809     
12810     syncValue : function()
12811     {
12812         if(!this.item.length){
12813             this.clearValue();
12814             return;
12815         }
12816             
12817         var value = [];
12818         var _this = this;
12819         Roo.each(this.item, function(i){
12820             if(_this.valueField){
12821                 value.push(i[_this.valueField]);
12822                 return;
12823             }
12824
12825             value.push(i);
12826         });
12827
12828         this.value = value.join(',');
12829
12830         if(this.hiddenField){
12831             this.hiddenField.dom.value = this.value;
12832         }
12833         
12834         this.store.fireEvent("datachanged", this.store);
12835     },
12836     
12837     clearItem : function()
12838     {
12839         if(!this.multiple){
12840             return;
12841         }
12842         
12843         this.item = [];
12844         
12845         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
12846            c.remove();
12847         });
12848         
12849         this.syncValue();
12850         
12851         this.validate();
12852     },
12853     
12854     inputEl: function ()
12855     {
12856         if(this.tickable){
12857             return this.searchField;
12858         }
12859         return this.el.select('input.form-control',true).first();
12860     },
12861     
12862     
12863     onTickableFooterButtonClick : function(e, btn, el)
12864     {
12865         e.preventDefault();
12866         
12867         this.lastItem = Roo.apply([], this.item);
12868         
12869         if(btn && btn.name == 'cancel'){
12870             this.tickItems = Roo.apply([], this.item);
12871             this.collapse();
12872             return;
12873         }
12874         
12875         this.clearItem();
12876         
12877         var _this = this;
12878         
12879         Roo.each(this.tickItems, function(o){
12880             _this.addItem(o);
12881         });
12882         
12883         this.collapse();
12884         
12885     },
12886     
12887     validate : function()
12888     {
12889         var v = this.getRawValue();
12890         
12891         if(this.multiple){
12892             v = this.getValue();
12893         }
12894         
12895         if(this.disabled || this.allowBlank || v.length){
12896             this.markValid();
12897             return true;
12898         }
12899         
12900         this.markInvalid();
12901         return false;
12902     },
12903     
12904     tickableInputEl : function()
12905     {
12906         if(!this.tickable || !this.editable){
12907             return this.inputEl();
12908         }
12909         
12910         return this.inputEl().select('.select2-search-field-input', true).first();
12911     }
12912     
12913     
12914
12915     /** 
12916     * @cfg {Boolean} grow 
12917     * @hide 
12918     */
12919     /** 
12920     * @cfg {Number} growMin 
12921     * @hide 
12922     */
12923     /** 
12924     * @cfg {Number} growMax 
12925     * @hide 
12926     */
12927     /**
12928      * @hide
12929      * @method autoSize
12930      */
12931 });
12932 /*
12933  * Based on:
12934  * Ext JS Library 1.1.1
12935  * Copyright(c) 2006-2007, Ext JS, LLC.
12936  *
12937  * Originally Released Under LGPL - original licence link has changed is not relivant.
12938  *
12939  * Fork - LGPL
12940  * <script type="text/javascript">
12941  */
12942
12943 /**
12944  * @class Roo.View
12945  * @extends Roo.util.Observable
12946  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
12947  * This class also supports single and multi selection modes. <br>
12948  * Create a data model bound view:
12949  <pre><code>
12950  var store = new Roo.data.Store(...);
12951
12952  var view = new Roo.View({
12953     el : "my-element",
12954     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
12955  
12956     singleSelect: true,
12957     selectedClass: "ydataview-selected",
12958     store: store
12959  });
12960
12961  // listen for node click?
12962  view.on("click", function(vw, index, node, e){
12963  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
12964  });
12965
12966  // load XML data
12967  dataModel.load("foobar.xml");
12968  </code></pre>
12969  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
12970  * <br><br>
12971  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
12972  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
12973  * 
12974  * Note: old style constructor is still suported (container, template, config)
12975  * 
12976  * @constructor
12977  * Create a new View
12978  * @param {Object} config The config object
12979  * 
12980  */
12981 Roo.View = function(config, depreciated_tpl, depreciated_config){
12982     
12983     this.parent = false;
12984     
12985     if (typeof(depreciated_tpl) == 'undefined') {
12986         // new way.. - universal constructor.
12987         Roo.apply(this, config);
12988         this.el  = Roo.get(this.el);
12989     } else {
12990         // old format..
12991         this.el  = Roo.get(config);
12992         this.tpl = depreciated_tpl;
12993         Roo.apply(this, depreciated_config);
12994     }
12995     this.wrapEl  = this.el.wrap().wrap();
12996     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
12997     
12998     
12999     if(typeof(this.tpl) == "string"){
13000         this.tpl = new Roo.Template(this.tpl);
13001     } else {
13002         // support xtype ctors..
13003         this.tpl = new Roo.factory(this.tpl, Roo);
13004     }
13005     
13006     
13007     this.tpl.compile();
13008     
13009     /** @private */
13010     this.addEvents({
13011         /**
13012          * @event beforeclick
13013          * Fires before a click is processed. Returns false to cancel the default action.
13014          * @param {Roo.View} this
13015          * @param {Number} index The index of the target node
13016          * @param {HTMLElement} node The target node
13017          * @param {Roo.EventObject} e The raw event object
13018          */
13019             "beforeclick" : true,
13020         /**
13021          * @event click
13022          * Fires when a template node is clicked.
13023          * @param {Roo.View} this
13024          * @param {Number} index The index of the target node
13025          * @param {HTMLElement} node The target node
13026          * @param {Roo.EventObject} e The raw event object
13027          */
13028             "click" : true,
13029         /**
13030          * @event dblclick
13031          * Fires when a template node is double clicked.
13032          * @param {Roo.View} this
13033          * @param {Number} index The index of the target node
13034          * @param {HTMLElement} node The target node
13035          * @param {Roo.EventObject} e The raw event object
13036          */
13037             "dblclick" : true,
13038         /**
13039          * @event contextmenu
13040          * Fires when a template node is right clicked.
13041          * @param {Roo.View} this
13042          * @param {Number} index The index of the target node
13043          * @param {HTMLElement} node The target node
13044          * @param {Roo.EventObject} e The raw event object
13045          */
13046             "contextmenu" : true,
13047         /**
13048          * @event selectionchange
13049          * Fires when the selected nodes change.
13050          * @param {Roo.View} this
13051          * @param {Array} selections Array of the selected nodes
13052          */
13053             "selectionchange" : true,
13054     
13055         /**
13056          * @event beforeselect
13057          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
13058          * @param {Roo.View} this
13059          * @param {HTMLElement} node The node to be selected
13060          * @param {Array} selections Array of currently selected nodes
13061          */
13062             "beforeselect" : true,
13063         /**
13064          * @event preparedata
13065          * Fires on every row to render, to allow you to change the data.
13066          * @param {Roo.View} this
13067          * @param {Object} data to be rendered (change this)
13068          */
13069           "preparedata" : true
13070           
13071           
13072         });
13073
13074
13075
13076     this.el.on({
13077         "click": this.onClick,
13078         "dblclick": this.onDblClick,
13079         "contextmenu": this.onContextMenu,
13080         scope:this
13081     });
13082
13083     this.selections = [];
13084     this.nodes = [];
13085     this.cmp = new Roo.CompositeElementLite([]);
13086     if(this.store){
13087         this.store = Roo.factory(this.store, Roo.data);
13088         this.setStore(this.store, true);
13089     }
13090     
13091     if ( this.footer && this.footer.xtype) {
13092            
13093          var fctr = this.wrapEl.appendChild(document.createElement("div"));
13094         
13095         this.footer.dataSource = this.store
13096         this.footer.container = fctr;
13097         this.footer = Roo.factory(this.footer, Roo);
13098         fctr.insertFirst(this.el);
13099         
13100         // this is a bit insane - as the paging toolbar seems to detach the el..
13101 //        dom.parentNode.parentNode.parentNode
13102          // they get detached?
13103     }
13104     
13105     
13106     Roo.View.superclass.constructor.call(this);
13107     
13108     
13109 };
13110
13111 Roo.extend(Roo.View, Roo.util.Observable, {
13112     
13113      /**
13114      * @cfg {Roo.data.Store} store Data store to load data from.
13115      */
13116     store : false,
13117     
13118     /**
13119      * @cfg {String|Roo.Element} el The container element.
13120      */
13121     el : '',
13122     
13123     /**
13124      * @cfg {String|Roo.Template} tpl The template used by this View 
13125      */
13126     tpl : false,
13127     /**
13128      * @cfg {String} dataName the named area of the template to use as the data area
13129      *                          Works with domtemplates roo-name="name"
13130      */
13131     dataName: false,
13132     /**
13133      * @cfg {String} selectedClass The css class to add to selected nodes
13134      */
13135     selectedClass : "x-view-selected",
13136      /**
13137      * @cfg {String} emptyText The empty text to show when nothing is loaded.
13138      */
13139     emptyText : "",
13140     
13141     /**
13142      * @cfg {String} text to display on mask (default Loading)
13143      */
13144     mask : false,
13145     /**
13146      * @cfg {Boolean} multiSelect Allow multiple selection
13147      */
13148     multiSelect : false,
13149     /**
13150      * @cfg {Boolean} singleSelect Allow single selection
13151      */
13152     singleSelect:  false,
13153     
13154     /**
13155      * @cfg {Boolean} toggleSelect - selecting 
13156      */
13157     toggleSelect : false,
13158     
13159     /**
13160      * @cfg {Boolean} tickable - selecting 
13161      */
13162     tickable : false,
13163     
13164     /**
13165      * Returns the element this view is bound to.
13166      * @return {Roo.Element}
13167      */
13168     getEl : function(){
13169         return this.wrapEl;
13170     },
13171     
13172     
13173
13174     /**
13175      * Refreshes the view. - called by datachanged on the store. - do not call directly.
13176      */
13177     refresh : function(){
13178         //Roo.log('refresh');
13179         var t = this.tpl;
13180         
13181         // if we are using something like 'domtemplate', then
13182         // the what gets used is:
13183         // t.applySubtemplate(NAME, data, wrapping data..)
13184         // the outer template then get' applied with
13185         //     the store 'extra data'
13186         // and the body get's added to the
13187         //      roo-name="data" node?
13188         //      <span class='roo-tpl-{name}'></span> ?????
13189         
13190         
13191         
13192         this.clearSelections();
13193         this.el.update("");
13194         var html = [];
13195         var records = this.store.getRange();
13196         if(records.length < 1) {
13197             
13198             // is this valid??  = should it render a template??
13199             
13200             this.el.update(this.emptyText);
13201             return;
13202         }
13203         var el = this.el;
13204         if (this.dataName) {
13205             this.el.update(t.apply(this.store.meta)); //????
13206             el = this.el.child('.roo-tpl-' + this.dataName);
13207         }
13208         
13209         for(var i = 0, len = records.length; i < len; i++){
13210             var data = this.prepareData(records[i].data, i, records[i]);
13211             this.fireEvent("preparedata", this, data, i, records[i]);
13212             
13213             var d = Roo.apply({}, data);
13214             
13215             if(this.tickable){
13216                 Roo.apply(d, {'roo-id' : Roo.id()});
13217                 
13218                 var _this = this;
13219             
13220                 Roo.each(this.parent.item, function(item){
13221                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
13222                         return;
13223                     }
13224                     Roo.apply(d, {'roo-data-checked' : 'checked'});
13225                 });
13226             }
13227             
13228             html[html.length] = Roo.util.Format.trim(
13229                 this.dataName ?
13230                     t.applySubtemplate(this.dataName, d, this.store.meta) :
13231                     t.apply(d)
13232             );
13233         }
13234         
13235         
13236         
13237         el.update(html.join(""));
13238         this.nodes = el.dom.childNodes;
13239         this.updateIndexes(0);
13240     },
13241     
13242
13243     /**
13244      * Function to override to reformat the data that is sent to
13245      * the template for each node.
13246      * DEPRICATED - use the preparedata event handler.
13247      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
13248      * a JSON object for an UpdateManager bound view).
13249      */
13250     prepareData : function(data, index, record)
13251     {
13252         this.fireEvent("preparedata", this, data, index, record);
13253         return data;
13254     },
13255
13256     onUpdate : function(ds, record){
13257         // Roo.log('on update');   
13258         this.clearSelections();
13259         var index = this.store.indexOf(record);
13260         var n = this.nodes[index];
13261         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
13262         n.parentNode.removeChild(n);
13263         this.updateIndexes(index, index);
13264     },
13265
13266     
13267     
13268 // --------- FIXME     
13269     onAdd : function(ds, records, index)
13270     {
13271         //Roo.log(['on Add', ds, records, index] );        
13272         this.clearSelections();
13273         if(this.nodes.length == 0){
13274             this.refresh();
13275             return;
13276         }
13277         var n = this.nodes[index];
13278         for(var i = 0, len = records.length; i < len; i++){
13279             var d = this.prepareData(records[i].data, i, records[i]);
13280             if(n){
13281                 this.tpl.insertBefore(n, d);
13282             }else{
13283                 
13284                 this.tpl.append(this.el, d);
13285             }
13286         }
13287         this.updateIndexes(index);
13288     },
13289
13290     onRemove : function(ds, record, index){
13291        // Roo.log('onRemove');
13292         this.clearSelections();
13293         var el = this.dataName  ?
13294             this.el.child('.roo-tpl-' + this.dataName) :
13295             this.el; 
13296         
13297         el.dom.removeChild(this.nodes[index]);
13298         this.updateIndexes(index);
13299     },
13300
13301     /**
13302      * Refresh an individual node.
13303      * @param {Number} index
13304      */
13305     refreshNode : function(index){
13306         this.onUpdate(this.store, this.store.getAt(index));
13307     },
13308
13309     updateIndexes : function(startIndex, endIndex){
13310         var ns = this.nodes;
13311         startIndex = startIndex || 0;
13312         endIndex = endIndex || ns.length - 1;
13313         for(var i = startIndex; i <= endIndex; i++){
13314             ns[i].nodeIndex = i;
13315         }
13316     },
13317
13318     /**
13319      * Changes the data store this view uses and refresh the view.
13320      * @param {Store} store
13321      */
13322     setStore : function(store, initial){
13323         if(!initial && this.store){
13324             this.store.un("datachanged", this.refresh);
13325             this.store.un("add", this.onAdd);
13326             this.store.un("remove", this.onRemove);
13327             this.store.un("update", this.onUpdate);
13328             this.store.un("clear", this.refresh);
13329             this.store.un("beforeload", this.onBeforeLoad);
13330             this.store.un("load", this.onLoad);
13331             this.store.un("loadexception", this.onLoad);
13332         }
13333         if(store){
13334           
13335             store.on("datachanged", this.refresh, this);
13336             store.on("add", this.onAdd, this);
13337             store.on("remove", this.onRemove, this);
13338             store.on("update", this.onUpdate, this);
13339             store.on("clear", this.refresh, this);
13340             store.on("beforeload", this.onBeforeLoad, this);
13341             store.on("load", this.onLoad, this);
13342             store.on("loadexception", this.onLoad, this);
13343         }
13344         
13345         if(store){
13346             this.refresh();
13347         }
13348     },
13349     /**
13350      * onbeforeLoad - masks the loading area.
13351      *
13352      */
13353     onBeforeLoad : function(store,opts)
13354     {
13355          //Roo.log('onBeforeLoad');   
13356         if (!opts.add) {
13357             this.el.update("");
13358         }
13359         this.el.mask(this.mask ? this.mask : "Loading" ); 
13360     },
13361     onLoad : function ()
13362     {
13363         this.el.unmask();
13364     },
13365     
13366
13367     /**
13368      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
13369      * @param {HTMLElement} node
13370      * @return {HTMLElement} The template node
13371      */
13372     findItemFromChild : function(node){
13373         var el = this.dataName  ?
13374             this.el.child('.roo-tpl-' + this.dataName,true) :
13375             this.el.dom; 
13376         
13377         if(!node || node.parentNode == el){
13378                     return node;
13379             }
13380             var p = node.parentNode;
13381             while(p && p != el){
13382             if(p.parentNode == el){
13383                 return p;
13384             }
13385             p = p.parentNode;
13386         }
13387             return null;
13388     },
13389
13390     /** @ignore */
13391     onClick : function(e){
13392         var item = this.findItemFromChild(e.getTarget());
13393         if(item){
13394             var index = this.indexOf(item);
13395             if(this.onItemClick(item, index, e) !== false){
13396                 this.fireEvent("click", this, index, item, e);
13397             }
13398         }else{
13399             this.clearSelections();
13400         }
13401     },
13402
13403     /** @ignore */
13404     onContextMenu : function(e){
13405         var item = this.findItemFromChild(e.getTarget());
13406         if(item){
13407             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
13408         }
13409     },
13410
13411     /** @ignore */
13412     onDblClick : function(e){
13413         var item = this.findItemFromChild(e.getTarget());
13414         if(item){
13415             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
13416         }
13417     },
13418
13419     onItemClick : function(item, index, e)
13420     {
13421         if(this.fireEvent("beforeclick", this, index, item, e) === false){
13422             return false;
13423         }
13424         if (this.toggleSelect) {
13425             var m = this.isSelected(item) ? 'unselect' : 'select';
13426             //Roo.log(m);
13427             var _t = this;
13428             _t[m](item, true, false);
13429             return true;
13430         }
13431         if(this.multiSelect || this.singleSelect){
13432             if(this.multiSelect && e.shiftKey && this.lastSelection){
13433                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
13434             }else{
13435                 this.select(item, this.multiSelect && e.ctrlKey);
13436                 this.lastSelection = item;
13437             }
13438             
13439             if(!this.tickable){
13440                 e.preventDefault();
13441             }
13442             
13443         }
13444         return true;
13445     },
13446
13447     /**
13448      * Get the number of selected nodes.
13449      * @return {Number}
13450      */
13451     getSelectionCount : function(){
13452         return this.selections.length;
13453     },
13454
13455     /**
13456      * Get the currently selected nodes.
13457      * @return {Array} An array of HTMLElements
13458      */
13459     getSelectedNodes : function(){
13460         return this.selections;
13461     },
13462
13463     /**
13464      * Get the indexes of the selected nodes.
13465      * @return {Array}
13466      */
13467     getSelectedIndexes : function(){
13468         var indexes = [], s = this.selections;
13469         for(var i = 0, len = s.length; i < len; i++){
13470             indexes.push(s[i].nodeIndex);
13471         }
13472         return indexes;
13473     },
13474
13475     /**
13476      * Clear all selections
13477      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
13478      */
13479     clearSelections : function(suppressEvent){
13480         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
13481             this.cmp.elements = this.selections;
13482             this.cmp.removeClass(this.selectedClass);
13483             this.selections = [];
13484             if(!suppressEvent){
13485                 this.fireEvent("selectionchange", this, this.selections);
13486             }
13487         }
13488     },
13489
13490     /**
13491      * Returns true if the passed node is selected
13492      * @param {HTMLElement/Number} node The node or node index
13493      * @return {Boolean}
13494      */
13495     isSelected : function(node){
13496         var s = this.selections;
13497         if(s.length < 1){
13498             return false;
13499         }
13500         node = this.getNode(node);
13501         return s.indexOf(node) !== -1;
13502     },
13503
13504     /**
13505      * Selects nodes.
13506      * @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
13507      * @param {Boolean} keepExisting (optional) true to keep existing selections
13508      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
13509      */
13510     select : function(nodeInfo, keepExisting, suppressEvent){
13511         if(nodeInfo instanceof Array){
13512             if(!keepExisting){
13513                 this.clearSelections(true);
13514             }
13515             for(var i = 0, len = nodeInfo.length; i < len; i++){
13516                 this.select(nodeInfo[i], true, true);
13517             }
13518             return;
13519         } 
13520         var node = this.getNode(nodeInfo);
13521         if(!node || this.isSelected(node)){
13522             return; // already selected.
13523         }
13524         if(!keepExisting){
13525             this.clearSelections(true);
13526         }
13527         
13528         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
13529             Roo.fly(node).addClass(this.selectedClass);
13530             this.selections.push(node);
13531             if(!suppressEvent){
13532                 this.fireEvent("selectionchange", this, this.selections);
13533             }
13534         }
13535         
13536         
13537     },
13538       /**
13539      * Unselects nodes.
13540      * @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
13541      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
13542      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
13543      */
13544     unselect : function(nodeInfo, keepExisting, suppressEvent)
13545     {
13546         if(nodeInfo instanceof Array){
13547             Roo.each(this.selections, function(s) {
13548                 this.unselect(s, nodeInfo);
13549             }, this);
13550             return;
13551         }
13552         var node = this.getNode(nodeInfo);
13553         if(!node || !this.isSelected(node)){
13554             //Roo.log("not selected");
13555             return; // not selected.
13556         }
13557         // fireevent???
13558         var ns = [];
13559         Roo.each(this.selections, function(s) {
13560             if (s == node ) {
13561                 Roo.fly(node).removeClass(this.selectedClass);
13562
13563                 return;
13564             }
13565             ns.push(s);
13566         },this);
13567         
13568         this.selections= ns;
13569         this.fireEvent("selectionchange", this, this.selections);
13570     },
13571
13572     /**
13573      * Gets a template node.
13574      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
13575      * @return {HTMLElement} The node or null if it wasn't found
13576      */
13577     getNode : function(nodeInfo){
13578         if(typeof nodeInfo == "string"){
13579             return document.getElementById(nodeInfo);
13580         }else if(typeof nodeInfo == "number"){
13581             return this.nodes[nodeInfo];
13582         }
13583         return nodeInfo;
13584     },
13585
13586     /**
13587      * Gets a range template nodes.
13588      * @param {Number} startIndex
13589      * @param {Number} endIndex
13590      * @return {Array} An array of nodes
13591      */
13592     getNodes : function(start, end){
13593         var ns = this.nodes;
13594         start = start || 0;
13595         end = typeof end == "undefined" ? ns.length - 1 : end;
13596         var nodes = [];
13597         if(start <= end){
13598             for(var i = start; i <= end; i++){
13599                 nodes.push(ns[i]);
13600             }
13601         } else{
13602             for(var i = start; i >= end; i--){
13603                 nodes.push(ns[i]);
13604             }
13605         }
13606         return nodes;
13607     },
13608
13609     /**
13610      * Finds the index of the passed node
13611      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
13612      * @return {Number} The index of the node or -1
13613      */
13614     indexOf : function(node){
13615         node = this.getNode(node);
13616         if(typeof node.nodeIndex == "number"){
13617             return node.nodeIndex;
13618         }
13619         var ns = this.nodes;
13620         for(var i = 0, len = ns.length; i < len; i++){
13621             if(ns[i] == node){
13622                 return i;
13623             }
13624         }
13625         return -1;
13626     }
13627 });
13628 /*
13629  * - LGPL
13630  *
13631  * based on jquery fullcalendar
13632  * 
13633  */
13634
13635 Roo.bootstrap = Roo.bootstrap || {};
13636 /**
13637  * @class Roo.bootstrap.Calendar
13638  * @extends Roo.bootstrap.Component
13639  * Bootstrap Calendar class
13640  * @cfg {Boolean} loadMask (true|false) default false
13641  * @cfg {Object} header generate the user specific header of the calendar, default false
13642
13643  * @constructor
13644  * Create a new Container
13645  * @param {Object} config The config object
13646  */
13647
13648
13649
13650 Roo.bootstrap.Calendar = function(config){
13651     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
13652      this.addEvents({
13653         /**
13654              * @event select
13655              * Fires when a date is selected
13656              * @param {DatePicker} this
13657              * @param {Date} date The selected date
13658              */
13659         'select': true,
13660         /**
13661              * @event monthchange
13662              * Fires when the displayed month changes 
13663              * @param {DatePicker} this
13664              * @param {Date} date The selected month
13665              */
13666         'monthchange': true,
13667         /**
13668              * @event evententer
13669              * Fires when mouse over an event
13670              * @param {Calendar} this
13671              * @param {event} Event
13672              */
13673         'evententer': true,
13674         /**
13675              * @event eventleave
13676              * Fires when the mouse leaves an
13677              * @param {Calendar} this
13678              * @param {event}
13679              */
13680         'eventleave': true,
13681         /**
13682              * @event eventclick
13683              * Fires when the mouse click an
13684              * @param {Calendar} this
13685              * @param {event}
13686              */
13687         'eventclick': true
13688         
13689     });
13690
13691 };
13692
13693 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
13694     
13695      /**
13696      * @cfg {Number} startDay
13697      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
13698      */
13699     startDay : 0,
13700     
13701     loadMask : false,
13702     
13703     header : false,
13704       
13705     getAutoCreate : function(){
13706         
13707         
13708         var fc_button = function(name, corner, style, content ) {
13709             return Roo.apply({},{
13710                 tag : 'span',
13711                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
13712                          (corner.length ?
13713                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
13714                             ''
13715                         ),
13716                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
13717                 unselectable: 'on'
13718             });
13719         };
13720         
13721         var header = {};
13722         
13723         if(!this.header){
13724             header = {
13725                 tag : 'table',
13726                 cls : 'fc-header',
13727                 style : 'width:100%',
13728                 cn : [
13729                     {
13730                         tag: 'tr',
13731                         cn : [
13732                             {
13733                                 tag : 'td',
13734                                 cls : 'fc-header-left',
13735                                 cn : [
13736                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
13737                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
13738                                     { tag: 'span', cls: 'fc-header-space' },
13739                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
13740
13741
13742                                 ]
13743                             },
13744
13745                             {
13746                                 tag : 'td',
13747                                 cls : 'fc-header-center',
13748                                 cn : [
13749                                     {
13750                                         tag: 'span',
13751                                         cls: 'fc-header-title',
13752                                         cn : {
13753                                             tag: 'H2',
13754                                             html : 'month / year'
13755                                         }
13756                                     }
13757
13758                                 ]
13759                             },
13760                             {
13761                                 tag : 'td',
13762                                 cls : 'fc-header-right',
13763                                 cn : [
13764                               /*      fc_button('month', 'left', '', 'month' ),
13765                                     fc_button('week', '', '', 'week' ),
13766                                     fc_button('day', 'right', '', 'day' )
13767                                 */    
13768
13769                                 ]
13770                             }
13771
13772                         ]
13773                     }
13774                 ]
13775             };
13776         }
13777         
13778         header = this.header;
13779         
13780        
13781         var cal_heads = function() {
13782             var ret = [];
13783             // fixme - handle this.
13784             
13785             for (var i =0; i < Date.dayNames.length; i++) {
13786                 var d = Date.dayNames[i];
13787                 ret.push({
13788                     tag: 'th',
13789                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
13790                     html : d.substring(0,3)
13791                 });
13792                 
13793             }
13794             ret[0].cls += ' fc-first';
13795             ret[6].cls += ' fc-last';
13796             return ret;
13797         };
13798         var cal_cell = function(n) {
13799             return  {
13800                 tag: 'td',
13801                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
13802                 cn : [
13803                     {
13804                         cn : [
13805                             {
13806                                 cls: 'fc-day-number',
13807                                 html: 'D'
13808                             },
13809                             {
13810                                 cls: 'fc-day-content',
13811                              
13812                                 cn : [
13813                                      {
13814                                         style: 'position: relative;' // height: 17px;
13815                                     }
13816                                 ]
13817                             }
13818                             
13819                             
13820                         ]
13821                     }
13822                 ]
13823                 
13824             }
13825         };
13826         var cal_rows = function() {
13827             
13828             var ret = [];
13829             for (var r = 0; r < 6; r++) {
13830                 var row= {
13831                     tag : 'tr',
13832                     cls : 'fc-week',
13833                     cn : []
13834                 };
13835                 
13836                 for (var i =0; i < Date.dayNames.length; i++) {
13837                     var d = Date.dayNames[i];
13838                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
13839
13840                 }
13841                 row.cn[0].cls+=' fc-first';
13842                 row.cn[0].cn[0].style = 'min-height:90px';
13843                 row.cn[6].cls+=' fc-last';
13844                 ret.push(row);
13845                 
13846             }
13847             ret[0].cls += ' fc-first';
13848             ret[4].cls += ' fc-prev-last';
13849             ret[5].cls += ' fc-last';
13850             return ret;
13851             
13852         };
13853         
13854         var cal_table = {
13855             tag: 'table',
13856             cls: 'fc-border-separate',
13857             style : 'width:100%',
13858             cellspacing  : 0,
13859             cn : [
13860                 { 
13861                     tag: 'thead',
13862                     cn : [
13863                         { 
13864                             tag: 'tr',
13865                             cls : 'fc-first fc-last',
13866                             cn : cal_heads()
13867                         }
13868                     ]
13869                 },
13870                 { 
13871                     tag: 'tbody',
13872                     cn : cal_rows()
13873                 }
13874                   
13875             ]
13876         };
13877          
13878          var cfg = {
13879             cls : 'fc fc-ltr',
13880             cn : [
13881                 header,
13882                 {
13883                     cls : 'fc-content',
13884                     style : "position: relative;",
13885                     cn : [
13886                         {
13887                             cls : 'fc-view fc-view-month fc-grid',
13888                             style : 'position: relative',
13889                             unselectable : 'on',
13890                             cn : [
13891                                 {
13892                                     cls : 'fc-event-container',
13893                                     style : 'position:absolute;z-index:8;top:0;left:0;'
13894                                 },
13895                                 cal_table
13896                             ]
13897                         }
13898                     ]
13899     
13900                 }
13901            ] 
13902             
13903         };
13904         
13905          
13906         
13907         return cfg;
13908     },
13909     
13910     
13911     initEvents : function()
13912     {
13913         if(!this.store){
13914             throw "can not find store for calendar";
13915         }
13916         
13917         var mark = {
13918             tag: "div",
13919             cls:"x-dlg-mask",
13920             style: "text-align:center",
13921             cn: [
13922                 {
13923                     tag: "div",
13924                     style: "background-color:white;width:50%;margin:250 auto",
13925                     cn: [
13926                         {
13927                             tag: "img",
13928                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
13929                         },
13930                         {
13931                             tag: "span",
13932                             html: "Loading"
13933                         }
13934                         
13935                     ]
13936                 }
13937             ]
13938         }
13939         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
13940         
13941         var size = this.el.select('.fc-content', true).first().getSize();
13942         this.maskEl.setSize(size.width, size.height);
13943         this.maskEl.enableDisplayMode("block");
13944         if(!this.loadMask){
13945             this.maskEl.hide();
13946         }
13947         
13948         this.store = Roo.factory(this.store, Roo.data);
13949         this.store.on('load', this.onLoad, this);
13950         this.store.on('beforeload', this.onBeforeLoad, this);
13951         
13952         this.resize();
13953         
13954         this.cells = this.el.select('.fc-day',true);
13955         //Roo.log(this.cells);
13956         this.textNodes = this.el.query('.fc-day-number');
13957         this.cells.addClassOnOver('fc-state-hover');
13958         
13959         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
13960         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
13961         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
13962         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
13963         
13964         this.on('monthchange', this.onMonthChange, this);
13965         
13966         this.update(new Date().clearTime());
13967     },
13968     
13969     resize : function() {
13970         var sz  = this.el.getSize();
13971         
13972         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
13973         this.el.select('.fc-day-content div',true).setHeight(34);
13974     },
13975     
13976     
13977     // private
13978     showPrevMonth : function(e){
13979         this.update(this.activeDate.add("mo", -1));
13980     },
13981     showToday : function(e){
13982         this.update(new Date().clearTime());
13983     },
13984     // private
13985     showNextMonth : function(e){
13986         this.update(this.activeDate.add("mo", 1));
13987     },
13988
13989     // private
13990     showPrevYear : function(){
13991         this.update(this.activeDate.add("y", -1));
13992     },
13993
13994     // private
13995     showNextYear : function(){
13996         this.update(this.activeDate.add("y", 1));
13997     },
13998
13999     
14000    // private
14001     update : function(date)
14002     {
14003         var vd = this.activeDate;
14004         this.activeDate = date;
14005 //        if(vd && this.el){
14006 //            var t = date.getTime();
14007 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
14008 //                Roo.log('using add remove');
14009 //                
14010 //                this.fireEvent('monthchange', this, date);
14011 //                
14012 //                this.cells.removeClass("fc-state-highlight");
14013 //                this.cells.each(function(c){
14014 //                   if(c.dateValue == t){
14015 //                       c.addClass("fc-state-highlight");
14016 //                       setTimeout(function(){
14017 //                            try{c.dom.firstChild.focus();}catch(e){}
14018 //                       }, 50);
14019 //                       return false;
14020 //                   }
14021 //                   return true;
14022 //                });
14023 //                return;
14024 //            }
14025 //        }
14026         
14027         var days = date.getDaysInMonth();
14028         
14029         var firstOfMonth = date.getFirstDateOfMonth();
14030         var startingPos = firstOfMonth.getDay()-this.startDay;
14031         
14032         if(startingPos < this.startDay){
14033             startingPos += 7;
14034         }
14035         
14036         var pm = date.add(Date.MONTH, -1);
14037         var prevStart = pm.getDaysInMonth()-startingPos;
14038 //        
14039         this.cells = this.el.select('.fc-day',true);
14040         this.textNodes = this.el.query('.fc-day-number');
14041         this.cells.addClassOnOver('fc-state-hover');
14042         
14043         var cells = this.cells.elements;
14044         var textEls = this.textNodes;
14045         
14046         Roo.each(cells, function(cell){
14047             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
14048         });
14049         
14050         days += startingPos;
14051
14052         // convert everything to numbers so it's fast
14053         var day = 86400000;
14054         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
14055         //Roo.log(d);
14056         //Roo.log(pm);
14057         //Roo.log(prevStart);
14058         
14059         var today = new Date().clearTime().getTime();
14060         var sel = date.clearTime().getTime();
14061         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
14062         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
14063         var ddMatch = this.disabledDatesRE;
14064         var ddText = this.disabledDatesText;
14065         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
14066         var ddaysText = this.disabledDaysText;
14067         var format = this.format;
14068         
14069         var setCellClass = function(cal, cell){
14070             cell.row = 0;
14071             cell.events = [];
14072             cell.more = [];
14073             //Roo.log('set Cell Class');
14074             cell.title = "";
14075             var t = d.getTime();
14076             
14077             //Roo.log(d);
14078             
14079             cell.dateValue = t;
14080             if(t == today){
14081                 cell.className += " fc-today";
14082                 cell.className += " fc-state-highlight";
14083                 cell.title = cal.todayText;
14084             }
14085             if(t == sel){
14086                 // disable highlight in other month..
14087                 //cell.className += " fc-state-highlight";
14088                 
14089             }
14090             // disabling
14091             if(t < min) {
14092                 cell.className = " fc-state-disabled";
14093                 cell.title = cal.minText;
14094                 return;
14095             }
14096             if(t > max) {
14097                 cell.className = " fc-state-disabled";
14098                 cell.title = cal.maxText;
14099                 return;
14100             }
14101             if(ddays){
14102                 if(ddays.indexOf(d.getDay()) != -1){
14103                     cell.title = ddaysText;
14104                     cell.className = " fc-state-disabled";
14105                 }
14106             }
14107             if(ddMatch && format){
14108                 var fvalue = d.dateFormat(format);
14109                 if(ddMatch.test(fvalue)){
14110                     cell.title = ddText.replace("%0", fvalue);
14111                     cell.className = " fc-state-disabled";
14112                 }
14113             }
14114             
14115             if (!cell.initialClassName) {
14116                 cell.initialClassName = cell.dom.className;
14117             }
14118             
14119             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
14120         };
14121
14122         var i = 0;
14123         
14124         for(; i < startingPos; i++) {
14125             textEls[i].innerHTML = (++prevStart);
14126             d.setDate(d.getDate()+1);
14127             
14128             cells[i].className = "fc-past fc-other-month";
14129             setCellClass(this, cells[i]);
14130         }
14131         
14132         var intDay = 0;
14133         
14134         for(; i < days; i++){
14135             intDay = i - startingPos + 1;
14136             textEls[i].innerHTML = (intDay);
14137             d.setDate(d.getDate()+1);
14138             
14139             cells[i].className = ''; // "x-date-active";
14140             setCellClass(this, cells[i]);
14141         }
14142         var extraDays = 0;
14143         
14144         for(; i < 42; i++) {
14145             textEls[i].innerHTML = (++extraDays);
14146             d.setDate(d.getDate()+1);
14147             
14148             cells[i].className = "fc-future fc-other-month";
14149             setCellClass(this, cells[i]);
14150         }
14151         
14152         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
14153         
14154         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
14155         
14156         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
14157         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
14158         
14159         if(totalRows != 6){
14160             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
14161             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
14162         }
14163         
14164         this.fireEvent('monthchange', this, date);
14165         
14166         
14167         /*
14168         if(!this.internalRender){
14169             var main = this.el.dom.firstChild;
14170             var w = main.offsetWidth;
14171             this.el.setWidth(w + this.el.getBorderWidth("lr"));
14172             Roo.fly(main).setWidth(w);
14173             this.internalRender = true;
14174             // opera does not respect the auto grow header center column
14175             // then, after it gets a width opera refuses to recalculate
14176             // without a second pass
14177             if(Roo.isOpera && !this.secondPass){
14178                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
14179                 this.secondPass = true;
14180                 this.update.defer(10, this, [date]);
14181             }
14182         }
14183         */
14184         
14185     },
14186     
14187     findCell : function(dt) {
14188         dt = dt.clearTime().getTime();
14189         var ret = false;
14190         this.cells.each(function(c){
14191             //Roo.log("check " +c.dateValue + '?=' + dt);
14192             if(c.dateValue == dt){
14193                 ret = c;
14194                 return false;
14195             }
14196             return true;
14197         });
14198         
14199         return ret;
14200     },
14201     
14202     findCells : function(ev) {
14203         var s = ev.start.clone().clearTime().getTime();
14204        // Roo.log(s);
14205         var e= ev.end.clone().clearTime().getTime();
14206        // Roo.log(e);
14207         var ret = [];
14208         this.cells.each(function(c){
14209              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
14210             
14211             if(c.dateValue > e){
14212                 return ;
14213             }
14214             if(c.dateValue < s){
14215                 return ;
14216             }
14217             ret.push(c);
14218         });
14219         
14220         return ret;    
14221     },
14222     
14223 //    findBestRow: function(cells)
14224 //    {
14225 //        var ret = 0;
14226 //        
14227 //        for (var i =0 ; i < cells.length;i++) {
14228 //            ret  = Math.max(cells[i].rows || 0,ret);
14229 //        }
14230 //        return ret;
14231 //        
14232 //    },
14233     
14234     
14235     addItem : function(ev)
14236     {
14237         // look for vertical location slot in
14238         var cells = this.findCells(ev);
14239         
14240 //        ev.row = this.findBestRow(cells);
14241         
14242         // work out the location.
14243         
14244         var crow = false;
14245         var rows = [];
14246         for(var i =0; i < cells.length; i++) {
14247             
14248             cells[i].row = cells[0].row;
14249             
14250             if(i == 0){
14251                 cells[i].row = cells[i].row + 1;
14252             }
14253             
14254             if (!crow) {
14255                 crow = {
14256                     start : cells[i],
14257                     end :  cells[i]
14258                 };
14259                 continue;
14260             }
14261             if (crow.start.getY() == cells[i].getY()) {
14262                 // on same row.
14263                 crow.end = cells[i];
14264                 continue;
14265             }
14266             // different row.
14267             rows.push(crow);
14268             crow = {
14269                 start: cells[i],
14270                 end : cells[i]
14271             };
14272             
14273         }
14274         
14275         rows.push(crow);
14276         ev.els = [];
14277         ev.rows = rows;
14278         ev.cells = cells;
14279         
14280         cells[0].events.push(ev);
14281         
14282         this.calevents.push(ev);
14283     },
14284     
14285     clearEvents: function() {
14286         
14287         if(!this.calevents){
14288             return;
14289         }
14290         
14291         Roo.each(this.cells.elements, function(c){
14292             c.row = 0;
14293             c.events = [];
14294             c.more = [];
14295         });
14296         
14297         Roo.each(this.calevents, function(e) {
14298             Roo.each(e.els, function(el) {
14299                 el.un('mouseenter' ,this.onEventEnter, this);
14300                 el.un('mouseleave' ,this.onEventLeave, this);
14301                 el.remove();
14302             },this);
14303         },this);
14304         
14305         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
14306             e.remove();
14307         });
14308         
14309     },
14310     
14311     renderEvents: function()
14312     {   
14313         var _this = this;
14314         
14315         this.cells.each(function(c) {
14316             
14317             if(c.row < 5){
14318                 return;
14319             }
14320             
14321             var ev = c.events;
14322             
14323             var r = 4;
14324             if(c.row != c.events.length){
14325                 r = 4 - (4 - (c.row - c.events.length));
14326             }
14327             
14328             c.events = ev.slice(0, r);
14329             c.more = ev.slice(r);
14330             
14331             if(c.more.length && c.more.length == 1){
14332                 c.events.push(c.more.pop());
14333             }
14334             
14335             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
14336             
14337         });
14338             
14339         this.cells.each(function(c) {
14340             
14341             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
14342             
14343             
14344             for (var e = 0; e < c.events.length; e++){
14345                 var ev = c.events[e];
14346                 var rows = ev.rows;
14347                 
14348                 for(var i = 0; i < rows.length; i++) {
14349                 
14350                     // how many rows should it span..
14351
14352                     var  cfg = {
14353                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
14354                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
14355
14356                         unselectable : "on",
14357                         cn : [
14358                             {
14359                                 cls: 'fc-event-inner',
14360                                 cn : [
14361     //                                {
14362     //                                  tag:'span',
14363     //                                  cls: 'fc-event-time',
14364     //                                  html : cells.length > 1 ? '' : ev.time
14365     //                                },
14366                                     {
14367                                       tag:'span',
14368                                       cls: 'fc-event-title',
14369                                       html : String.format('{0}', ev.title)
14370                                     }
14371
14372
14373                                 ]
14374                             },
14375                             {
14376                                 cls: 'ui-resizable-handle ui-resizable-e',
14377                                 html : '&nbsp;&nbsp;&nbsp'
14378                             }
14379
14380                         ]
14381                     };
14382
14383                     if (i == 0) {
14384                         cfg.cls += ' fc-event-start';
14385                     }
14386                     if ((i+1) == rows.length) {
14387                         cfg.cls += ' fc-event-end';
14388                     }
14389
14390                     var ctr = _this.el.select('.fc-event-container',true).first();
14391                     var cg = ctr.createChild(cfg);
14392
14393                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
14394                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
14395
14396                     var r = (c.more.length) ? 1 : 0;
14397                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
14398                     cg.setWidth(ebox.right - sbox.x -2);
14399
14400                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
14401                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
14402                     cg.on('click', _this.onEventClick, _this, ev);
14403
14404                     ev.els.push(cg);
14405                     
14406                 }
14407                 
14408             }
14409             
14410             
14411             if(c.more.length){
14412                 var  cfg = {
14413                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
14414                     style : 'position: absolute',
14415                     unselectable : "on",
14416                     cn : [
14417                         {
14418                             cls: 'fc-event-inner',
14419                             cn : [
14420                                 {
14421                                   tag:'span',
14422                                   cls: 'fc-event-title',
14423                                   html : 'More'
14424                                 }
14425
14426
14427                             ]
14428                         },
14429                         {
14430                             cls: 'ui-resizable-handle ui-resizable-e',
14431                             html : '&nbsp;&nbsp;&nbsp'
14432                         }
14433
14434                     ]
14435                 };
14436
14437                 var ctr = _this.el.select('.fc-event-container',true).first();
14438                 var cg = ctr.createChild(cfg);
14439
14440                 var sbox = c.select('.fc-day-content',true).first().getBox();
14441                 var ebox = c.select('.fc-day-content',true).first().getBox();
14442                 //Roo.log(cg);
14443                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
14444                 cg.setWidth(ebox.right - sbox.x -2);
14445
14446                 cg.on('click', _this.onMoreEventClick, _this, c.more);
14447                 
14448             }
14449             
14450         });
14451         
14452         
14453         
14454     },
14455     
14456     onEventEnter: function (e, el,event,d) {
14457         this.fireEvent('evententer', this, el, event);
14458     },
14459     
14460     onEventLeave: function (e, el,event,d) {
14461         this.fireEvent('eventleave', this, el, event);
14462     },
14463     
14464     onEventClick: function (e, el,event,d) {
14465         this.fireEvent('eventclick', this, el, event);
14466     },
14467     
14468     onMonthChange: function () {
14469         this.store.load();
14470     },
14471     
14472     onMoreEventClick: function(e, el, more)
14473     {
14474         var _this = this;
14475         
14476         this.calpopover.placement = 'right';
14477         this.calpopover.setTitle('More');
14478         
14479         this.calpopover.setContent('');
14480         
14481         var ctr = this.calpopover.el.select('.popover-content', true).first();
14482         
14483         Roo.each(more, function(m){
14484             var cfg = {
14485                 cls : 'fc-event-hori fc-event-draggable',
14486                 html : m.title
14487             }
14488             var cg = ctr.createChild(cfg);
14489             
14490             cg.on('click', _this.onEventClick, _this, m);
14491         });
14492         
14493         this.calpopover.show(el);
14494         
14495         
14496     },
14497     
14498     onLoad: function () 
14499     {   
14500         this.calevents = [];
14501         var cal = this;
14502         
14503         if(this.store.getCount() > 0){
14504             this.store.data.each(function(d){
14505                cal.addItem({
14506                     id : d.data.id,
14507                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
14508                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
14509                     time : d.data.start_time,
14510                     title : d.data.title,
14511                     description : d.data.description,
14512                     venue : d.data.venue
14513                 });
14514             });
14515         }
14516         
14517         this.renderEvents();
14518         
14519         if(this.calevents.length && this.loadMask){
14520             this.maskEl.hide();
14521         }
14522     },
14523     
14524     onBeforeLoad: function()
14525     {
14526         this.clearEvents();
14527         if(this.loadMask){
14528             this.maskEl.show();
14529         }
14530     }
14531 });
14532
14533  
14534  /*
14535  * - LGPL
14536  *
14537  * element
14538  * 
14539  */
14540
14541 /**
14542  * @class Roo.bootstrap.Popover
14543  * @extends Roo.bootstrap.Component
14544  * Bootstrap Popover class
14545  * @cfg {String} html contents of the popover   (or false to use children..)
14546  * @cfg {String} title of popover (or false to hide)
14547  * @cfg {String} placement how it is placed
14548  * @cfg {String} trigger click || hover (or false to trigger manually)
14549  * @cfg {String} over what (parent or false to trigger manually.)
14550  * @cfg {Number} delay - delay before showing
14551  
14552  * @constructor
14553  * Create a new Popover
14554  * @param {Object} config The config object
14555  */
14556
14557 Roo.bootstrap.Popover = function(config){
14558     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
14559 };
14560
14561 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
14562     
14563     title: 'Fill in a title',
14564     html: false,
14565     
14566     placement : 'right',
14567     trigger : 'hover', // hover
14568     
14569     delay : 0,
14570     
14571     over: 'parent',
14572     
14573     can_build_overlaid : false,
14574     
14575     getChildContainer : function()
14576     {
14577         return this.el.select('.popover-content',true).first();
14578     },
14579     
14580     getAutoCreate : function(){
14581          Roo.log('make popover?');
14582         var cfg = {
14583            cls : 'popover roo-dynamic',
14584            style: 'display:block',
14585            cn : [
14586                 {
14587                     cls : 'arrow'
14588                 },
14589                 {
14590                     cls : 'popover-inner',
14591                     cn : [
14592                         {
14593                             tag: 'h3',
14594                             cls: 'popover-title',
14595                             html : this.title
14596                         },
14597                         {
14598                             cls : 'popover-content',
14599                             html : this.html
14600                         }
14601                     ]
14602                     
14603                 }
14604            ]
14605         };
14606         
14607         return cfg;
14608     },
14609     setTitle: function(str)
14610     {
14611         this.el.select('.popover-title',true).first().dom.innerHTML = str;
14612     },
14613     setContent: function(str)
14614     {
14615         this.el.select('.popover-content',true).first().dom.innerHTML = str;
14616     },
14617     // as it get's added to the bottom of the page.
14618     onRender : function(ct, position)
14619     {
14620         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
14621         if(!this.el){
14622             var cfg = Roo.apply({},  this.getAutoCreate());
14623             cfg.id = Roo.id();
14624             
14625             if (this.cls) {
14626                 cfg.cls += ' ' + this.cls;
14627             }
14628             if (this.style) {
14629                 cfg.style = this.style;
14630             }
14631             Roo.log("adding to ")
14632             this.el = Roo.get(document.body).createChild(cfg, position);
14633             Roo.log(this.el);
14634         }
14635         this.initEvents();
14636     },
14637     
14638     initEvents : function()
14639     {
14640         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
14641         this.el.enableDisplayMode('block');
14642         this.el.hide();
14643         if (this.over === false) {
14644             return; 
14645         }
14646         if (this.triggers === false) {
14647             return;
14648         }
14649         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
14650         var triggers = this.trigger ? this.trigger.split(' ') : [];
14651         Roo.each(triggers, function(trigger) {
14652         
14653             if (trigger == 'click') {
14654                 on_el.on('click', this.toggle, this);
14655             } else if (trigger != 'manual') {
14656                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
14657                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
14658       
14659                 on_el.on(eventIn  ,this.enter, this);
14660                 on_el.on(eventOut, this.leave, this);
14661             }
14662         }, this);
14663         
14664     },
14665     
14666     
14667     // private
14668     timeout : null,
14669     hoverState : null,
14670     
14671     toggle : function () {
14672         this.hoverState == 'in' ? this.leave() : this.enter();
14673     },
14674     
14675     enter : function () {
14676        
14677     
14678         clearTimeout(this.timeout);
14679     
14680         this.hoverState = 'in';
14681     
14682         if (!this.delay || !this.delay.show) {
14683             this.show();
14684             return;
14685         }
14686         var _t = this;
14687         this.timeout = setTimeout(function () {
14688             if (_t.hoverState == 'in') {
14689                 _t.show();
14690             }
14691         }, this.delay.show)
14692     },
14693     leave : function() {
14694         clearTimeout(this.timeout);
14695     
14696         this.hoverState = 'out';
14697     
14698         if (!this.delay || !this.delay.hide) {
14699             this.hide();
14700             return;
14701         }
14702         var _t = this;
14703         this.timeout = setTimeout(function () {
14704             if (_t.hoverState == 'out') {
14705                 _t.hide();
14706             }
14707         }, this.delay.hide)
14708     },
14709     
14710     show : function (on_el)
14711     {
14712         if (!on_el) {
14713             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
14714         }
14715         // set content.
14716         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
14717         if (this.html !== false) {
14718             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
14719         }
14720         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
14721         if (!this.title.length) {
14722             this.el.select('.popover-title',true).hide();
14723         }
14724         
14725         var placement = typeof this.placement == 'function' ?
14726             this.placement.call(this, this.el, on_el) :
14727             this.placement;
14728             
14729         var autoToken = /\s?auto?\s?/i;
14730         var autoPlace = autoToken.test(placement);
14731         if (autoPlace) {
14732             placement = placement.replace(autoToken, '') || 'top';
14733         }
14734         
14735         //this.el.detach()
14736         //this.el.setXY([0,0]);
14737         this.el.show();
14738         this.el.dom.style.display='block';
14739         this.el.addClass(placement);
14740         
14741         //this.el.appendTo(on_el);
14742         
14743         var p = this.getPosition();
14744         var box = this.el.getBox();
14745         
14746         if (autoPlace) {
14747             // fixme..
14748         }
14749         var align = Roo.bootstrap.Popover.alignment[placement];
14750         this.el.alignTo(on_el, align[0],align[1]);
14751         //var arrow = this.el.select('.arrow',true).first();
14752         //arrow.set(align[2], 
14753         
14754         this.el.addClass('in');
14755         this.hoverState = null;
14756         
14757         if (this.el.hasClass('fade')) {
14758             // fade it?
14759         }
14760         
14761     },
14762     hide : function()
14763     {
14764         this.el.setXY([0,0]);
14765         this.el.removeClass('in');
14766         this.el.hide();
14767         
14768     }
14769     
14770 });
14771
14772 Roo.bootstrap.Popover.alignment = {
14773     'left' : ['r-l', [-10,0], 'right'],
14774     'right' : ['l-r', [10,0], 'left'],
14775     'bottom' : ['t-b', [0,10], 'top'],
14776     'top' : [ 'b-t', [0,-10], 'bottom']
14777 };
14778
14779  /*
14780  * - LGPL
14781  *
14782  * Progress
14783  * 
14784  */
14785
14786 /**
14787  * @class Roo.bootstrap.Progress
14788  * @extends Roo.bootstrap.Component
14789  * Bootstrap Progress class
14790  * @cfg {Boolean} striped striped of the progress bar
14791  * @cfg {Boolean} active animated of the progress bar
14792  * 
14793  * 
14794  * @constructor
14795  * Create a new Progress
14796  * @param {Object} config The config object
14797  */
14798
14799 Roo.bootstrap.Progress = function(config){
14800     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
14801 };
14802
14803 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
14804     
14805     striped : false,
14806     active: false,
14807     
14808     getAutoCreate : function(){
14809         var cfg = {
14810             tag: 'div',
14811             cls: 'progress'
14812         };
14813         
14814         
14815         if(this.striped){
14816             cfg.cls += ' progress-striped';
14817         }
14818       
14819         if(this.active){
14820             cfg.cls += ' active';
14821         }
14822         
14823         
14824         return cfg;
14825     }
14826    
14827 });
14828
14829  
14830
14831  /*
14832  * - LGPL
14833  *
14834  * ProgressBar
14835  * 
14836  */
14837
14838 /**
14839  * @class Roo.bootstrap.ProgressBar
14840  * @extends Roo.bootstrap.Component
14841  * Bootstrap ProgressBar class
14842  * @cfg {Number} aria_valuenow aria-value now
14843  * @cfg {Number} aria_valuemin aria-value min
14844  * @cfg {Number} aria_valuemax aria-value max
14845  * @cfg {String} label label for the progress bar
14846  * @cfg {String} panel (success | info | warning | danger )
14847  * @cfg {String} role role of the progress bar
14848  * @cfg {String} sr_only text
14849  * 
14850  * 
14851  * @constructor
14852  * Create a new ProgressBar
14853  * @param {Object} config The config object
14854  */
14855
14856 Roo.bootstrap.ProgressBar = function(config){
14857     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
14858 };
14859
14860 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
14861     
14862     aria_valuenow : 0,
14863     aria_valuemin : 0,
14864     aria_valuemax : 100,
14865     label : false,
14866     panel : false,
14867     role : false,
14868     sr_only: false,
14869     
14870     getAutoCreate : function()
14871     {
14872         
14873         var cfg = {
14874             tag: 'div',
14875             cls: 'progress-bar',
14876             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
14877         };
14878         
14879         if(this.sr_only){
14880             cfg.cn = {
14881                 tag: 'span',
14882                 cls: 'sr-only',
14883                 html: this.sr_only
14884             }
14885         }
14886         
14887         if(this.role){
14888             cfg.role = this.role;
14889         }
14890         
14891         if(this.aria_valuenow){
14892             cfg['aria-valuenow'] = this.aria_valuenow;
14893         }
14894         
14895         if(this.aria_valuemin){
14896             cfg['aria-valuemin'] = this.aria_valuemin;
14897         }
14898         
14899         if(this.aria_valuemax){
14900             cfg['aria-valuemax'] = this.aria_valuemax;
14901         }
14902         
14903         if(this.label && !this.sr_only){
14904             cfg.html = this.label;
14905         }
14906         
14907         if(this.panel){
14908             cfg.cls += ' progress-bar-' + this.panel;
14909         }
14910         
14911         return cfg;
14912     },
14913     
14914     update : function(aria_valuenow)
14915     {
14916         this.aria_valuenow = aria_valuenow;
14917         
14918         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
14919     }
14920    
14921 });
14922
14923  
14924
14925  /*
14926  * - LGPL
14927  *
14928  * column
14929  * 
14930  */
14931
14932 /**
14933  * @class Roo.bootstrap.TabGroup
14934  * @extends Roo.bootstrap.Column
14935  * Bootstrap Column class
14936  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
14937  * @cfg {Boolean} carousel true to make the group behave like a carousel
14938  * @cfg {Number} bullets show the panel pointer.. default 0
14939  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
14940  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
14941  * @cfg {Number} timer auto slide timer .. default 0 millisecond
14942  * 
14943  * @constructor
14944  * Create a new TabGroup
14945  * @param {Object} config The config object
14946  */
14947
14948 Roo.bootstrap.TabGroup = function(config){
14949     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
14950     if (!this.navId) {
14951         this.navId = Roo.id();
14952     }
14953     this.tabs = [];
14954     Roo.bootstrap.TabGroup.register(this);
14955     
14956 };
14957
14958 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
14959     
14960     carousel : false,
14961     transition : false,
14962     bullets : 0,
14963     timer : 0,
14964     autoslide : false,
14965     slideFn : false,
14966     slideOnTouch : false,
14967     
14968     getAutoCreate : function()
14969     {
14970         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
14971         
14972         cfg.cls += ' tab-content';
14973         
14974         Roo.log('get auto create...............');
14975         
14976         if (this.carousel) {
14977             cfg.cls += ' carousel slide';
14978             
14979             cfg.cn = [{
14980                cls : 'carousel-inner'
14981             }];
14982         
14983             if(this.bullets > 0 && !Roo.isTouch){
14984                 
14985                 var bullets = {
14986                     cls : 'carousel-bullets',
14987                     cn : []
14988                 };
14989                 
14990                 if(this.bullets_cls){
14991                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
14992                 }
14993                 
14994                 for (var i = 0; i < this.bullets; i++){
14995                     bullets.cn.push({
14996                         cls : 'bullet bullet-' + i
14997                     });
14998                 }
14999                 
15000                 bullets.cn.push({
15001                     cls : 'clear'
15002                 });
15003                 
15004                 cfg.cn[0].cn = bullets;
15005             }
15006         }
15007         
15008         return cfg;
15009     },
15010     
15011     initEvents:  function()
15012     {
15013         Roo.log('-------- init events on tab group ---------');
15014         
15015         if(this.bullets > 0 && !Roo.isTouch){
15016             this.initBullet();
15017         }
15018         
15019         Roo.log(this);
15020         
15021         if(Roo.isTouch && this.slideOnTouch){
15022             this.el.on("touchstart", this.onTouchStart, this);
15023         }
15024         
15025         if(this.autoslide){
15026             var _this = this;
15027             
15028             this.slideFn = window.setInterval(function() {
15029                 _this.showPanelNext();
15030             }, this.timer);
15031         }
15032         
15033     },
15034     
15035     onTouchStart : function(e, el, o)
15036     {
15037         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
15038             return;
15039         }
15040         
15041         this.showPanelNext();
15042     },
15043     
15044     getChildContainer : function()
15045     {
15046         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
15047     },
15048     
15049     /**
15050     * register a Navigation item
15051     * @param {Roo.bootstrap.NavItem} the navitem to add
15052     */
15053     register : function(item)
15054     {
15055         this.tabs.push( item);
15056         item.navId = this.navId; // not really needed..
15057     
15058     },
15059     
15060     getActivePanel : function()
15061     {
15062         var r = false;
15063         Roo.each(this.tabs, function(t) {
15064             if (t.active) {
15065                 r = t;
15066                 return false;
15067             }
15068             return null;
15069         });
15070         return r;
15071         
15072     },
15073     getPanelByName : function(n)
15074     {
15075         var r = false;
15076         Roo.each(this.tabs, function(t) {
15077             if (t.tabId == n) {
15078                 r = t;
15079                 return false;
15080             }
15081             return null;
15082         });
15083         return r;
15084     },
15085     indexOfPanel : function(p)
15086     {
15087         var r = false;
15088         Roo.each(this.tabs, function(t,i) {
15089             if (t.tabId == p.tabId) {
15090                 r = i;
15091                 return false;
15092             }
15093             return null;
15094         });
15095         return r;
15096     },
15097     /**
15098      * show a specific panel
15099      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
15100      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
15101      */
15102     showPanel : function (pan)
15103     {
15104         if(this.transition){
15105             Roo.log("waiting for the transitionend");
15106             return;
15107         }
15108         
15109         if (typeof(pan) == 'number') {
15110             pan = this.tabs[pan];
15111         }
15112         if (typeof(pan) == 'string') {
15113             pan = this.getPanelByName(pan);
15114         }
15115         if (pan.tabId == this.getActivePanel().tabId) {
15116             return true;
15117         }
15118         var cur = this.getActivePanel();
15119         
15120         if (false === cur.fireEvent('beforedeactivate')) {
15121             return false;
15122         }
15123         
15124         if(this.bullets > 0 && !Roo.isTouch){
15125             this.setActiveBullet(this.indexOfPanel(pan));
15126         }
15127         
15128         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
15129             
15130             this.transition = true;
15131             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
15132             var lr = dir == 'next' ? 'left' : 'right';
15133             pan.el.addClass(dir); // or prev
15134             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
15135             cur.el.addClass(lr); // or right
15136             pan.el.addClass(lr);
15137             
15138             var _this = this;
15139             cur.el.on('transitionend', function() {
15140                 Roo.log("trans end?");
15141                 
15142                 pan.el.removeClass([lr,dir]);
15143                 pan.setActive(true);
15144                 
15145                 cur.el.removeClass([lr]);
15146                 cur.setActive(false);
15147                 
15148                 _this.transition = false;
15149                 
15150             }, this, { single:  true } );
15151             
15152             return true;
15153         }
15154         
15155         cur.setActive(false);
15156         pan.setActive(true);
15157         
15158         return true;
15159         
15160     },
15161     showPanelNext : function()
15162     {
15163         var i = this.indexOfPanel(this.getActivePanel());
15164         
15165         if (i >= this.tabs.length - 1 && !this.autoslide) {
15166             return;
15167         }
15168         
15169         if (i >= this.tabs.length - 1 && this.autoslide) {
15170             i = -1;
15171         }
15172         
15173         this.showPanel(this.tabs[i+1]);
15174     },
15175     
15176     showPanelPrev : function()
15177     {
15178         var i = this.indexOfPanel(this.getActivePanel());
15179         
15180         if (i  < 1 && !this.autoslide) {
15181             return;
15182         }
15183         
15184         if (i < 1 && this.autoslide) {
15185             i = this.tabs.length;
15186         }
15187         
15188         this.showPanel(this.tabs[i-1]);
15189     },
15190     
15191     initBullet : function()
15192     {
15193         if(Roo.isTouch){
15194             return;
15195         }
15196         
15197         var _this = this;
15198         
15199         for (var i = 0; i < this.bullets; i++){
15200             var bullet = this.el.select('.bullet-' + i, true).first();
15201
15202             if(!bullet){
15203                 continue;
15204             }
15205
15206             bullet.on('click', (function(e, el, o, ii, t){
15207
15208                 e.preventDefault();
15209
15210                 _this.showPanel(ii);
15211
15212                 if(_this.autoslide && _this.slideFn){
15213                     clearInterval(_this.slideFn);
15214                     _this.slideFn = window.setInterval(function() {
15215                         _this.showPanelNext();
15216                     }, _this.timer);
15217                 }
15218
15219             }).createDelegate(this, [i, bullet], true));
15220         }
15221     },
15222     
15223     setActiveBullet : function(i)
15224     {
15225         if(Roo.isTouch){
15226             return;
15227         }
15228         
15229         Roo.each(this.el.select('.bullet', true).elements, function(el){
15230             el.removeClass('selected');
15231         });
15232
15233         var bullet = this.el.select('.bullet-' + i, true).first();
15234         
15235         if(!bullet){
15236             return;
15237         }
15238         
15239         bullet.addClass('selected');
15240     }
15241     
15242     
15243   
15244 });
15245
15246  
15247
15248  
15249  
15250 Roo.apply(Roo.bootstrap.TabGroup, {
15251     
15252     groups: {},
15253      /**
15254     * register a Navigation Group
15255     * @param {Roo.bootstrap.NavGroup} the navgroup to add
15256     */
15257     register : function(navgrp)
15258     {
15259         this.groups[navgrp.navId] = navgrp;
15260         
15261     },
15262     /**
15263     * fetch a Navigation Group based on the navigation ID
15264     * if one does not exist , it will get created.
15265     * @param {string} the navgroup to add
15266     * @returns {Roo.bootstrap.NavGroup} the navgroup 
15267     */
15268     get: function(navId) {
15269         if (typeof(this.groups[navId]) == 'undefined') {
15270             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
15271         }
15272         return this.groups[navId] ;
15273     }
15274     
15275     
15276     
15277 });
15278
15279  /*
15280  * - LGPL
15281  *
15282  * TabPanel
15283  * 
15284  */
15285
15286 /**
15287  * @class Roo.bootstrap.TabPanel
15288  * @extends Roo.bootstrap.Component
15289  * Bootstrap TabPanel class
15290  * @cfg {Boolean} active panel active
15291  * @cfg {String} html panel content
15292  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
15293  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
15294  * 
15295  * 
15296  * @constructor
15297  * Create a new TabPanel
15298  * @param {Object} config The config object
15299  */
15300
15301 Roo.bootstrap.TabPanel = function(config){
15302     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
15303     this.addEvents({
15304         /**
15305              * @event changed
15306              * Fires when the active status changes
15307              * @param {Roo.bootstrap.TabPanel} this
15308              * @param {Boolean} state the new state
15309             
15310          */
15311         'changed': true,
15312         /**
15313              * @event beforedeactivate
15314              * Fires before a tab is de-activated - can be used to do validation on a form.
15315              * @param {Roo.bootstrap.TabPanel} this
15316              * @return {Boolean} false if there is an error
15317             
15318          */
15319         'beforedeactivate': true
15320      });
15321     
15322     this.tabId = this.tabId || Roo.id();
15323   
15324 };
15325
15326 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
15327     
15328     active: false,
15329     html: false,
15330     tabId: false,
15331     navId : false,
15332     
15333     getAutoCreate : function(){
15334         var cfg = {
15335             tag: 'div',
15336             // item is needed for carousel - not sure if it has any effect otherwise
15337             cls: 'tab-pane item',
15338             html: this.html || ''
15339         };
15340         
15341         if(this.active){
15342             cfg.cls += ' active';
15343         }
15344         
15345         if(this.tabId){
15346             cfg.tabId = this.tabId;
15347         }
15348         
15349         
15350         return cfg;
15351     },
15352     
15353     initEvents:  function()
15354     {
15355         Roo.log('-------- init events on tab panel ---------');
15356         
15357         var p = this.parent();
15358         this.navId = this.navId || p.navId;
15359         
15360         if (typeof(this.navId) != 'undefined') {
15361             // not really needed.. but just in case.. parent should be a NavGroup.
15362             var tg = Roo.bootstrap.TabGroup.get(this.navId);
15363             Roo.log(['register', tg, this]);
15364             tg.register(this);
15365             
15366             var i = tg.tabs.length - 1;
15367             
15368             if(this.active && tg.bullets > 0 && i < tg.bullets){
15369                 tg.setActiveBullet(i);
15370             }
15371         }
15372         
15373     },
15374     
15375     
15376     onRender : function(ct, position)
15377     {
15378        // Roo.log("Call onRender: " + this.xtype);
15379         
15380         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
15381         
15382         
15383         
15384         
15385         
15386     },
15387     
15388     setActive: function(state)
15389     {
15390         Roo.log("panel - set active " + this.tabId + "=" + state);
15391         
15392         this.active = state;
15393         if (!state) {
15394             this.el.removeClass('active');
15395             
15396         } else  if (!this.el.hasClass('active')) {
15397             this.el.addClass('active');
15398         }
15399         
15400         this.fireEvent('changed', this, state);
15401     }
15402     
15403     
15404 });
15405  
15406
15407  
15408
15409  /*
15410  * - LGPL
15411  *
15412  * DateField
15413  * 
15414  */
15415
15416 /**
15417  * @class Roo.bootstrap.DateField
15418  * @extends Roo.bootstrap.Input
15419  * Bootstrap DateField class
15420  * @cfg {Number} weekStart default 0
15421  * @cfg {String} viewMode default empty, (months|years)
15422  * @cfg {String} minViewMode default empty, (months|years)
15423  * @cfg {Number} startDate default -Infinity
15424  * @cfg {Number} endDate default Infinity
15425  * @cfg {Boolean} todayHighlight default false
15426  * @cfg {Boolean} todayBtn default false
15427  * @cfg {Boolean} calendarWeeks default false
15428  * @cfg {Object} daysOfWeekDisabled default empty
15429  * @cfg {Boolean} singleMode default false (true | false)
15430  * 
15431  * @cfg {Boolean} keyboardNavigation default true
15432  * @cfg {String} language default en
15433  * 
15434  * @constructor
15435  * Create a new DateField
15436  * @param {Object} config The config object
15437  */
15438
15439 Roo.bootstrap.DateField = function(config){
15440     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
15441      this.addEvents({
15442             /**
15443              * @event show
15444              * Fires when this field show.
15445              * @param {Roo.bootstrap.DateField} this
15446              * @param {Mixed} date The date value
15447              */
15448             show : true,
15449             /**
15450              * @event show
15451              * Fires when this field hide.
15452              * @param {Roo.bootstrap.DateField} this
15453              * @param {Mixed} date The date value
15454              */
15455             hide : true,
15456             /**
15457              * @event select
15458              * Fires when select a date.
15459              * @param {Roo.bootstrap.DateField} this
15460              * @param {Mixed} date The date value
15461              */
15462             select : true
15463         });
15464 };
15465
15466 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
15467     
15468     /**
15469      * @cfg {String} format
15470      * The default date format string which can be overriden for localization support.  The format must be
15471      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
15472      */
15473     format : "m/d/y",
15474     /**
15475      * @cfg {String} altFormats
15476      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
15477      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
15478      */
15479     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
15480     
15481     weekStart : 0,
15482     
15483     viewMode : '',
15484     
15485     minViewMode : '',
15486     
15487     todayHighlight : false,
15488     
15489     todayBtn: false,
15490     
15491     language: 'en',
15492     
15493     keyboardNavigation: true,
15494     
15495     calendarWeeks: false,
15496     
15497     startDate: -Infinity,
15498     
15499     endDate: Infinity,
15500     
15501     daysOfWeekDisabled: [],
15502     
15503     _events: [],
15504     
15505     singleMode : false,
15506     
15507     UTCDate: function()
15508     {
15509         return new Date(Date.UTC.apply(Date, arguments));
15510     },
15511     
15512     UTCToday: function()
15513     {
15514         var today = new Date();
15515         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
15516     },
15517     
15518     getDate: function() {
15519             var d = this.getUTCDate();
15520             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
15521     },
15522     
15523     getUTCDate: function() {
15524             return this.date;
15525     },
15526     
15527     setDate: function(d) {
15528             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
15529     },
15530     
15531     setUTCDate: function(d) {
15532             this.date = d;
15533             this.setValue(this.formatDate(this.date));
15534     },
15535         
15536     onRender: function(ct, position)
15537     {
15538         
15539         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
15540         
15541         this.language = this.language || 'en';
15542         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
15543         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
15544         
15545         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
15546         this.format = this.format || 'm/d/y';
15547         this.isInline = false;
15548         this.isInput = true;
15549         this.component = this.el.select('.add-on', true).first() || false;
15550         this.component = (this.component && this.component.length === 0) ? false : this.component;
15551         this.hasInput = this.component && this.inputEL().length;
15552         
15553         if (typeof(this.minViewMode === 'string')) {
15554             switch (this.minViewMode) {
15555                 case 'months':
15556                     this.minViewMode = 1;
15557                     break;
15558                 case 'years':
15559                     this.minViewMode = 2;
15560                     break;
15561                 default:
15562                     this.minViewMode = 0;
15563                     break;
15564             }
15565         }
15566         
15567         if (typeof(this.viewMode === 'string')) {
15568             switch (this.viewMode) {
15569                 case 'months':
15570                     this.viewMode = 1;
15571                     break;
15572                 case 'years':
15573                     this.viewMode = 2;
15574                     break;
15575                 default:
15576                     this.viewMode = 0;
15577                     break;
15578             }
15579         }
15580                 
15581         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
15582         
15583 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
15584         
15585         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15586         
15587         this.picker().on('mousedown', this.onMousedown, this);
15588         this.picker().on('click', this.onClick, this);
15589         
15590         this.picker().addClass('datepicker-dropdown');
15591         
15592         this.startViewMode = this.viewMode;
15593         
15594         if(this.singleMode){
15595             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
15596                 v.setVisibilityMode(Roo.Element.DISPLAY)
15597                 v.hide();
15598             });
15599             
15600             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
15601                 v.setStyle('width', '189px');
15602             });
15603         }
15604         
15605         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
15606             if(!this.calendarWeeks){
15607                 v.remove();
15608                 return;
15609             }
15610             
15611             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
15612             v.attr('colspan', function(i, val){
15613                 return parseInt(val) + 1;
15614             });
15615         })
15616                         
15617         
15618         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
15619         
15620         this.setStartDate(this.startDate);
15621         this.setEndDate(this.endDate);
15622         
15623         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
15624         
15625         this.fillDow();
15626         this.fillMonths();
15627         this.update();
15628         this.showMode();
15629         
15630         if(this.isInline) {
15631             this.show();
15632         }
15633     },
15634     
15635     picker : function()
15636     {
15637         return this.pickerEl;
15638 //        return this.el.select('.datepicker', true).first();
15639     },
15640     
15641     fillDow: function()
15642     {
15643         var dowCnt = this.weekStart;
15644         
15645         var dow = {
15646             tag: 'tr',
15647             cn: [
15648                 
15649             ]
15650         };
15651         
15652         if(this.calendarWeeks){
15653             dow.cn.push({
15654                 tag: 'th',
15655                 cls: 'cw',
15656                 html: '&nbsp;'
15657             })
15658         }
15659         
15660         while (dowCnt < this.weekStart + 7) {
15661             dow.cn.push({
15662                 tag: 'th',
15663                 cls: 'dow',
15664                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
15665             });
15666         }
15667         
15668         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
15669     },
15670     
15671     fillMonths: function()
15672     {    
15673         var i = 0;
15674         var months = this.picker().select('>.datepicker-months td', true).first();
15675         
15676         months.dom.innerHTML = '';
15677         
15678         while (i < 12) {
15679             var month = {
15680                 tag: 'span',
15681                 cls: 'month',
15682                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
15683             }
15684             
15685             months.createChild(month);
15686         }
15687         
15688     },
15689     
15690     update: function()
15691     {
15692         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;
15693         
15694         if (this.date < this.startDate) {
15695             this.viewDate = new Date(this.startDate);
15696         } else if (this.date > this.endDate) {
15697             this.viewDate = new Date(this.endDate);
15698         } else {
15699             this.viewDate = new Date(this.date);
15700         }
15701         
15702         this.fill();
15703     },
15704     
15705     fill: function() 
15706     {
15707         var d = new Date(this.viewDate),
15708                 year = d.getUTCFullYear(),
15709                 month = d.getUTCMonth(),
15710                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
15711                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
15712                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
15713                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
15714                 currentDate = this.date && this.date.valueOf(),
15715                 today = this.UTCToday();
15716         
15717         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
15718         
15719 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
15720         
15721 //        this.picker.select('>tfoot th.today').
15722 //                                              .text(dates[this.language].today)
15723 //                                              .toggle(this.todayBtn !== false);
15724     
15725         this.updateNavArrows();
15726         this.fillMonths();
15727                                                 
15728         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
15729         
15730         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
15731          
15732         prevMonth.setUTCDate(day);
15733         
15734         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
15735         
15736         var nextMonth = new Date(prevMonth);
15737         
15738         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
15739         
15740         nextMonth = nextMonth.valueOf();
15741         
15742         var fillMonths = false;
15743         
15744         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
15745         
15746         while(prevMonth.valueOf() < nextMonth) {
15747             var clsName = '';
15748             
15749             if (prevMonth.getUTCDay() === this.weekStart) {
15750                 if(fillMonths){
15751                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
15752                 }
15753                     
15754                 fillMonths = {
15755                     tag: 'tr',
15756                     cn: []
15757                 };
15758                 
15759                 if(this.calendarWeeks){
15760                     // ISO 8601: First week contains first thursday.
15761                     // ISO also states week starts on Monday, but we can be more abstract here.
15762                     var
15763                     // Start of current week: based on weekstart/current date
15764                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
15765                     // Thursday of this week
15766                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
15767                     // First Thursday of year, year from thursday
15768                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
15769                     // Calendar week: ms between thursdays, div ms per day, div 7 days
15770                     calWeek =  (th - yth) / 864e5 / 7 + 1;
15771                     
15772                     fillMonths.cn.push({
15773                         tag: 'td',
15774                         cls: 'cw',
15775                         html: calWeek
15776                     });
15777                 }
15778             }
15779             
15780             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
15781                 clsName += ' old';
15782             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
15783                 clsName += ' new';
15784             }
15785             if (this.todayHighlight &&
15786                 prevMonth.getUTCFullYear() == today.getFullYear() &&
15787                 prevMonth.getUTCMonth() == today.getMonth() &&
15788                 prevMonth.getUTCDate() == today.getDate()) {
15789                 clsName += ' today';
15790             }
15791             
15792             if (currentDate && prevMonth.valueOf() === currentDate) {
15793                 clsName += ' active';
15794             }
15795             
15796             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
15797                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
15798                     clsName += ' disabled';
15799             }
15800             
15801             fillMonths.cn.push({
15802                 tag: 'td',
15803                 cls: 'day ' + clsName,
15804                 html: prevMonth.getDate()
15805             })
15806             
15807             prevMonth.setDate(prevMonth.getDate()+1);
15808         }
15809           
15810         var currentYear = this.date && this.date.getUTCFullYear();
15811         var currentMonth = this.date && this.date.getUTCMonth();
15812         
15813         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
15814         
15815         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
15816             v.removeClass('active');
15817             
15818             if(currentYear === year && k === currentMonth){
15819                 v.addClass('active');
15820             }
15821             
15822             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
15823                 v.addClass('disabled');
15824             }
15825             
15826         });
15827         
15828         
15829         year = parseInt(year/10, 10) * 10;
15830         
15831         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
15832         
15833         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
15834         
15835         year -= 1;
15836         for (var i = -1; i < 11; i++) {
15837             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
15838                 tag: 'span',
15839                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
15840                 html: year
15841             })
15842             
15843             year += 1;
15844         }
15845     },
15846     
15847     showMode: function(dir) 
15848     {
15849         if (dir) {
15850             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
15851         }
15852         
15853         Roo.each(this.picker().select('>div',true).elements, function(v){
15854             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15855             v.hide();
15856         });
15857         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
15858     },
15859     
15860     place: function()
15861     {
15862         if(this.isInline) return;
15863         
15864         this.picker().removeClass(['bottom', 'top']);
15865         
15866         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
15867             /*
15868              * place to the top of element!
15869              *
15870              */
15871             
15872             this.picker().addClass('top');
15873             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
15874             
15875             return;
15876         }
15877         
15878         this.picker().addClass('bottom');
15879         
15880         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
15881     },
15882     
15883     parseDate : function(value)
15884     {
15885         if(!value || value instanceof Date){
15886             return value;
15887         }
15888         var v = Date.parseDate(value, this.format);
15889         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
15890             v = Date.parseDate(value, 'Y-m-d');
15891         }
15892         if(!v && this.altFormats){
15893             if(!this.altFormatsArray){
15894                 this.altFormatsArray = this.altFormats.split("|");
15895             }
15896             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
15897                 v = Date.parseDate(value, this.altFormatsArray[i]);
15898             }
15899         }
15900         return v;
15901     },
15902     
15903     formatDate : function(date, fmt)
15904     {   
15905         return (!date || !(date instanceof Date)) ?
15906         date : date.dateFormat(fmt || this.format);
15907     },
15908     
15909     onFocus : function()
15910     {
15911         Roo.bootstrap.DateField.superclass.onFocus.call(this);
15912         this.show();
15913     },
15914     
15915     onBlur : function()
15916     {
15917         Roo.bootstrap.DateField.superclass.onBlur.call(this);
15918         
15919         var d = this.inputEl().getValue();
15920         
15921         this.setValue(d);
15922                 
15923         this.hide();
15924     },
15925     
15926     show : function()
15927     {
15928         this.picker().show();
15929         this.update();
15930         this.place();
15931         
15932         this.fireEvent('show', this, this.date);
15933     },
15934     
15935     hide : function()
15936     {
15937         if(this.isInline) return;
15938         this.picker().hide();
15939         this.viewMode = this.startViewMode;
15940         this.showMode();
15941         
15942         this.fireEvent('hide', this, this.date);
15943         
15944     },
15945     
15946     onMousedown: function(e)
15947     {
15948         e.stopPropagation();
15949         e.preventDefault();
15950     },
15951     
15952     keyup: function(e)
15953     {
15954         Roo.bootstrap.DateField.superclass.keyup.call(this);
15955         this.update();
15956     },
15957
15958     setValue: function(v)
15959     {
15960         
15961         // v can be a string or a date..
15962         
15963         
15964         var d = new Date(this.parseDate(v) ).clearTime();
15965         
15966         if(isNaN(d.getTime())){
15967             this.date = this.viewDate = '';
15968             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
15969             return;
15970         }
15971         
15972         v = this.formatDate(d);
15973         
15974         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
15975         
15976         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
15977      
15978         this.update();
15979
15980         this.fireEvent('select', this, this.date);
15981         
15982     },
15983     
15984     getValue: function()
15985     {
15986         return this.formatDate(this.date);
15987     },
15988     
15989     fireKey: function(e)
15990     {
15991         if (!this.picker().isVisible()){
15992             if (e.keyCode == 27) // allow escape to hide and re-show picker
15993                 this.show();
15994             return;
15995         }
15996         
15997         var dateChanged = false,
15998         dir, day, month,
15999         newDate, newViewDate;
16000         
16001         switch(e.keyCode){
16002             case 27: // escape
16003                 this.hide();
16004                 e.preventDefault();
16005                 break;
16006             case 37: // left
16007             case 39: // right
16008                 if (!this.keyboardNavigation) break;
16009                 dir = e.keyCode == 37 ? -1 : 1;
16010                 
16011                 if (e.ctrlKey){
16012                     newDate = this.moveYear(this.date, dir);
16013                     newViewDate = this.moveYear(this.viewDate, dir);
16014                 } else if (e.shiftKey){
16015                     newDate = this.moveMonth(this.date, dir);
16016                     newViewDate = this.moveMonth(this.viewDate, dir);
16017                 } else {
16018                     newDate = new Date(this.date);
16019                     newDate.setUTCDate(this.date.getUTCDate() + dir);
16020                     newViewDate = new Date(this.viewDate);
16021                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
16022                 }
16023                 if (this.dateWithinRange(newDate)){
16024                     this.date = newDate;
16025                     this.viewDate = newViewDate;
16026                     this.setValue(this.formatDate(this.date));
16027 //                    this.update();
16028                     e.preventDefault();
16029                     dateChanged = true;
16030                 }
16031                 break;
16032             case 38: // up
16033             case 40: // down
16034                 if (!this.keyboardNavigation) break;
16035                 dir = e.keyCode == 38 ? -1 : 1;
16036                 if (e.ctrlKey){
16037                     newDate = this.moveYear(this.date, dir);
16038                     newViewDate = this.moveYear(this.viewDate, dir);
16039                 } else if (e.shiftKey){
16040                     newDate = this.moveMonth(this.date, dir);
16041                     newViewDate = this.moveMonth(this.viewDate, dir);
16042                 } else {
16043                     newDate = new Date(this.date);
16044                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
16045                     newViewDate = new Date(this.viewDate);
16046                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
16047                 }
16048                 if (this.dateWithinRange(newDate)){
16049                     this.date = newDate;
16050                     this.viewDate = newViewDate;
16051                     this.setValue(this.formatDate(this.date));
16052 //                    this.update();
16053                     e.preventDefault();
16054                     dateChanged = true;
16055                 }
16056                 break;
16057             case 13: // enter
16058                 this.setValue(this.formatDate(this.date));
16059                 this.hide();
16060                 e.preventDefault();
16061                 break;
16062             case 9: // tab
16063                 this.setValue(this.formatDate(this.date));
16064                 this.hide();
16065                 break;
16066             case 16: // shift
16067             case 17: // ctrl
16068             case 18: // alt
16069                 break;
16070             default :
16071                 this.hide();
16072                 
16073         }
16074     },
16075     
16076     
16077     onClick: function(e) 
16078     {
16079         e.stopPropagation();
16080         e.preventDefault();
16081         
16082         var target = e.getTarget();
16083         
16084         if(target.nodeName.toLowerCase() === 'i'){
16085             target = Roo.get(target).dom.parentNode;
16086         }
16087         
16088         var nodeName = target.nodeName;
16089         var className = target.className;
16090         var html = target.innerHTML;
16091         //Roo.log(nodeName);
16092         
16093         switch(nodeName.toLowerCase()) {
16094             case 'th':
16095                 switch(className) {
16096                     case 'switch':
16097                         this.showMode(1);
16098                         break;
16099                     case 'prev':
16100                     case 'next':
16101                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
16102                         switch(this.viewMode){
16103                                 case 0:
16104                                         this.viewDate = this.moveMonth(this.viewDate, dir);
16105                                         break;
16106                                 case 1:
16107                                 case 2:
16108                                         this.viewDate = this.moveYear(this.viewDate, dir);
16109                                         break;
16110                         }
16111                         this.fill();
16112                         break;
16113                     case 'today':
16114                         var date = new Date();
16115                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
16116 //                        this.fill()
16117                         this.setValue(this.formatDate(this.date));
16118                         
16119                         this.hide();
16120                         break;
16121                 }
16122                 break;
16123             case 'span':
16124                 if (className.indexOf('disabled') < 0) {
16125                     this.viewDate.setUTCDate(1);
16126                     if (className.indexOf('month') > -1) {
16127                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
16128                     } else {
16129                         var year = parseInt(html, 10) || 0;
16130                         this.viewDate.setUTCFullYear(year);
16131                         
16132                     }
16133                     
16134                     if(this.singleMode){
16135                         this.setValue(this.formatDate(this.viewDate));
16136                         this.hide();
16137                         return;
16138                     }
16139                     
16140                     this.showMode(-1);
16141                     this.fill();
16142                 }
16143                 break;
16144                 
16145             case 'td':
16146                 //Roo.log(className);
16147                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
16148                     var day = parseInt(html, 10) || 1;
16149                     var year = this.viewDate.getUTCFullYear(),
16150                         month = this.viewDate.getUTCMonth();
16151
16152                     if (className.indexOf('old') > -1) {
16153                         if(month === 0 ){
16154                             month = 11;
16155                             year -= 1;
16156                         }else{
16157                             month -= 1;
16158                         }
16159                     } else if (className.indexOf('new') > -1) {
16160                         if (month == 11) {
16161                             month = 0;
16162                             year += 1;
16163                         } else {
16164                             month += 1;
16165                         }
16166                     }
16167                     //Roo.log([year,month,day]);
16168                     this.date = this.UTCDate(year, month, day,0,0,0,0);
16169                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
16170 //                    this.fill();
16171                     //Roo.log(this.formatDate(this.date));
16172                     this.setValue(this.formatDate(this.date));
16173                     this.hide();
16174                 }
16175                 break;
16176         }
16177     },
16178     
16179     setStartDate: function(startDate)
16180     {
16181         this.startDate = startDate || -Infinity;
16182         if (this.startDate !== -Infinity) {
16183             this.startDate = this.parseDate(this.startDate);
16184         }
16185         this.update();
16186         this.updateNavArrows();
16187     },
16188
16189     setEndDate: function(endDate)
16190     {
16191         this.endDate = endDate || Infinity;
16192         if (this.endDate !== Infinity) {
16193             this.endDate = this.parseDate(this.endDate);
16194         }
16195         this.update();
16196         this.updateNavArrows();
16197     },
16198     
16199     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
16200     {
16201         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
16202         if (typeof(this.daysOfWeekDisabled) !== 'object') {
16203             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
16204         }
16205         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
16206             return parseInt(d, 10);
16207         });
16208         this.update();
16209         this.updateNavArrows();
16210     },
16211     
16212     updateNavArrows: function() 
16213     {
16214         if(this.singleMode){
16215             return;
16216         }
16217         
16218         var d = new Date(this.viewDate),
16219         year = d.getUTCFullYear(),
16220         month = d.getUTCMonth();
16221         
16222         Roo.each(this.picker().select('.prev', true).elements, function(v){
16223             v.show();
16224             switch (this.viewMode) {
16225                 case 0:
16226
16227                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
16228                         v.hide();
16229                     }
16230                     break;
16231                 case 1:
16232                 case 2:
16233                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
16234                         v.hide();
16235                     }
16236                     break;
16237             }
16238         });
16239         
16240         Roo.each(this.picker().select('.next', true).elements, function(v){
16241             v.show();
16242             switch (this.viewMode) {
16243                 case 0:
16244
16245                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
16246                         v.hide();
16247                     }
16248                     break;
16249                 case 1:
16250                 case 2:
16251                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
16252                         v.hide();
16253                     }
16254                     break;
16255             }
16256         })
16257     },
16258     
16259     moveMonth: function(date, dir)
16260     {
16261         if (!dir) return date;
16262         var new_date = new Date(date.valueOf()),
16263         day = new_date.getUTCDate(),
16264         month = new_date.getUTCMonth(),
16265         mag = Math.abs(dir),
16266         new_month, test;
16267         dir = dir > 0 ? 1 : -1;
16268         if (mag == 1){
16269             test = dir == -1
16270             // If going back one month, make sure month is not current month
16271             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
16272             ? function(){
16273                 return new_date.getUTCMonth() == month;
16274             }
16275             // If going forward one month, make sure month is as expected
16276             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
16277             : function(){
16278                 return new_date.getUTCMonth() != new_month;
16279             };
16280             new_month = month + dir;
16281             new_date.setUTCMonth(new_month);
16282             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
16283             if (new_month < 0 || new_month > 11)
16284                 new_month = (new_month + 12) % 12;
16285         } else {
16286             // For magnitudes >1, move one month at a time...
16287             for (var i=0; i<mag; i++)
16288                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
16289                 new_date = this.moveMonth(new_date, dir);
16290             // ...then reset the day, keeping it in the new month
16291             new_month = new_date.getUTCMonth();
16292             new_date.setUTCDate(day);
16293             test = function(){
16294                 return new_month != new_date.getUTCMonth();
16295             };
16296         }
16297         // Common date-resetting loop -- if date is beyond end of month, make it
16298         // end of month
16299         while (test()){
16300             new_date.setUTCDate(--day);
16301             new_date.setUTCMonth(new_month);
16302         }
16303         return new_date;
16304     },
16305
16306     moveYear: function(date, dir)
16307     {
16308         return this.moveMonth(date, dir*12);
16309     },
16310
16311     dateWithinRange: function(date)
16312     {
16313         return date >= this.startDate && date <= this.endDate;
16314     },
16315
16316     
16317     remove: function() 
16318     {
16319         this.picker().remove();
16320     }
16321    
16322 });
16323
16324 Roo.apply(Roo.bootstrap.DateField,  {
16325     
16326     head : {
16327         tag: 'thead',
16328         cn: [
16329         {
16330             tag: 'tr',
16331             cn: [
16332             {
16333                 tag: 'th',
16334                 cls: 'prev',
16335                 html: '<i class="fa fa-arrow-left"/>'
16336             },
16337             {
16338                 tag: 'th',
16339                 cls: 'switch',
16340                 colspan: '5'
16341             },
16342             {
16343                 tag: 'th',
16344                 cls: 'next',
16345                 html: '<i class="fa fa-arrow-right"/>'
16346             }
16347
16348             ]
16349         }
16350         ]
16351     },
16352     
16353     content : {
16354         tag: 'tbody',
16355         cn: [
16356         {
16357             tag: 'tr',
16358             cn: [
16359             {
16360                 tag: 'td',
16361                 colspan: '7'
16362             }
16363             ]
16364         }
16365         ]
16366     },
16367     
16368     footer : {
16369         tag: 'tfoot',
16370         cn: [
16371         {
16372             tag: 'tr',
16373             cn: [
16374             {
16375                 tag: 'th',
16376                 colspan: '7',
16377                 cls: 'today'
16378             }
16379                     
16380             ]
16381         }
16382         ]
16383     },
16384     
16385     dates:{
16386         en: {
16387             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
16388             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
16389             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
16390             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
16391             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
16392             today: "Today"
16393         }
16394     },
16395     
16396     modes: [
16397     {
16398         clsName: 'days',
16399         navFnc: 'Month',
16400         navStep: 1
16401     },
16402     {
16403         clsName: 'months',
16404         navFnc: 'FullYear',
16405         navStep: 1
16406     },
16407     {
16408         clsName: 'years',
16409         navFnc: 'FullYear',
16410         navStep: 10
16411     }]
16412 });
16413
16414 Roo.apply(Roo.bootstrap.DateField,  {
16415   
16416     template : {
16417         tag: 'div',
16418         cls: 'datepicker dropdown-menu roo-dynamic',
16419         cn: [
16420         {
16421             tag: 'div',
16422             cls: 'datepicker-days',
16423             cn: [
16424             {
16425                 tag: 'table',
16426                 cls: 'table-condensed',
16427                 cn:[
16428                 Roo.bootstrap.DateField.head,
16429                 {
16430                     tag: 'tbody'
16431                 },
16432                 Roo.bootstrap.DateField.footer
16433                 ]
16434             }
16435             ]
16436         },
16437         {
16438             tag: 'div',
16439             cls: 'datepicker-months',
16440             cn: [
16441             {
16442                 tag: 'table',
16443                 cls: 'table-condensed',
16444                 cn:[
16445                 Roo.bootstrap.DateField.head,
16446                 Roo.bootstrap.DateField.content,
16447                 Roo.bootstrap.DateField.footer
16448                 ]
16449             }
16450             ]
16451         },
16452         {
16453             tag: 'div',
16454             cls: 'datepicker-years',
16455             cn: [
16456             {
16457                 tag: 'table',
16458                 cls: 'table-condensed',
16459                 cn:[
16460                 Roo.bootstrap.DateField.head,
16461                 Roo.bootstrap.DateField.content,
16462                 Roo.bootstrap.DateField.footer
16463                 ]
16464             }
16465             ]
16466         }
16467         ]
16468     }
16469 });
16470
16471  
16472
16473  /*
16474  * - LGPL
16475  *
16476  * TimeField
16477  * 
16478  */
16479
16480 /**
16481  * @class Roo.bootstrap.TimeField
16482  * @extends Roo.bootstrap.Input
16483  * Bootstrap DateField class
16484  * 
16485  * 
16486  * @constructor
16487  * Create a new TimeField
16488  * @param {Object} config The config object
16489  */
16490
16491 Roo.bootstrap.TimeField = function(config){
16492     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
16493     this.addEvents({
16494             /**
16495              * @event show
16496              * Fires when this field show.
16497              * @param {Roo.bootstrap.DateField} thisthis
16498              * @param {Mixed} date The date value
16499              */
16500             show : true,
16501             /**
16502              * @event show
16503              * Fires when this field hide.
16504              * @param {Roo.bootstrap.DateField} this
16505              * @param {Mixed} date The date value
16506              */
16507             hide : true,
16508             /**
16509              * @event select
16510              * Fires when select a date.
16511              * @param {Roo.bootstrap.DateField} this
16512              * @param {Mixed} date The date value
16513              */
16514             select : true
16515         });
16516 };
16517
16518 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
16519     
16520     /**
16521      * @cfg {String} format
16522      * The default time format string which can be overriden for localization support.  The format must be
16523      * valid according to {@link Date#parseDate} (defaults to 'H:i').
16524      */
16525     format : "H:i",
16526        
16527     onRender: function(ct, position)
16528     {
16529         
16530         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
16531                 
16532         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
16533         
16534         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16535         
16536         this.pop = this.picker().select('>.datepicker-time',true).first();
16537         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16538         
16539         this.picker().on('mousedown', this.onMousedown, this);
16540         this.picker().on('click', this.onClick, this);
16541         
16542         this.picker().addClass('datepicker-dropdown');
16543     
16544         this.fillTime();
16545         this.update();
16546             
16547         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
16548         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
16549         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
16550         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
16551         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
16552         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
16553
16554     },
16555     
16556     fireKey: function(e){
16557         if (!this.picker().isVisible()){
16558             if (e.keyCode == 27) { // allow escape to hide and re-show picker
16559                 this.show();
16560             }
16561             return;
16562         }
16563
16564         e.preventDefault();
16565         
16566         switch(e.keyCode){
16567             case 27: // escape
16568                 this.hide();
16569                 break;
16570             case 37: // left
16571             case 39: // right
16572                 this.onTogglePeriod();
16573                 break;
16574             case 38: // up
16575                 this.onIncrementMinutes();
16576                 break;
16577             case 40: // down
16578                 this.onDecrementMinutes();
16579                 break;
16580             case 13: // enter
16581             case 9: // tab
16582                 this.setTime();
16583                 break;
16584         }
16585     },
16586     
16587     onClick: function(e) {
16588         e.stopPropagation();
16589         e.preventDefault();
16590     },
16591     
16592     picker : function()
16593     {
16594         return this.el.select('.datepicker', true).first();
16595     },
16596     
16597     fillTime: function()
16598     {    
16599         var time = this.pop.select('tbody', true).first();
16600         
16601         time.dom.innerHTML = '';
16602         
16603         time.createChild({
16604             tag: 'tr',
16605             cn: [
16606                 {
16607                     tag: 'td',
16608                     cn: [
16609                         {
16610                             tag: 'a',
16611                             href: '#',
16612                             cls: 'btn',
16613                             cn: [
16614                                 {
16615                                     tag: 'span',
16616                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
16617                                 }
16618                             ]
16619                         } 
16620                     ]
16621                 },
16622                 {
16623                     tag: 'td',
16624                     cls: 'separator'
16625                 },
16626                 {
16627                     tag: 'td',
16628                     cn: [
16629                         {
16630                             tag: 'a',
16631                             href: '#',
16632                             cls: 'btn',
16633                             cn: [
16634                                 {
16635                                     tag: 'span',
16636                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
16637                                 }
16638                             ]
16639                         }
16640                     ]
16641                 },
16642                 {
16643                     tag: 'td',
16644                     cls: 'separator'
16645                 }
16646             ]
16647         });
16648         
16649         time.createChild({
16650             tag: 'tr',
16651             cn: [
16652                 {
16653                     tag: 'td',
16654                     cn: [
16655                         {
16656                             tag: 'span',
16657                             cls: 'timepicker-hour',
16658                             html: '00'
16659                         }  
16660                     ]
16661                 },
16662                 {
16663                     tag: 'td',
16664                     cls: 'separator',
16665                     html: ':'
16666                 },
16667                 {
16668                     tag: 'td',
16669                     cn: [
16670                         {
16671                             tag: 'span',
16672                             cls: 'timepicker-minute',
16673                             html: '00'
16674                         }  
16675                     ]
16676                 },
16677                 {
16678                     tag: 'td',
16679                     cls: 'separator'
16680                 },
16681                 {
16682                     tag: 'td',
16683                     cn: [
16684                         {
16685                             tag: 'button',
16686                             type: 'button',
16687                             cls: 'btn btn-primary period',
16688                             html: 'AM'
16689                             
16690                         }
16691                     ]
16692                 }
16693             ]
16694         });
16695         
16696         time.createChild({
16697             tag: 'tr',
16698             cn: [
16699                 {
16700                     tag: 'td',
16701                     cn: [
16702                         {
16703                             tag: 'a',
16704                             href: '#',
16705                             cls: 'btn',
16706                             cn: [
16707                                 {
16708                                     tag: 'span',
16709                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
16710                                 }
16711                             ]
16712                         }
16713                     ]
16714                 },
16715                 {
16716                     tag: 'td',
16717                     cls: 'separator'
16718                 },
16719                 {
16720                     tag: 'td',
16721                     cn: [
16722                         {
16723                             tag: 'a',
16724                             href: '#',
16725                             cls: 'btn',
16726                             cn: [
16727                                 {
16728                                     tag: 'span',
16729                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
16730                                 }
16731                             ]
16732                         }
16733                     ]
16734                 },
16735                 {
16736                     tag: 'td',
16737                     cls: 'separator'
16738                 }
16739             ]
16740         });
16741         
16742     },
16743     
16744     update: function()
16745     {
16746         
16747         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
16748         
16749         this.fill();
16750     },
16751     
16752     fill: function() 
16753     {
16754         var hours = this.time.getHours();
16755         var minutes = this.time.getMinutes();
16756         var period = 'AM';
16757         
16758         if(hours > 11){
16759             period = 'PM';
16760         }
16761         
16762         if(hours == 0){
16763             hours = 12;
16764         }
16765         
16766         
16767         if(hours > 12){
16768             hours = hours - 12;
16769         }
16770         
16771         if(hours < 10){
16772             hours = '0' + hours;
16773         }
16774         
16775         if(minutes < 10){
16776             minutes = '0' + minutes;
16777         }
16778         
16779         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
16780         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
16781         this.pop.select('button', true).first().dom.innerHTML = period;
16782         
16783     },
16784     
16785     place: function()
16786     {   
16787         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
16788         
16789         var cls = ['bottom'];
16790         
16791         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
16792             cls.pop();
16793             cls.push('top');
16794         }
16795         
16796         cls.push('right');
16797         
16798         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
16799             cls.pop();
16800             cls.push('left');
16801         }
16802         
16803         this.picker().addClass(cls.join('-'));
16804         
16805         var _this = this;
16806         
16807         Roo.each(cls, function(c){
16808             if(c == 'bottom'){
16809                 _this.picker().setTop(_this.inputEl().getHeight());
16810                 return;
16811             }
16812             if(c == 'top'){
16813                 _this.picker().setTop(0 - _this.picker().getHeight());
16814                 return;
16815             }
16816             
16817             if(c == 'left'){
16818                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
16819                 return;
16820             }
16821             if(c == 'right'){
16822                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
16823                 return;
16824             }
16825         });
16826         
16827     },
16828   
16829     onFocus : function()
16830     {
16831         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
16832         this.show();
16833     },
16834     
16835     onBlur : function()
16836     {
16837         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
16838         this.hide();
16839     },
16840     
16841     show : function()
16842     {
16843         this.picker().show();
16844         this.pop.show();
16845         this.update();
16846         this.place();
16847         
16848         this.fireEvent('show', this, this.date);
16849     },
16850     
16851     hide : function()
16852     {
16853         this.picker().hide();
16854         this.pop.hide();
16855         
16856         this.fireEvent('hide', this, this.date);
16857     },
16858     
16859     setTime : function()
16860     {
16861         this.hide();
16862         this.setValue(this.time.format(this.format));
16863         
16864         this.fireEvent('select', this, this.date);
16865         
16866         
16867     },
16868     
16869     onMousedown: function(e){
16870         e.stopPropagation();
16871         e.preventDefault();
16872     },
16873     
16874     onIncrementHours: function()
16875     {
16876         Roo.log('onIncrementHours');
16877         this.time = this.time.add(Date.HOUR, 1);
16878         this.update();
16879         
16880     },
16881     
16882     onDecrementHours: function()
16883     {
16884         Roo.log('onDecrementHours');
16885         this.time = this.time.add(Date.HOUR, -1);
16886         this.update();
16887     },
16888     
16889     onIncrementMinutes: function()
16890     {
16891         Roo.log('onIncrementMinutes');
16892         this.time = this.time.add(Date.MINUTE, 1);
16893         this.update();
16894     },
16895     
16896     onDecrementMinutes: function()
16897     {
16898         Roo.log('onDecrementMinutes');
16899         this.time = this.time.add(Date.MINUTE, -1);
16900         this.update();
16901     },
16902     
16903     onTogglePeriod: function()
16904     {
16905         Roo.log('onTogglePeriod');
16906         this.time = this.time.add(Date.HOUR, 12);
16907         this.update();
16908     }
16909     
16910    
16911 });
16912
16913 Roo.apply(Roo.bootstrap.TimeField,  {
16914     
16915     content : {
16916         tag: 'tbody',
16917         cn: [
16918             {
16919                 tag: 'tr',
16920                 cn: [
16921                 {
16922                     tag: 'td',
16923                     colspan: '7'
16924                 }
16925                 ]
16926             }
16927         ]
16928     },
16929     
16930     footer : {
16931         tag: 'tfoot',
16932         cn: [
16933             {
16934                 tag: 'tr',
16935                 cn: [
16936                 {
16937                     tag: 'th',
16938                     colspan: '7',
16939                     cls: '',
16940                     cn: [
16941                         {
16942                             tag: 'button',
16943                             cls: 'btn btn-info ok',
16944                             html: 'OK'
16945                         }
16946                     ]
16947                 }
16948
16949                 ]
16950             }
16951         ]
16952     }
16953 });
16954
16955 Roo.apply(Roo.bootstrap.TimeField,  {
16956   
16957     template : {
16958         tag: 'div',
16959         cls: 'datepicker dropdown-menu',
16960         cn: [
16961             {
16962                 tag: 'div',
16963                 cls: 'datepicker-time',
16964                 cn: [
16965                 {
16966                     tag: 'table',
16967                     cls: 'table-condensed',
16968                     cn:[
16969                     Roo.bootstrap.TimeField.content,
16970                     Roo.bootstrap.TimeField.footer
16971                     ]
16972                 }
16973                 ]
16974             }
16975         ]
16976     }
16977 });
16978
16979  
16980
16981  /*
16982  * - LGPL
16983  *
16984  * MonthField
16985  * 
16986  */
16987
16988 /**
16989  * @class Roo.bootstrap.MonthField
16990  * @extends Roo.bootstrap.Input
16991  * Bootstrap MonthField class
16992  * 
16993  * @cfg {String} language default en
16994  * 
16995  * @constructor
16996  * Create a new MonthField
16997  * @param {Object} config The config object
16998  */
16999
17000 Roo.bootstrap.MonthField = function(config){
17001     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
17002     
17003     this.addEvents({
17004         /**
17005          * @event show
17006          * Fires when this field show.
17007          * @param {Roo.bootstrap.MonthField} this
17008          * @param {Mixed} date The date value
17009          */
17010         show : true,
17011         /**
17012          * @event show
17013          * Fires when this field hide.
17014          * @param {Roo.bootstrap.MonthField} this
17015          * @param {Mixed} date The date value
17016          */
17017         hide : true,
17018         /**
17019          * @event select
17020          * Fires when select a date.
17021          * @param {Roo.bootstrap.MonthField} this
17022          * @param {String} oldvalue The old value
17023          * @param {String} newvalue The new value
17024          */
17025         select : true
17026     });
17027 };
17028
17029 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
17030     
17031     onRender: function(ct, position)
17032     {
17033         
17034         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
17035         
17036         this.language = this.language || 'en';
17037         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
17038         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
17039         
17040         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
17041         this.isInline = false;
17042         this.isInput = true;
17043         this.component = this.el.select('.add-on', true).first() || false;
17044         this.component = (this.component && this.component.length === 0) ? false : this.component;
17045         this.hasInput = this.component && this.inputEL().length;
17046         
17047         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
17048         
17049         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17050         
17051         this.picker().on('mousedown', this.onMousedown, this);
17052         this.picker().on('click', this.onClick, this);
17053         
17054         this.picker().addClass('datepicker-dropdown');
17055         
17056         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17057             v.setStyle('width', '189px');
17058         });
17059         
17060         this.fillMonths();
17061         
17062         this.update();
17063         
17064         if(this.isInline) {
17065             this.show();
17066         }
17067         
17068     },
17069     
17070     setValue: function(v, suppressEvent)
17071     {   
17072         var o = this.getValue();
17073         
17074         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
17075         
17076         this.update();
17077
17078         if(suppressEvent !== true){
17079             this.fireEvent('select', this, o, v);
17080         }
17081         
17082     },
17083     
17084     getValue: function()
17085     {
17086         return this.value;
17087     },
17088     
17089     onClick: function(e) 
17090     {
17091         e.stopPropagation();
17092         e.preventDefault();
17093         
17094         var target = e.getTarget();
17095         
17096         if(target.nodeName.toLowerCase() === 'i'){
17097             target = Roo.get(target).dom.parentNode;
17098         }
17099         
17100         var nodeName = target.nodeName;
17101         var className = target.className;
17102         var html = target.innerHTML;
17103         
17104         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
17105             return;
17106         }
17107         
17108         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
17109         
17110         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17111         
17112         this.hide();
17113                         
17114     },
17115     
17116     picker : function()
17117     {
17118         return this.pickerEl;
17119     },
17120     
17121     fillMonths: function()
17122     {    
17123         var i = 0;
17124         var months = this.picker().select('>.datepicker-months td', true).first();
17125         
17126         months.dom.innerHTML = '';
17127         
17128         while (i < 12) {
17129             var month = {
17130                 tag: 'span',
17131                 cls: 'month',
17132                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
17133             }
17134             
17135             months.createChild(month);
17136         }
17137         
17138     },
17139     
17140     update: function()
17141     {
17142         var _this = this;
17143         
17144         if(typeof(this.vIndex) == 'undefined' && this.value.length){
17145             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
17146         }
17147         
17148         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
17149             e.removeClass('active');
17150             
17151             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
17152                 e.addClass('active');
17153             }
17154         })
17155     },
17156     
17157     place: function()
17158     {
17159         if(this.isInline) return;
17160         
17161         this.picker().removeClass(['bottom', 'top']);
17162         
17163         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17164             /*
17165              * place to the top of element!
17166              *
17167              */
17168             
17169             this.picker().addClass('top');
17170             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17171             
17172             return;
17173         }
17174         
17175         this.picker().addClass('bottom');
17176         
17177         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17178     },
17179     
17180     onFocus : function()
17181     {
17182         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
17183         this.show();
17184     },
17185     
17186     onBlur : function()
17187     {
17188         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
17189         
17190         var d = this.inputEl().getValue();
17191         
17192         this.setValue(d);
17193                 
17194         this.hide();
17195     },
17196     
17197     show : function()
17198     {
17199         this.picker().show();
17200         this.picker().select('>.datepicker-months', true).first().show();
17201         this.update();
17202         this.place();
17203         
17204         this.fireEvent('show', this, this.date);
17205     },
17206     
17207     hide : function()
17208     {
17209         if(this.isInline) return;
17210         this.picker().hide();
17211         this.fireEvent('hide', this, this.date);
17212         
17213     },
17214     
17215     onMousedown: function(e)
17216     {
17217         e.stopPropagation();
17218         e.preventDefault();
17219     },
17220     
17221     keyup: function(e)
17222     {
17223         Roo.bootstrap.MonthField.superclass.keyup.call(this);
17224         this.update();
17225     },
17226
17227     fireKey: function(e)
17228     {
17229         if (!this.picker().isVisible()){
17230             if (e.keyCode == 27) // allow escape to hide and re-show picker
17231                 this.show();
17232             return;
17233         }
17234         
17235         var dir;
17236         
17237         switch(e.keyCode){
17238             case 27: // escape
17239                 this.hide();
17240                 e.preventDefault();
17241                 break;
17242             case 37: // left
17243             case 39: // right
17244                 dir = e.keyCode == 37 ? -1 : 1;
17245                 
17246                 this.vIndex = this.vIndex + dir;
17247                 
17248                 if(this.vIndex < 0){
17249                     this.vIndex = 0;
17250                 }
17251                 
17252                 if(this.vIndex > 11){
17253                     this.vIndex = 11;
17254                 }
17255                 
17256                 if(isNaN(this.vIndex)){
17257                     this.vIndex = 0;
17258                 }
17259                 
17260                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17261                 
17262                 break;
17263             case 38: // up
17264             case 40: // down
17265                 
17266                 dir = e.keyCode == 38 ? -1 : 1;
17267                 
17268                 this.vIndex = this.vIndex + dir * 4;
17269                 
17270                 if(this.vIndex < 0){
17271                     this.vIndex = 0;
17272                 }
17273                 
17274                 if(this.vIndex > 11){
17275                     this.vIndex = 11;
17276                 }
17277                 
17278                 if(isNaN(this.vIndex)){
17279                     this.vIndex = 0;
17280                 }
17281                 
17282                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17283                 break;
17284                 
17285             case 13: // enter
17286                 
17287                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17288                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17289                 }
17290                 
17291                 this.hide();
17292                 e.preventDefault();
17293                 break;
17294             case 9: // tab
17295                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17296                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17297                 }
17298                 this.hide();
17299                 break;
17300             case 16: // shift
17301             case 17: // ctrl
17302             case 18: // alt
17303                 break;
17304             default :
17305                 this.hide();
17306                 
17307         }
17308     },
17309     
17310     remove: function() 
17311     {
17312         this.picker().remove();
17313     }
17314    
17315 });
17316
17317 Roo.apply(Roo.bootstrap.MonthField,  {
17318     
17319     content : {
17320         tag: 'tbody',
17321         cn: [
17322         {
17323             tag: 'tr',
17324             cn: [
17325             {
17326                 tag: 'td',
17327                 colspan: '7'
17328             }
17329             ]
17330         }
17331         ]
17332     },
17333     
17334     dates:{
17335         en: {
17336             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17337             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
17338         }
17339     }
17340 });
17341
17342 Roo.apply(Roo.bootstrap.MonthField,  {
17343   
17344     template : {
17345         tag: 'div',
17346         cls: 'datepicker dropdown-menu roo-dynamic',
17347         cn: [
17348             {
17349                 tag: 'div',
17350                 cls: 'datepicker-months',
17351                 cn: [
17352                 {
17353                     tag: 'table',
17354                     cls: 'table-condensed',
17355                     cn:[
17356                         Roo.bootstrap.DateField.content
17357                     ]
17358                 }
17359                 ]
17360             }
17361         ]
17362     }
17363 });
17364
17365  
17366
17367  
17368  /*
17369  * - LGPL
17370  *
17371  * CheckBox
17372  * 
17373  */
17374
17375 /**
17376  * @class Roo.bootstrap.CheckBox
17377  * @extends Roo.bootstrap.Input
17378  * Bootstrap CheckBox class
17379  * 
17380  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
17381  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
17382  * @cfg {String} boxLabel The text that appears beside the checkbox
17383  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
17384  * @cfg {Boolean} checked initnal the element
17385  * @cfg {Boolean} inline inline the element (default false)
17386  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
17387  * 
17388  * @constructor
17389  * Create a new CheckBox
17390  * @param {Object} config The config object
17391  */
17392
17393 Roo.bootstrap.CheckBox = function(config){
17394     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
17395    
17396     this.addEvents({
17397         /**
17398         * @event check
17399         * Fires when the element is checked or unchecked.
17400         * @param {Roo.bootstrap.CheckBox} this This input
17401         * @param {Boolean} checked The new checked value
17402         */
17403        check : true
17404     });
17405     
17406 };
17407
17408 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
17409   
17410     inputType: 'checkbox',
17411     inputValue: 1,
17412     valueOff: 0,
17413     boxLabel: false,
17414     checked: false,
17415     weight : false,
17416     inline: false,
17417     
17418     getAutoCreate : function()
17419     {
17420         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
17421         
17422         var id = Roo.id();
17423         
17424         var cfg = {};
17425         
17426         cfg.cls = 'form-group ' + this.inputType; //input-group
17427         
17428         if(this.inline){
17429             cfg.cls += ' ' + this.inputType + '-inline';
17430         }
17431         
17432         var input =  {
17433             tag: 'input',
17434             id : id,
17435             type : this.inputType,
17436             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
17437             cls : 'roo-' + this.inputType, //'form-box',
17438             placeholder : this.placeholder || ''
17439             
17440         };
17441         
17442         if (this.weight) { // Validity check?
17443             cfg.cls += " " + this.inputType + "-" + this.weight;
17444         }
17445         
17446         if (this.disabled) {
17447             input.disabled=true;
17448         }
17449         
17450         if(this.checked){
17451             input.checked = this.checked;
17452         }
17453         
17454         if (this.name) {
17455             input.name = this.name;
17456         }
17457         
17458         if (this.size) {
17459             input.cls += ' input-' + this.size;
17460         }
17461         
17462         var settings=this;
17463         
17464         ['xs','sm','md','lg'].map(function(size){
17465             if (settings[size]) {
17466                 cfg.cls += ' col-' + size + '-' + settings[size];
17467             }
17468         });
17469         
17470         var inputblock = input;
17471          
17472         if (this.before || this.after) {
17473             
17474             inputblock = {
17475                 cls : 'input-group',
17476                 cn :  [] 
17477             };
17478             
17479             if (this.before) {
17480                 inputblock.cn.push({
17481                     tag :'span',
17482                     cls : 'input-group-addon',
17483                     html : this.before
17484                 });
17485             }
17486             
17487             inputblock.cn.push(input);
17488             
17489             if (this.after) {
17490                 inputblock.cn.push({
17491                     tag :'span',
17492                     cls : 'input-group-addon',
17493                     html : this.after
17494                 });
17495             }
17496             
17497         }
17498         
17499         if (align ==='left' && this.fieldLabel.length) {
17500                 Roo.log("left and has label");
17501                 cfg.cn = [
17502                     
17503                     {
17504                         tag: 'label',
17505                         'for' :  id,
17506                         cls : 'control-label col-md-' + this.labelWidth,
17507                         html : this.fieldLabel
17508                         
17509                     },
17510                     {
17511                         cls : "col-md-" + (12 - this.labelWidth), 
17512                         cn: [
17513                             inputblock
17514                         ]
17515                     }
17516                     
17517                 ];
17518         } else if ( this.fieldLabel.length) {
17519                 Roo.log(" label");
17520                 cfg.cn = [
17521                    
17522                     {
17523                         tag: this.boxLabel ? 'span' : 'label',
17524                         'for': id,
17525                         cls: 'control-label box-input-label',
17526                         //cls : 'input-group-addon',
17527                         html : this.fieldLabel
17528                         
17529                     },
17530                     
17531                     inputblock
17532                     
17533                 ];
17534
17535         } else {
17536             
17537                 Roo.log(" no label && no align");
17538                 cfg.cn = [  inputblock ] ;
17539                 
17540                 
17541         }
17542         if(this.boxLabel){
17543              var boxLabelCfg = {
17544                 tag: 'label',
17545                 //'for': id, // box label is handled by onclick - so no for...
17546                 cls: 'box-label',
17547                 html: this.boxLabel
17548             }
17549             
17550             if(this.tooltip){
17551                 boxLabelCfg.tooltip = this.tooltip;
17552             }
17553              
17554             cfg.cn.push(boxLabelCfg);
17555         }
17556         
17557         
17558        
17559         return cfg;
17560         
17561     },
17562     
17563     /**
17564      * return the real input element.
17565      */
17566     inputEl: function ()
17567     {
17568         return this.el.select('input.roo-' + this.inputType,true).first();
17569     },
17570     
17571     labelEl: function()
17572     {
17573         return this.el.select('label.control-label',true).first();
17574     },
17575     /* depricated... */
17576     
17577     label: function()
17578     {
17579         return this.labelEl();
17580     },
17581     
17582     initEvents : function()
17583     {
17584 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
17585         
17586         this.inputEl().on('click', this.onClick,  this);
17587         
17588         if (this.boxLabel) { 
17589             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
17590         }
17591         
17592         this.startValue = this.getValue();
17593         
17594         if(this.groupId){
17595             Roo.bootstrap.CheckBox.register(this);
17596         }
17597     },
17598     
17599     onClick : function()
17600     {   
17601         this.setChecked(!this.checked);
17602     },
17603     
17604     setChecked : function(state,suppressEvent)
17605     {
17606         this.startValue = this.getValue();
17607         
17608         if(this.inputType == 'radio'){
17609             
17610             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17611                 e.dom.checked = false;
17612             });
17613             
17614             this.inputEl().dom.checked = true;
17615             
17616             this.inputEl().dom.value = this.inputValue;
17617             
17618             if(suppressEvent !== true){
17619                 this.fireEvent('check', this, true);
17620             }
17621             
17622             this.validate();
17623             
17624             return;
17625         }
17626         
17627         this.checked = state;
17628         
17629         this.inputEl().dom.checked = state;
17630         
17631         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
17632         
17633         if(suppressEvent !== true){
17634             this.fireEvent('check', this, state);
17635         }
17636         
17637         this.validate();
17638     },
17639     
17640     getValue : function()
17641     {
17642         if(this.inputType == 'radio'){
17643             return this.getGroupValue();
17644         }
17645         
17646         return this.inputEl().getValue();
17647         
17648     },
17649     
17650     getGroupValue : function()
17651     {
17652         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
17653             return '';
17654         }
17655         
17656         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
17657     },
17658     
17659     setValue : function(v,suppressEvent)
17660     {
17661         if(this.inputType == 'radio'){
17662             this.setGroupValue(v, suppressEvent);
17663             return;
17664         }
17665         
17666         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
17667         
17668         this.validate();
17669     },
17670     
17671     setGroupValue : function(v, suppressEvent)
17672     {
17673         this.startValue = this.getValue();
17674         
17675         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17676             e.dom.checked = false;
17677             
17678             if(e.dom.value == v){
17679                 e.dom.checked = true;
17680             }
17681         });
17682         
17683         if(suppressEvent !== true){
17684             this.fireEvent('check', this, true);
17685         }
17686
17687         this.validate();
17688         
17689         return;
17690     },
17691     
17692     validate : function()
17693     {
17694         if(
17695                 this.disabled || 
17696                 (this.inputType == 'radio' && this.validateRadio()) ||
17697                 (this.inputType == 'checkbox' && this.validateCheckbox())
17698         ){
17699             this.markValid();
17700             return true;
17701         }
17702         
17703         this.markInvalid();
17704         return false;
17705     },
17706     
17707     validateRadio : function()
17708     {
17709         var valid = false;
17710         
17711         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17712             if(!e.dom.checked){
17713                 return;
17714             }
17715             
17716             valid = true;
17717             
17718             return false;
17719         });
17720         
17721         return valid;
17722     },
17723     
17724     validateCheckbox : function()
17725     {
17726         if(!this.groupId){
17727             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
17728         }
17729         
17730         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17731         
17732         if(!group){
17733             return false;
17734         }
17735         
17736         var r = false;
17737         
17738         for(var i in group){
17739             if(r){
17740                 break;
17741             }
17742             
17743             r = (group[i].getValue() == group[i].inputValue) ? true : false;
17744         }
17745         
17746         return r;
17747     },
17748     
17749     /**
17750      * Mark this field as valid
17751      */
17752     markValid : function()
17753     {
17754         if(this.allowBlank){
17755             return;
17756         }
17757         
17758         var _this = this;
17759         
17760         this.fireEvent('valid', this);
17761         
17762         if(this.inputType == 'radio'){
17763             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17764                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
17765                 e.findParent('.form-group', false, true).addClass(_this.validClass);
17766             });
17767             
17768             return;
17769         }
17770         
17771         if(!this.groupId){
17772             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17773             this.el.findParent('.form-group', false, true).addClass(this.validClass);
17774             return;
17775         }
17776         
17777         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17778             
17779         if(!group){
17780             return;
17781         }
17782         
17783         for(var i in group){
17784             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17785             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
17786         }
17787     },
17788     
17789      /**
17790      * Mark this field as invalid
17791      * @param {String} msg The validation message
17792      */
17793     markInvalid : function(msg)
17794     {
17795         if(this.allowBlank){
17796             return;
17797         }
17798         
17799         var _this = this;
17800         
17801         this.fireEvent('invalid', this, msg);
17802         
17803         if(this.inputType == 'radio'){
17804             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17805                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
17806                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
17807             });
17808             
17809             return;
17810         }
17811         
17812         if(!this.groupId){
17813             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17814             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
17815             return;
17816         }
17817         
17818         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17819             
17820         if(!group){
17821             return;
17822         }
17823         
17824         for(var i in group){
17825             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17826             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
17827         }
17828         
17829     }
17830     
17831 });
17832
17833 Roo.apply(Roo.bootstrap.CheckBox, {
17834     
17835     groups: {},
17836     
17837      /**
17838     * register a CheckBox Group
17839     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
17840     */
17841     register : function(checkbox)
17842     {
17843         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
17844             this.groups[checkbox.groupId] = {};
17845         }
17846         
17847         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
17848             return;
17849         }
17850         
17851         this.groups[checkbox.groupId][checkbox.name] = checkbox;
17852         
17853     },
17854     /**
17855     * fetch a CheckBox Group based on the group ID
17856     * @param {string} the group ID
17857     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
17858     */
17859     get: function(groupId) {
17860         if (typeof(this.groups[groupId]) == 'undefined') {
17861             return false;
17862         }
17863         
17864         return this.groups[groupId] ;
17865     }
17866     
17867     
17868 });
17869 /*
17870  * - LGPL
17871  *
17872  * Radio
17873  *
17874  *
17875  * not inline
17876  *<div class="radio">
17877   <label>
17878     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
17879     Option one is this and that&mdash;be sure to include why it's great
17880   </label>
17881 </div>
17882  *
17883  *
17884  *inline
17885  *<span>
17886  *<label class="radio-inline">fieldLabel</label>
17887  *<label class="radio-inline">
17888   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
17889 </label>
17890 <span>
17891  * 
17892  * 
17893  */
17894
17895 /**
17896  * @class Roo.bootstrap.Radio
17897  * @extends Roo.bootstrap.CheckBox
17898  * Bootstrap Radio class
17899
17900  * @constructor
17901  * Create a new Radio
17902  * @param {Object} config The config object
17903  */
17904
17905 Roo.bootstrap.Radio = function(config){
17906     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
17907    
17908 };
17909
17910 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
17911     
17912     inputType: 'radio',
17913     inputValue: '',
17914     valueOff: '',
17915     
17916     getAutoCreate : function()
17917     {
17918         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
17919         align = align || 'left'; // default...
17920         
17921         
17922         
17923         var id = Roo.id();
17924         
17925         var cfg = {
17926                 tag : this.inline ? 'span' : 'div',
17927                 cls : '',
17928                 cn : []
17929         };
17930         
17931         var inline = this.inline ? ' radio-inline' : '';
17932         
17933         var lbl = {
17934                 tag: 'label' ,
17935                 // does not need for, as we wrap the input with it..
17936                 'for' : id,
17937                 cls : 'control-label box-label' + inline,
17938                 cn : []
17939         };
17940         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
17941         
17942         var fieldLabel = {
17943             tag: 'label' ,
17944             //cls : 'control-label' + inline,
17945             html : this.fieldLabel,
17946             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
17947         };
17948         
17949  
17950         
17951         
17952         var input =  {
17953             tag: 'input',
17954             id : id,
17955             type : this.inputType,
17956             //value : (!this.checked) ? this.valueOff : this.inputValue,
17957             value : this.inputValue,
17958             cls : 'roo-radio',
17959             placeholder : this.placeholder || '' // ?? needed????
17960             
17961         };
17962         if (this.weight) { // Validity check?
17963             input.cls += " radio-" + this.weight;
17964         }
17965         if (this.disabled) {
17966             input.disabled=true;
17967         }
17968         
17969         if(this.checked){
17970             input.checked = this.checked;
17971         }
17972         
17973         if (this.name) {
17974             input.name = this.name;
17975         }
17976         
17977         if (this.size) {
17978             input.cls += ' input-' + this.size;
17979         }
17980         
17981         //?? can span's inline have a width??
17982         
17983         var settings=this;
17984         ['xs','sm','md','lg'].map(function(size){
17985             if (settings[size]) {
17986                 cfg.cls += ' col-' + size + '-' + settings[size];
17987             }
17988         });
17989         
17990         var inputblock = input;
17991         
17992         if (this.before || this.after) {
17993             
17994             inputblock = {
17995                 cls : 'input-group',
17996                 tag : 'span',
17997                 cn :  [] 
17998             };
17999             if (this.before) {
18000                 inputblock.cn.push({
18001                     tag :'span',
18002                     cls : 'input-group-addon',
18003                     html : this.before
18004                 });
18005             }
18006             inputblock.cn.push(input);
18007             if (this.after) {
18008                 inputblock.cn.push({
18009                     tag :'span',
18010                     cls : 'input-group-addon',
18011                     html : this.after
18012                 });
18013             }
18014             
18015         };
18016         
18017         
18018         if (this.fieldLabel && this.fieldLabel.length) {
18019             cfg.cn.push(fieldLabel);
18020         }
18021        
18022         // normal bootstrap puts the input inside the label.
18023         // however with our styled version - it has to go after the input.
18024        
18025         //lbl.cn.push(inputblock);
18026         
18027         var lblwrap =  {
18028             tag: 'span',
18029             cls: 'radio' + inline,
18030             cn: [
18031                 inputblock,
18032                 lbl
18033             ]
18034         };
18035         
18036         cfg.cn.push( lblwrap);
18037         
18038         if(this.boxLabel){
18039             lbl.cn.push({
18040                 tag: 'span',
18041                 html: this.boxLabel
18042             })
18043         }
18044          
18045         
18046         return cfg;
18047         
18048     },
18049     
18050     initEvents : function()
18051     {
18052 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18053         
18054         this.inputEl().on('click', this.onClick,  this);
18055         if (this.boxLabel) {
18056             Roo.log('find label')
18057             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
18058         }
18059         
18060     },
18061     
18062     inputEl: function ()
18063     {
18064         return this.el.select('input.roo-radio',true).first();
18065     },
18066     onClick : function()
18067     {   
18068         Roo.log("click");
18069         this.setChecked(true);
18070     },
18071     
18072     setChecked : function(state,suppressEvent)
18073     {
18074         if(state){
18075             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18076                 v.dom.checked = false;
18077             });
18078         }
18079         Roo.log(this.inputEl().dom);
18080         this.checked = state;
18081         this.inputEl().dom.checked = state;
18082         
18083         if(suppressEvent !== true){
18084             this.fireEvent('check', this, state);
18085         }
18086         
18087         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18088         
18089     },
18090     
18091     getGroupValue : function()
18092     {
18093         var value = '';
18094         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18095             if(v.dom.checked == true){
18096                 value = v.dom.value;
18097             }
18098         });
18099         
18100         return value;
18101     },
18102     
18103     /**
18104      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
18105      * @return {Mixed} value The field value
18106      */
18107     getValue : function(){
18108         return this.getGroupValue();
18109     }
18110     
18111 });
18112
18113  
18114 //<script type="text/javascript">
18115
18116 /*
18117  * Based  Ext JS Library 1.1.1
18118  * Copyright(c) 2006-2007, Ext JS, LLC.
18119  * LGPL
18120  *
18121  */
18122  
18123 /**
18124  * @class Roo.HtmlEditorCore
18125  * @extends Roo.Component
18126  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
18127  *
18128  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
18129  */
18130
18131 Roo.HtmlEditorCore = function(config){
18132     
18133     
18134     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
18135     
18136     
18137     this.addEvents({
18138         /**
18139          * @event initialize
18140          * Fires when the editor is fully initialized (including the iframe)
18141          * @param {Roo.HtmlEditorCore} this
18142          */
18143         initialize: true,
18144         /**
18145          * @event activate
18146          * Fires when the editor is first receives the focus. Any insertion must wait
18147          * until after this event.
18148          * @param {Roo.HtmlEditorCore} this
18149          */
18150         activate: true,
18151          /**
18152          * @event beforesync
18153          * Fires before the textarea is updated with content from the editor iframe. Return false
18154          * to cancel the sync.
18155          * @param {Roo.HtmlEditorCore} this
18156          * @param {String} html
18157          */
18158         beforesync: true,
18159          /**
18160          * @event beforepush
18161          * Fires before the iframe editor is updated with content from the textarea. Return false
18162          * to cancel the push.
18163          * @param {Roo.HtmlEditorCore} this
18164          * @param {String} html
18165          */
18166         beforepush: true,
18167          /**
18168          * @event sync
18169          * Fires when the textarea is updated with content from the editor iframe.
18170          * @param {Roo.HtmlEditorCore} this
18171          * @param {String} html
18172          */
18173         sync: true,
18174          /**
18175          * @event push
18176          * Fires when the iframe editor is updated with content from the textarea.
18177          * @param {Roo.HtmlEditorCore} this
18178          * @param {String} html
18179          */
18180         push: true,
18181         
18182         /**
18183          * @event editorevent
18184          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
18185          * @param {Roo.HtmlEditorCore} this
18186          */
18187         editorevent: true
18188         
18189     });
18190     
18191     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
18192     
18193     // defaults : white / black...
18194     this.applyBlacklists();
18195     
18196     
18197     
18198 };
18199
18200
18201 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
18202
18203
18204      /**
18205      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
18206      */
18207     
18208     owner : false,
18209     
18210      /**
18211      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
18212      *                        Roo.resizable.
18213      */
18214     resizable : false,
18215      /**
18216      * @cfg {Number} height (in pixels)
18217      */   
18218     height: 300,
18219    /**
18220      * @cfg {Number} width (in pixels)
18221      */   
18222     width: 500,
18223     
18224     /**
18225      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
18226      * 
18227      */
18228     stylesheets: false,
18229     
18230     // id of frame..
18231     frameId: false,
18232     
18233     // private properties
18234     validationEvent : false,
18235     deferHeight: true,
18236     initialized : false,
18237     activated : false,
18238     sourceEditMode : false,
18239     onFocus : Roo.emptyFn,
18240     iframePad:3,
18241     hideMode:'offsets',
18242     
18243     clearUp: true,
18244     
18245     // blacklist + whitelisted elements..
18246     black: false,
18247     white: false,
18248      
18249     
18250
18251     /**
18252      * Protected method that will not generally be called directly. It
18253      * is called when the editor initializes the iframe with HTML contents. Override this method if you
18254      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
18255      */
18256     getDocMarkup : function(){
18257         // body styles..
18258         var st = '';
18259         
18260         // inherit styels from page...?? 
18261         if (this.stylesheets === false) {
18262             
18263             Roo.get(document.head).select('style').each(function(node) {
18264                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18265             });
18266             
18267             Roo.get(document.head).select('link').each(function(node) { 
18268                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18269             });
18270             
18271         } else if (!this.stylesheets.length) {
18272                 // simple..
18273                 st = '<style type="text/css">' +
18274                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18275                    '</style>';
18276         } else { 
18277             
18278         }
18279         
18280         st +=  '<style type="text/css">' +
18281             'IMG { cursor: pointer } ' +
18282         '</style>';
18283
18284         
18285         return '<html><head>' + st  +
18286             //<style type="text/css">' +
18287             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18288             //'</style>' +
18289             ' </head><body class="roo-htmleditor-body"></body></html>';
18290     },
18291
18292     // private
18293     onRender : function(ct, position)
18294     {
18295         var _t = this;
18296         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
18297         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
18298         
18299         
18300         this.el.dom.style.border = '0 none';
18301         this.el.dom.setAttribute('tabIndex', -1);
18302         this.el.addClass('x-hidden hide');
18303         
18304         
18305         
18306         if(Roo.isIE){ // fix IE 1px bogus margin
18307             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
18308         }
18309        
18310         
18311         this.frameId = Roo.id();
18312         
18313          
18314         
18315         var iframe = this.owner.wrap.createChild({
18316             tag: 'iframe',
18317             cls: 'form-control', // bootstrap..
18318             id: this.frameId,
18319             name: this.frameId,
18320             frameBorder : 'no',
18321             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
18322         }, this.el
18323         );
18324         
18325         
18326         this.iframe = iframe.dom;
18327
18328          this.assignDocWin();
18329         
18330         this.doc.designMode = 'on';
18331        
18332         this.doc.open();
18333         this.doc.write(this.getDocMarkup());
18334         this.doc.close();
18335
18336         
18337         var task = { // must defer to wait for browser to be ready
18338             run : function(){
18339                 //console.log("run task?" + this.doc.readyState);
18340                 this.assignDocWin();
18341                 if(this.doc.body || this.doc.readyState == 'complete'){
18342                     try {
18343                         this.doc.designMode="on";
18344                     } catch (e) {
18345                         return;
18346                     }
18347                     Roo.TaskMgr.stop(task);
18348                     this.initEditor.defer(10, this);
18349                 }
18350             },
18351             interval : 10,
18352             duration: 10000,
18353             scope: this
18354         };
18355         Roo.TaskMgr.start(task);
18356
18357     },
18358
18359     // private
18360     onResize : function(w, h)
18361     {
18362          Roo.log('resize: ' +w + ',' + h );
18363         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
18364         if(!this.iframe){
18365             return;
18366         }
18367         if(typeof w == 'number'){
18368             
18369             this.iframe.style.width = w + 'px';
18370         }
18371         if(typeof h == 'number'){
18372             
18373             this.iframe.style.height = h + 'px';
18374             if(this.doc){
18375                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
18376             }
18377         }
18378         
18379     },
18380
18381     /**
18382      * Toggles the editor between standard and source edit mode.
18383      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
18384      */
18385     toggleSourceEdit : function(sourceEditMode){
18386         
18387         this.sourceEditMode = sourceEditMode === true;
18388         
18389         if(this.sourceEditMode){
18390  
18391             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
18392             
18393         }else{
18394             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
18395             //this.iframe.className = '';
18396             this.deferFocus();
18397         }
18398         //this.setSize(this.owner.wrap.getSize());
18399         //this.fireEvent('editmodechange', this, this.sourceEditMode);
18400     },
18401
18402     
18403   
18404
18405     /**
18406      * Protected method that will not generally be called directly. If you need/want
18407      * custom HTML cleanup, this is the method you should override.
18408      * @param {String} html The HTML to be cleaned
18409      * return {String} The cleaned HTML
18410      */
18411     cleanHtml : function(html){
18412         html = String(html);
18413         if(html.length > 5){
18414             if(Roo.isSafari){ // strip safari nonsense
18415                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
18416             }
18417         }
18418         if(html == '&nbsp;'){
18419             html = '';
18420         }
18421         return html;
18422     },
18423
18424     /**
18425      * HTML Editor -> Textarea
18426      * Protected method that will not generally be called directly. Syncs the contents
18427      * of the editor iframe with the textarea.
18428      */
18429     syncValue : function(){
18430         if(this.initialized){
18431             var bd = (this.doc.body || this.doc.documentElement);
18432             //this.cleanUpPaste(); -- this is done else where and causes havoc..
18433             var html = bd.innerHTML;
18434             if(Roo.isSafari){
18435                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
18436                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
18437                 if(m && m[1]){
18438                     html = '<div style="'+m[0]+'">' + html + '</div>';
18439                 }
18440             }
18441             html = this.cleanHtml(html);
18442             // fix up the special chars.. normaly like back quotes in word...
18443             // however we do not want to do this with chinese..
18444             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
18445                 var cc = b.charCodeAt();
18446                 if (
18447                     (cc >= 0x4E00 && cc < 0xA000 ) ||
18448                     (cc >= 0x3400 && cc < 0x4E00 ) ||
18449                     (cc >= 0xf900 && cc < 0xfb00 )
18450                 ) {
18451                         return b;
18452                 }
18453                 return "&#"+cc+";" 
18454             });
18455             if(this.owner.fireEvent('beforesync', this, html) !== false){
18456                 this.el.dom.value = html;
18457                 this.owner.fireEvent('sync', this, html);
18458             }
18459         }
18460     },
18461
18462     /**
18463      * Protected method that will not generally be called directly. Pushes the value of the textarea
18464      * into the iframe editor.
18465      */
18466     pushValue : function(){
18467         if(this.initialized){
18468             var v = this.el.dom.value.trim();
18469             
18470 //            if(v.length < 1){
18471 //                v = '&#160;';
18472 //            }
18473             
18474             if(this.owner.fireEvent('beforepush', this, v) !== false){
18475                 var d = (this.doc.body || this.doc.documentElement);
18476                 d.innerHTML = v;
18477                 this.cleanUpPaste();
18478                 this.el.dom.value = d.innerHTML;
18479                 this.owner.fireEvent('push', this, v);
18480             }
18481         }
18482     },
18483
18484     // private
18485     deferFocus : function(){
18486         this.focus.defer(10, this);
18487     },
18488
18489     // doc'ed in Field
18490     focus : function(){
18491         if(this.win && !this.sourceEditMode){
18492             this.win.focus();
18493         }else{
18494             this.el.focus();
18495         }
18496     },
18497     
18498     assignDocWin: function()
18499     {
18500         var iframe = this.iframe;
18501         
18502          if(Roo.isIE){
18503             this.doc = iframe.contentWindow.document;
18504             this.win = iframe.contentWindow;
18505         } else {
18506 //            if (!Roo.get(this.frameId)) {
18507 //                return;
18508 //            }
18509 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
18510 //            this.win = Roo.get(this.frameId).dom.contentWindow;
18511             
18512             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
18513                 return;
18514             }
18515             
18516             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
18517             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
18518         }
18519     },
18520     
18521     // private
18522     initEditor : function(){
18523         //console.log("INIT EDITOR");
18524         this.assignDocWin();
18525         
18526         
18527         
18528         this.doc.designMode="on";
18529         this.doc.open();
18530         this.doc.write(this.getDocMarkup());
18531         this.doc.close();
18532         
18533         var dbody = (this.doc.body || this.doc.documentElement);
18534         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
18535         // this copies styles from the containing element into thsi one..
18536         // not sure why we need all of this..
18537         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
18538         
18539         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
18540         //ss['background-attachment'] = 'fixed'; // w3c
18541         dbody.bgProperties = 'fixed'; // ie
18542         //Roo.DomHelper.applyStyles(dbody, ss);
18543         Roo.EventManager.on(this.doc, {
18544             //'mousedown': this.onEditorEvent,
18545             'mouseup': this.onEditorEvent,
18546             'dblclick': this.onEditorEvent,
18547             'click': this.onEditorEvent,
18548             'keyup': this.onEditorEvent,
18549             buffer:100,
18550             scope: this
18551         });
18552         if(Roo.isGecko){
18553             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
18554         }
18555         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
18556             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
18557         }
18558         this.initialized = true;
18559
18560         this.owner.fireEvent('initialize', this);
18561         this.pushValue();
18562     },
18563
18564     // private
18565     onDestroy : function(){
18566         
18567         
18568         
18569         if(this.rendered){
18570             
18571             //for (var i =0; i < this.toolbars.length;i++) {
18572             //    // fixme - ask toolbars for heights?
18573             //    this.toolbars[i].onDestroy();
18574            // }
18575             
18576             //this.wrap.dom.innerHTML = '';
18577             //this.wrap.remove();
18578         }
18579     },
18580
18581     // private
18582     onFirstFocus : function(){
18583         
18584         this.assignDocWin();
18585         
18586         
18587         this.activated = true;
18588          
18589     
18590         if(Roo.isGecko){ // prevent silly gecko errors
18591             this.win.focus();
18592             var s = this.win.getSelection();
18593             if(!s.focusNode || s.focusNode.nodeType != 3){
18594                 var r = s.getRangeAt(0);
18595                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
18596                 r.collapse(true);
18597                 this.deferFocus();
18598             }
18599             try{
18600                 this.execCmd('useCSS', true);
18601                 this.execCmd('styleWithCSS', false);
18602             }catch(e){}
18603         }
18604         this.owner.fireEvent('activate', this);
18605     },
18606
18607     // private
18608     adjustFont: function(btn){
18609         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
18610         //if(Roo.isSafari){ // safari
18611         //    adjust *= 2;
18612        // }
18613         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
18614         if(Roo.isSafari){ // safari
18615             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
18616             v =  (v < 10) ? 10 : v;
18617             v =  (v > 48) ? 48 : v;
18618             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
18619             
18620         }
18621         
18622         
18623         v = Math.max(1, v+adjust);
18624         
18625         this.execCmd('FontSize', v  );
18626     },
18627
18628     onEditorEvent : function(e)
18629     {
18630         this.owner.fireEvent('editorevent', this, e);
18631       //  this.updateToolbar();
18632         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
18633     },
18634
18635     insertTag : function(tg)
18636     {
18637         // could be a bit smarter... -> wrap the current selected tRoo..
18638         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
18639             
18640             range = this.createRange(this.getSelection());
18641             var wrappingNode = this.doc.createElement(tg.toLowerCase());
18642             wrappingNode.appendChild(range.extractContents());
18643             range.insertNode(wrappingNode);
18644
18645             return;
18646             
18647             
18648             
18649         }
18650         this.execCmd("formatblock",   tg);
18651         
18652     },
18653     
18654     insertText : function(txt)
18655     {
18656         
18657         
18658         var range = this.createRange();
18659         range.deleteContents();
18660                //alert(Sender.getAttribute('label'));
18661                
18662         range.insertNode(this.doc.createTextNode(txt));
18663     } ,
18664     
18665      
18666
18667     /**
18668      * Executes a Midas editor command on the editor document and performs necessary focus and
18669      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
18670      * @param {String} cmd The Midas command
18671      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
18672      */
18673     relayCmd : function(cmd, value){
18674         this.win.focus();
18675         this.execCmd(cmd, value);
18676         this.owner.fireEvent('editorevent', this);
18677         //this.updateToolbar();
18678         this.owner.deferFocus();
18679     },
18680
18681     /**
18682      * Executes a Midas editor command directly on the editor document.
18683      * For visual commands, you should use {@link #relayCmd} instead.
18684      * <b>This should only be called after the editor is initialized.</b>
18685      * @param {String} cmd The Midas command
18686      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
18687      */
18688     execCmd : function(cmd, value){
18689         this.doc.execCommand(cmd, false, value === undefined ? null : value);
18690         this.syncValue();
18691     },
18692  
18693  
18694    
18695     /**
18696      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
18697      * to insert tRoo.
18698      * @param {String} text | dom node.. 
18699      */
18700     insertAtCursor : function(text)
18701     {
18702         
18703         
18704         
18705         if(!this.activated){
18706             return;
18707         }
18708         /*
18709         if(Roo.isIE){
18710             this.win.focus();
18711             var r = this.doc.selection.createRange();
18712             if(r){
18713                 r.collapse(true);
18714                 r.pasteHTML(text);
18715                 this.syncValue();
18716                 this.deferFocus();
18717             
18718             }
18719             return;
18720         }
18721         */
18722         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
18723             this.win.focus();
18724             
18725             
18726             // from jquery ui (MIT licenced)
18727             var range, node;
18728             var win = this.win;
18729             
18730             if (win.getSelection && win.getSelection().getRangeAt) {
18731                 range = win.getSelection().getRangeAt(0);
18732                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
18733                 range.insertNode(node);
18734             } else if (win.document.selection && win.document.selection.createRange) {
18735                 // no firefox support
18736                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
18737                 win.document.selection.createRange().pasteHTML(txt);
18738             } else {
18739                 // no firefox support
18740                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
18741                 this.execCmd('InsertHTML', txt);
18742             } 
18743             
18744             this.syncValue();
18745             
18746             this.deferFocus();
18747         }
18748     },
18749  // private
18750     mozKeyPress : function(e){
18751         if(e.ctrlKey){
18752             var c = e.getCharCode(), cmd;
18753           
18754             if(c > 0){
18755                 c = String.fromCharCode(c).toLowerCase();
18756                 switch(c){
18757                     case 'b':
18758                         cmd = 'bold';
18759                         break;
18760                     case 'i':
18761                         cmd = 'italic';
18762                         break;
18763                     
18764                     case 'u':
18765                         cmd = 'underline';
18766                         break;
18767                     
18768                     case 'v':
18769                         this.cleanUpPaste.defer(100, this);
18770                         return;
18771                         
18772                 }
18773                 if(cmd){
18774                     this.win.focus();
18775                     this.execCmd(cmd);
18776                     this.deferFocus();
18777                     e.preventDefault();
18778                 }
18779                 
18780             }
18781         }
18782     },
18783
18784     // private
18785     fixKeys : function(){ // load time branching for fastest keydown performance
18786         if(Roo.isIE){
18787             return function(e){
18788                 var k = e.getKey(), r;
18789                 if(k == e.TAB){
18790                     e.stopEvent();
18791                     r = this.doc.selection.createRange();
18792                     if(r){
18793                         r.collapse(true);
18794                         r.pasteHTML('&#160;&#160;&#160;&#160;');
18795                         this.deferFocus();
18796                     }
18797                     return;
18798                 }
18799                 
18800                 if(k == e.ENTER){
18801                     r = this.doc.selection.createRange();
18802                     if(r){
18803                         var target = r.parentElement();
18804                         if(!target || target.tagName.toLowerCase() != 'li'){
18805                             e.stopEvent();
18806                             r.pasteHTML('<br />');
18807                             r.collapse(false);
18808                             r.select();
18809                         }
18810                     }
18811                 }
18812                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18813                     this.cleanUpPaste.defer(100, this);
18814                     return;
18815                 }
18816                 
18817                 
18818             };
18819         }else if(Roo.isOpera){
18820             return function(e){
18821                 var k = e.getKey();
18822                 if(k == e.TAB){
18823                     e.stopEvent();
18824                     this.win.focus();
18825                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
18826                     this.deferFocus();
18827                 }
18828                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18829                     this.cleanUpPaste.defer(100, this);
18830                     return;
18831                 }
18832                 
18833             };
18834         }else if(Roo.isSafari){
18835             return function(e){
18836                 var k = e.getKey();
18837                 
18838                 if(k == e.TAB){
18839                     e.stopEvent();
18840                     this.execCmd('InsertText','\t');
18841                     this.deferFocus();
18842                     return;
18843                 }
18844                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18845                     this.cleanUpPaste.defer(100, this);
18846                     return;
18847                 }
18848                 
18849              };
18850         }
18851     }(),
18852     
18853     getAllAncestors: function()
18854     {
18855         var p = this.getSelectedNode();
18856         var a = [];
18857         if (!p) {
18858             a.push(p); // push blank onto stack..
18859             p = this.getParentElement();
18860         }
18861         
18862         
18863         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
18864             a.push(p);
18865             p = p.parentNode;
18866         }
18867         a.push(this.doc.body);
18868         return a;
18869     },
18870     lastSel : false,
18871     lastSelNode : false,
18872     
18873     
18874     getSelection : function() 
18875     {
18876         this.assignDocWin();
18877         return Roo.isIE ? this.doc.selection : this.win.getSelection();
18878     },
18879     
18880     getSelectedNode: function() 
18881     {
18882         // this may only work on Gecko!!!
18883         
18884         // should we cache this!!!!
18885         
18886         
18887         
18888          
18889         var range = this.createRange(this.getSelection()).cloneRange();
18890         
18891         if (Roo.isIE) {
18892             var parent = range.parentElement();
18893             while (true) {
18894                 var testRange = range.duplicate();
18895                 testRange.moveToElementText(parent);
18896                 if (testRange.inRange(range)) {
18897                     break;
18898                 }
18899                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
18900                     break;
18901                 }
18902                 parent = parent.parentElement;
18903             }
18904             return parent;
18905         }
18906         
18907         // is ancestor a text element.
18908         var ac =  range.commonAncestorContainer;
18909         if (ac.nodeType == 3) {
18910             ac = ac.parentNode;
18911         }
18912         
18913         var ar = ac.childNodes;
18914          
18915         var nodes = [];
18916         var other_nodes = [];
18917         var has_other_nodes = false;
18918         for (var i=0;i<ar.length;i++) {
18919             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
18920                 continue;
18921             }
18922             // fullly contained node.
18923             
18924             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
18925                 nodes.push(ar[i]);
18926                 continue;
18927             }
18928             
18929             // probably selected..
18930             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
18931                 other_nodes.push(ar[i]);
18932                 continue;
18933             }
18934             // outer..
18935             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
18936                 continue;
18937             }
18938             
18939             
18940             has_other_nodes = true;
18941         }
18942         if (!nodes.length && other_nodes.length) {
18943             nodes= other_nodes;
18944         }
18945         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
18946             return false;
18947         }
18948         
18949         return nodes[0];
18950     },
18951     createRange: function(sel)
18952     {
18953         // this has strange effects when using with 
18954         // top toolbar - not sure if it's a great idea.
18955         //this.editor.contentWindow.focus();
18956         if (typeof sel != "undefined") {
18957             try {
18958                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
18959             } catch(e) {
18960                 return this.doc.createRange();
18961             }
18962         } else {
18963             return this.doc.createRange();
18964         }
18965     },
18966     getParentElement: function()
18967     {
18968         
18969         this.assignDocWin();
18970         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
18971         
18972         var range = this.createRange(sel);
18973          
18974         try {
18975             var p = range.commonAncestorContainer;
18976             while (p.nodeType == 3) { // text node
18977                 p = p.parentNode;
18978             }
18979             return p;
18980         } catch (e) {
18981             return null;
18982         }
18983     
18984     },
18985     /***
18986      *
18987      * Range intersection.. the hard stuff...
18988      *  '-1' = before
18989      *  '0' = hits..
18990      *  '1' = after.
18991      *         [ -- selected range --- ]
18992      *   [fail]                        [fail]
18993      *
18994      *    basically..
18995      *      if end is before start or  hits it. fail.
18996      *      if start is after end or hits it fail.
18997      *
18998      *   if either hits (but other is outside. - then it's not 
18999      *   
19000      *    
19001      **/
19002     
19003     
19004     // @see http://www.thismuchiknow.co.uk/?p=64.
19005     rangeIntersectsNode : function(range, node)
19006     {
19007         var nodeRange = node.ownerDocument.createRange();
19008         try {
19009             nodeRange.selectNode(node);
19010         } catch (e) {
19011             nodeRange.selectNodeContents(node);
19012         }
19013     
19014         var rangeStartRange = range.cloneRange();
19015         rangeStartRange.collapse(true);
19016     
19017         var rangeEndRange = range.cloneRange();
19018         rangeEndRange.collapse(false);
19019     
19020         var nodeStartRange = nodeRange.cloneRange();
19021         nodeStartRange.collapse(true);
19022     
19023         var nodeEndRange = nodeRange.cloneRange();
19024         nodeEndRange.collapse(false);
19025     
19026         return rangeStartRange.compareBoundaryPoints(
19027                  Range.START_TO_START, nodeEndRange) == -1 &&
19028                rangeEndRange.compareBoundaryPoints(
19029                  Range.START_TO_START, nodeStartRange) == 1;
19030         
19031          
19032     },
19033     rangeCompareNode : function(range, node)
19034     {
19035         var nodeRange = node.ownerDocument.createRange();
19036         try {
19037             nodeRange.selectNode(node);
19038         } catch (e) {
19039             nodeRange.selectNodeContents(node);
19040         }
19041         
19042         
19043         range.collapse(true);
19044     
19045         nodeRange.collapse(true);
19046      
19047         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
19048         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
19049          
19050         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
19051         
19052         var nodeIsBefore   =  ss == 1;
19053         var nodeIsAfter    = ee == -1;
19054         
19055         if (nodeIsBefore && nodeIsAfter)
19056             return 0; // outer
19057         if (!nodeIsBefore && nodeIsAfter)
19058             return 1; //right trailed.
19059         
19060         if (nodeIsBefore && !nodeIsAfter)
19061             return 2;  // left trailed.
19062         // fully contined.
19063         return 3;
19064     },
19065
19066     // private? - in a new class?
19067     cleanUpPaste :  function()
19068     {
19069         // cleans up the whole document..
19070         Roo.log('cleanuppaste');
19071         
19072         this.cleanUpChildren(this.doc.body);
19073         var clean = this.cleanWordChars(this.doc.body.innerHTML);
19074         if (clean != this.doc.body.innerHTML) {
19075             this.doc.body.innerHTML = clean;
19076         }
19077         
19078     },
19079     
19080     cleanWordChars : function(input) {// change the chars to hex code
19081         var he = Roo.HtmlEditorCore;
19082         
19083         var output = input;
19084         Roo.each(he.swapCodes, function(sw) { 
19085             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
19086             
19087             output = output.replace(swapper, sw[1]);
19088         });
19089         
19090         return output;
19091     },
19092     
19093     
19094     cleanUpChildren : function (n)
19095     {
19096         if (!n.childNodes.length) {
19097             return;
19098         }
19099         for (var i = n.childNodes.length-1; i > -1 ; i--) {
19100            this.cleanUpChild(n.childNodes[i]);
19101         }
19102     },
19103     
19104     
19105         
19106     
19107     cleanUpChild : function (node)
19108     {
19109         var ed = this;
19110         //console.log(node);
19111         if (node.nodeName == "#text") {
19112             // clean up silly Windows -- stuff?
19113             return; 
19114         }
19115         if (node.nodeName == "#comment") {
19116             node.parentNode.removeChild(node);
19117             // clean up silly Windows -- stuff?
19118             return; 
19119         }
19120         var lcname = node.tagName.toLowerCase();
19121         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
19122         // whitelist of tags..
19123         
19124         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
19125             // remove node.
19126             node.parentNode.removeChild(node);
19127             return;
19128             
19129         }
19130         
19131         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
19132         
19133         // remove <a name=....> as rendering on yahoo mailer is borked with this.
19134         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
19135         
19136         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
19137         //    remove_keep_children = true;
19138         //}
19139         
19140         if (remove_keep_children) {
19141             this.cleanUpChildren(node);
19142             // inserts everything just before this node...
19143             while (node.childNodes.length) {
19144                 var cn = node.childNodes[0];
19145                 node.removeChild(cn);
19146                 node.parentNode.insertBefore(cn, node);
19147             }
19148             node.parentNode.removeChild(node);
19149             return;
19150         }
19151         
19152         if (!node.attributes || !node.attributes.length) {
19153             this.cleanUpChildren(node);
19154             return;
19155         }
19156         
19157         function cleanAttr(n,v)
19158         {
19159             
19160             if (v.match(/^\./) || v.match(/^\//)) {
19161                 return;
19162             }
19163             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
19164                 return;
19165             }
19166             if (v.match(/^#/)) {
19167                 return;
19168             }
19169 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
19170             node.removeAttribute(n);
19171             
19172         }
19173         
19174         var cwhite = this.cwhite;
19175         var cblack = this.cblack;
19176             
19177         function cleanStyle(n,v)
19178         {
19179             if (v.match(/expression/)) { //XSS?? should we even bother..
19180                 node.removeAttribute(n);
19181                 return;
19182             }
19183             
19184             var parts = v.split(/;/);
19185             var clean = [];
19186             
19187             Roo.each(parts, function(p) {
19188                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
19189                 if (!p.length) {
19190                     return true;
19191                 }
19192                 var l = p.split(':').shift().replace(/\s+/g,'');
19193                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
19194                 
19195                 if ( cwhite.length && cblack.indexOf(l) > -1) {
19196 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19197                     //node.removeAttribute(n);
19198                     return true;
19199                 }
19200                 //Roo.log()
19201                 // only allow 'c whitelisted system attributes'
19202                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
19203 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19204                     //node.removeAttribute(n);
19205                     return true;
19206                 }
19207                 
19208                 
19209                  
19210                 
19211                 clean.push(p);
19212                 return true;
19213             });
19214             if (clean.length) { 
19215                 node.setAttribute(n, clean.join(';'));
19216             } else {
19217                 node.removeAttribute(n);
19218             }
19219             
19220         }
19221         
19222         
19223         for (var i = node.attributes.length-1; i > -1 ; i--) {
19224             var a = node.attributes[i];
19225             //console.log(a);
19226             
19227             if (a.name.toLowerCase().substr(0,2)=='on')  {
19228                 node.removeAttribute(a.name);
19229                 continue;
19230             }
19231             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
19232                 node.removeAttribute(a.name);
19233                 continue;
19234             }
19235             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
19236                 cleanAttr(a.name,a.value); // fixme..
19237                 continue;
19238             }
19239             if (a.name == 'style') {
19240                 cleanStyle(a.name,a.value);
19241                 continue;
19242             }
19243             /// clean up MS crap..
19244             // tecnically this should be a list of valid class'es..
19245             
19246             
19247             if (a.name == 'class') {
19248                 if (a.value.match(/^Mso/)) {
19249                     node.className = '';
19250                 }
19251                 
19252                 if (a.value.match(/body/)) {
19253                     node.className = '';
19254                 }
19255                 continue;
19256             }
19257             
19258             // style cleanup!?
19259             // class cleanup?
19260             
19261         }
19262         
19263         
19264         this.cleanUpChildren(node);
19265         
19266         
19267     },
19268     
19269     /**
19270      * Clean up MS wordisms...
19271      */
19272     cleanWord : function(node)
19273     {
19274         
19275         
19276         if (!node) {
19277             this.cleanWord(this.doc.body);
19278             return;
19279         }
19280         if (node.nodeName == "#text") {
19281             // clean up silly Windows -- stuff?
19282             return; 
19283         }
19284         if (node.nodeName == "#comment") {
19285             node.parentNode.removeChild(node);
19286             // clean up silly Windows -- stuff?
19287             return; 
19288         }
19289         
19290         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
19291             node.parentNode.removeChild(node);
19292             return;
19293         }
19294         
19295         // remove - but keep children..
19296         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
19297             while (node.childNodes.length) {
19298                 var cn = node.childNodes[0];
19299                 node.removeChild(cn);
19300                 node.parentNode.insertBefore(cn, node);
19301             }
19302             node.parentNode.removeChild(node);
19303             this.iterateChildren(node, this.cleanWord);
19304             return;
19305         }
19306         // clean styles
19307         if (node.className.length) {
19308             
19309             var cn = node.className.split(/\W+/);
19310             var cna = [];
19311             Roo.each(cn, function(cls) {
19312                 if (cls.match(/Mso[a-zA-Z]+/)) {
19313                     return;
19314                 }
19315                 cna.push(cls);
19316             });
19317             node.className = cna.length ? cna.join(' ') : '';
19318             if (!cna.length) {
19319                 node.removeAttribute("class");
19320             }
19321         }
19322         
19323         if (node.hasAttribute("lang")) {
19324             node.removeAttribute("lang");
19325         }
19326         
19327         if (node.hasAttribute("style")) {
19328             
19329             var styles = node.getAttribute("style").split(";");
19330             var nstyle = [];
19331             Roo.each(styles, function(s) {
19332                 if (!s.match(/:/)) {
19333                     return;
19334                 }
19335                 var kv = s.split(":");
19336                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
19337                     return;
19338                 }
19339                 // what ever is left... we allow.
19340                 nstyle.push(s);
19341             });
19342             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
19343             if (!nstyle.length) {
19344                 node.removeAttribute('style');
19345             }
19346         }
19347         this.iterateChildren(node, this.cleanWord);
19348         
19349         
19350         
19351     },
19352     /**
19353      * iterateChildren of a Node, calling fn each time, using this as the scole..
19354      * @param {DomNode} node node to iterate children of.
19355      * @param {Function} fn method of this class to call on each item.
19356      */
19357     iterateChildren : function(node, fn)
19358     {
19359         if (!node.childNodes.length) {
19360                 return;
19361         }
19362         for (var i = node.childNodes.length-1; i > -1 ; i--) {
19363            fn.call(this, node.childNodes[i])
19364         }
19365     },
19366     
19367     
19368     /**
19369      * cleanTableWidths.
19370      *
19371      * Quite often pasting from word etc.. results in tables with column and widths.
19372      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
19373      *
19374      */
19375     cleanTableWidths : function(node)
19376     {
19377          
19378          
19379         if (!node) {
19380             this.cleanTableWidths(this.doc.body);
19381             return;
19382         }
19383         
19384         // ignore list...
19385         if (node.nodeName == "#text" || node.nodeName == "#comment") {
19386             return; 
19387         }
19388         Roo.log(node.tagName);
19389         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
19390             this.iterateChildren(node, this.cleanTableWidths);
19391             return;
19392         }
19393         if (node.hasAttribute('width')) {
19394             node.removeAttribute('width');
19395         }
19396         
19397          
19398         if (node.hasAttribute("style")) {
19399             // pretty basic...
19400             
19401             var styles = node.getAttribute("style").split(";");
19402             var nstyle = [];
19403             Roo.each(styles, function(s) {
19404                 if (!s.match(/:/)) {
19405                     return;
19406                 }
19407                 var kv = s.split(":");
19408                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
19409                     return;
19410                 }
19411                 // what ever is left... we allow.
19412                 nstyle.push(s);
19413             });
19414             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
19415             if (!nstyle.length) {
19416                 node.removeAttribute('style');
19417             }
19418         }
19419         
19420         this.iterateChildren(node, this.cleanTableWidths);
19421         
19422         
19423     },
19424     
19425     
19426     
19427     
19428     domToHTML : function(currentElement, depth, nopadtext) {
19429         
19430         depth = depth || 0;
19431         nopadtext = nopadtext || false;
19432     
19433         if (!currentElement) {
19434             return this.domToHTML(this.doc.body);
19435         }
19436         
19437         //Roo.log(currentElement);
19438         var j;
19439         var allText = false;
19440         var nodeName = currentElement.nodeName;
19441         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
19442         
19443         if  (nodeName == '#text') {
19444             
19445             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
19446         }
19447         
19448         
19449         var ret = '';
19450         if (nodeName != 'BODY') {
19451              
19452             var i = 0;
19453             // Prints the node tagName, such as <A>, <IMG>, etc
19454             if (tagName) {
19455                 var attr = [];
19456                 for(i = 0; i < currentElement.attributes.length;i++) {
19457                     // quoting?
19458                     var aname = currentElement.attributes.item(i).name;
19459                     if (!currentElement.attributes.item(i).value.length) {
19460                         continue;
19461                     }
19462                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
19463                 }
19464                 
19465                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
19466             } 
19467             else {
19468                 
19469                 // eack
19470             }
19471         } else {
19472             tagName = false;
19473         }
19474         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
19475             return ret;
19476         }
19477         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
19478             nopadtext = true;
19479         }
19480         
19481         
19482         // Traverse the tree
19483         i = 0;
19484         var currentElementChild = currentElement.childNodes.item(i);
19485         var allText = true;
19486         var innerHTML  = '';
19487         lastnode = '';
19488         while (currentElementChild) {
19489             // Formatting code (indent the tree so it looks nice on the screen)
19490             var nopad = nopadtext;
19491             if (lastnode == 'SPAN') {
19492                 nopad  = true;
19493             }
19494             // text
19495             if  (currentElementChild.nodeName == '#text') {
19496                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
19497                 toadd = nopadtext ? toadd : toadd.trim();
19498                 if (!nopad && toadd.length > 80) {
19499                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
19500                 }
19501                 innerHTML  += toadd;
19502                 
19503                 i++;
19504                 currentElementChild = currentElement.childNodes.item(i);
19505                 lastNode = '';
19506                 continue;
19507             }
19508             allText = false;
19509             
19510             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
19511                 
19512             // Recursively traverse the tree structure of the child node
19513             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
19514             lastnode = currentElementChild.nodeName;
19515             i++;
19516             currentElementChild=currentElement.childNodes.item(i);
19517         }
19518         
19519         ret += innerHTML;
19520         
19521         if (!allText) {
19522                 // The remaining code is mostly for formatting the tree
19523             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
19524         }
19525         
19526         
19527         if (tagName) {
19528             ret+= "</"+tagName+">";
19529         }
19530         return ret;
19531         
19532     },
19533         
19534     applyBlacklists : function()
19535     {
19536         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
19537         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
19538         
19539         this.white = [];
19540         this.black = [];
19541         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
19542             if (b.indexOf(tag) > -1) {
19543                 return;
19544             }
19545             this.white.push(tag);
19546             
19547         }, this);
19548         
19549         Roo.each(w, function(tag) {
19550             if (b.indexOf(tag) > -1) {
19551                 return;
19552             }
19553             if (this.white.indexOf(tag) > -1) {
19554                 return;
19555             }
19556             this.white.push(tag);
19557             
19558         }, this);
19559         
19560         
19561         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
19562             if (w.indexOf(tag) > -1) {
19563                 return;
19564             }
19565             this.black.push(tag);
19566             
19567         }, this);
19568         
19569         Roo.each(b, function(tag) {
19570             if (w.indexOf(tag) > -1) {
19571                 return;
19572             }
19573             if (this.black.indexOf(tag) > -1) {
19574                 return;
19575             }
19576             this.black.push(tag);
19577             
19578         }, this);
19579         
19580         
19581         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
19582         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
19583         
19584         this.cwhite = [];
19585         this.cblack = [];
19586         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
19587             if (b.indexOf(tag) > -1) {
19588                 return;
19589             }
19590             this.cwhite.push(tag);
19591             
19592         }, this);
19593         
19594         Roo.each(w, function(tag) {
19595             if (b.indexOf(tag) > -1) {
19596                 return;
19597             }
19598             if (this.cwhite.indexOf(tag) > -1) {
19599                 return;
19600             }
19601             this.cwhite.push(tag);
19602             
19603         }, this);
19604         
19605         
19606         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
19607             if (w.indexOf(tag) > -1) {
19608                 return;
19609             }
19610             this.cblack.push(tag);
19611             
19612         }, this);
19613         
19614         Roo.each(b, function(tag) {
19615             if (w.indexOf(tag) > -1) {
19616                 return;
19617             }
19618             if (this.cblack.indexOf(tag) > -1) {
19619                 return;
19620             }
19621             this.cblack.push(tag);
19622             
19623         }, this);
19624     },
19625     
19626     setStylesheets : function(stylesheets)
19627     {
19628         if(typeof(stylesheets) == 'string'){
19629             Roo.get(this.iframe.contentDocument.head).createChild({
19630                 tag : 'link',
19631                 rel : 'stylesheet',
19632                 type : 'text/css',
19633                 href : stylesheets
19634             });
19635             
19636             return;
19637         }
19638         var _this = this;
19639      
19640         Roo.each(stylesheets, function(s) {
19641             if(!s.length){
19642                 return;
19643             }
19644             
19645             Roo.get(_this.iframe.contentDocument.head).createChild({
19646                 tag : 'link',
19647                 rel : 'stylesheet',
19648                 type : 'text/css',
19649                 href : s
19650             });
19651         });
19652
19653         
19654     },
19655     
19656     removeStylesheets : function()
19657     {
19658         var _this = this;
19659         
19660         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
19661             s.remove();
19662         });
19663     }
19664     
19665     // hide stuff that is not compatible
19666     /**
19667      * @event blur
19668      * @hide
19669      */
19670     /**
19671      * @event change
19672      * @hide
19673      */
19674     /**
19675      * @event focus
19676      * @hide
19677      */
19678     /**
19679      * @event specialkey
19680      * @hide
19681      */
19682     /**
19683      * @cfg {String} fieldClass @hide
19684      */
19685     /**
19686      * @cfg {String} focusClass @hide
19687      */
19688     /**
19689      * @cfg {String} autoCreate @hide
19690      */
19691     /**
19692      * @cfg {String} inputType @hide
19693      */
19694     /**
19695      * @cfg {String} invalidClass @hide
19696      */
19697     /**
19698      * @cfg {String} invalidText @hide
19699      */
19700     /**
19701      * @cfg {String} msgFx @hide
19702      */
19703     /**
19704      * @cfg {String} validateOnBlur @hide
19705      */
19706 });
19707
19708 Roo.HtmlEditorCore.white = [
19709         'area', 'br', 'img', 'input', 'hr', 'wbr',
19710         
19711        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
19712        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
19713        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
19714        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
19715        'table',   'ul',         'xmp', 
19716        
19717        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
19718       'thead',   'tr', 
19719      
19720       'dir', 'menu', 'ol', 'ul', 'dl',
19721        
19722       'embed',  'object'
19723 ];
19724
19725
19726 Roo.HtmlEditorCore.black = [
19727     //    'embed',  'object', // enable - backend responsiblity to clean thiese
19728         'applet', // 
19729         'base',   'basefont', 'bgsound', 'blink',  'body', 
19730         'frame',  'frameset', 'head',    'html',   'ilayer', 
19731         'iframe', 'layer',  'link',     'meta',    'object',   
19732         'script', 'style' ,'title',  'xml' // clean later..
19733 ];
19734 Roo.HtmlEditorCore.clean = [
19735     'script', 'style', 'title', 'xml'
19736 ];
19737 Roo.HtmlEditorCore.remove = [
19738     'font'
19739 ];
19740 // attributes..
19741
19742 Roo.HtmlEditorCore.ablack = [
19743     'on'
19744 ];
19745     
19746 Roo.HtmlEditorCore.aclean = [ 
19747     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
19748 ];
19749
19750 // protocols..
19751 Roo.HtmlEditorCore.pwhite= [
19752         'http',  'https',  'mailto'
19753 ];
19754
19755 // white listed style attributes.
19756 Roo.HtmlEditorCore.cwhite= [
19757       //  'text-align', /// default is to allow most things..
19758       
19759          
19760 //        'font-size'//??
19761 ];
19762
19763 // black listed style attributes.
19764 Roo.HtmlEditorCore.cblack= [
19765       //  'font-size' -- this can be set by the project 
19766 ];
19767
19768
19769 Roo.HtmlEditorCore.swapCodes   =[ 
19770     [    8211, "--" ], 
19771     [    8212, "--" ], 
19772     [    8216,  "'" ],  
19773     [    8217, "'" ],  
19774     [    8220, '"' ],  
19775     [    8221, '"' ],  
19776     [    8226, "*" ],  
19777     [    8230, "..." ]
19778 ]; 
19779
19780     /*
19781  * - LGPL
19782  *
19783  * HtmlEditor
19784  * 
19785  */
19786
19787 /**
19788  * @class Roo.bootstrap.HtmlEditor
19789  * @extends Roo.bootstrap.TextArea
19790  * Bootstrap HtmlEditor class
19791
19792  * @constructor
19793  * Create a new HtmlEditor
19794  * @param {Object} config The config object
19795  */
19796
19797 Roo.bootstrap.HtmlEditor = function(config){
19798     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
19799     if (!this.toolbars) {
19800         this.toolbars = [];
19801     }
19802     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
19803     this.addEvents({
19804             /**
19805              * @event initialize
19806              * Fires when the editor is fully initialized (including the iframe)
19807              * @param {HtmlEditor} this
19808              */
19809             initialize: true,
19810             /**
19811              * @event activate
19812              * Fires when the editor is first receives the focus. Any insertion must wait
19813              * until after this event.
19814              * @param {HtmlEditor} this
19815              */
19816             activate: true,
19817              /**
19818              * @event beforesync
19819              * Fires before the textarea is updated with content from the editor iframe. Return false
19820              * to cancel the sync.
19821              * @param {HtmlEditor} this
19822              * @param {String} html
19823              */
19824             beforesync: true,
19825              /**
19826              * @event beforepush
19827              * Fires before the iframe editor is updated with content from the textarea. Return false
19828              * to cancel the push.
19829              * @param {HtmlEditor} this
19830              * @param {String} html
19831              */
19832             beforepush: true,
19833              /**
19834              * @event sync
19835              * Fires when the textarea is updated with content from the editor iframe.
19836              * @param {HtmlEditor} this
19837              * @param {String} html
19838              */
19839             sync: true,
19840              /**
19841              * @event push
19842              * Fires when the iframe editor is updated with content from the textarea.
19843              * @param {HtmlEditor} this
19844              * @param {String} html
19845              */
19846             push: true,
19847              /**
19848              * @event editmodechange
19849              * Fires when the editor switches edit modes
19850              * @param {HtmlEditor} this
19851              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
19852              */
19853             editmodechange: true,
19854             /**
19855              * @event editorevent
19856              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19857              * @param {HtmlEditor} this
19858              */
19859             editorevent: true,
19860             /**
19861              * @event firstfocus
19862              * Fires when on first focus - needed by toolbars..
19863              * @param {HtmlEditor} this
19864              */
19865             firstfocus: true,
19866             /**
19867              * @event autosave
19868              * Auto save the htmlEditor value as a file into Events
19869              * @param {HtmlEditor} this
19870              */
19871             autosave: true,
19872             /**
19873              * @event savedpreview
19874              * preview the saved version of htmlEditor
19875              * @param {HtmlEditor} this
19876              */
19877             savedpreview: true
19878         });
19879 };
19880
19881
19882 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
19883     
19884     
19885       /**
19886      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
19887      */
19888     toolbars : false,
19889    
19890      /**
19891      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
19892      *                        Roo.resizable.
19893      */
19894     resizable : false,
19895      /**
19896      * @cfg {Number} height (in pixels)
19897      */   
19898     height: 300,
19899    /**
19900      * @cfg {Number} width (in pixels)
19901      */   
19902     width: false,
19903     
19904     /**
19905      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19906      * 
19907      */
19908     stylesheets: false,
19909     
19910     // id of frame..
19911     frameId: false,
19912     
19913     // private properties
19914     validationEvent : false,
19915     deferHeight: true,
19916     initialized : false,
19917     activated : false,
19918     
19919     onFocus : Roo.emptyFn,
19920     iframePad:3,
19921     hideMode:'offsets',
19922     
19923     
19924     tbContainer : false,
19925     
19926     toolbarContainer :function() {
19927         return this.wrap.select('.x-html-editor-tb',true).first();
19928     },
19929
19930     /**
19931      * Protected method that will not generally be called directly. It
19932      * is called when the editor creates its toolbar. Override this method if you need to
19933      * add custom toolbar buttons.
19934      * @param {HtmlEditor} editor
19935      */
19936     createToolbar : function(){
19937         
19938         Roo.log("create toolbars");
19939         
19940         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
19941         this.toolbars[0].render(this.toolbarContainer());
19942         
19943         return;
19944         
19945 //        if (!editor.toolbars || !editor.toolbars.length) {
19946 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
19947 //        }
19948 //        
19949 //        for (var i =0 ; i < editor.toolbars.length;i++) {
19950 //            editor.toolbars[i] = Roo.factory(
19951 //                    typeof(editor.toolbars[i]) == 'string' ?
19952 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
19953 //                Roo.bootstrap.HtmlEditor);
19954 //            editor.toolbars[i].init(editor);
19955 //        }
19956     },
19957
19958      
19959     // private
19960     onRender : function(ct, position)
19961     {
19962        // Roo.log("Call onRender: " + this.xtype);
19963         var _t = this;
19964         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
19965       
19966         this.wrap = this.inputEl().wrap({
19967             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
19968         });
19969         
19970         this.editorcore.onRender(ct, position);
19971          
19972         if (this.resizable) {
19973             this.resizeEl = new Roo.Resizable(this.wrap, {
19974                 pinned : true,
19975                 wrap: true,
19976                 dynamic : true,
19977                 minHeight : this.height,
19978                 height: this.height,
19979                 handles : this.resizable,
19980                 width: this.width,
19981                 listeners : {
19982                     resize : function(r, w, h) {
19983                         _t.onResize(w,h); // -something
19984                     }
19985                 }
19986             });
19987             
19988         }
19989         this.createToolbar(this);
19990        
19991         
19992         if(!this.width && this.resizable){
19993             this.setSize(this.wrap.getSize());
19994         }
19995         if (this.resizeEl) {
19996             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
19997             // should trigger onReize..
19998         }
19999         
20000     },
20001
20002     // private
20003     onResize : function(w, h)
20004     {
20005         Roo.log('resize: ' +w + ',' + h );
20006         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
20007         var ew = false;
20008         var eh = false;
20009         
20010         if(this.inputEl() ){
20011             if(typeof w == 'number'){
20012                 var aw = w - this.wrap.getFrameWidth('lr');
20013                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
20014                 ew = aw;
20015             }
20016             if(typeof h == 'number'){
20017                  var tbh = -11;  // fixme it needs to tool bar size!
20018                 for (var i =0; i < this.toolbars.length;i++) {
20019                     // fixme - ask toolbars for heights?
20020                     tbh += this.toolbars[i].el.getHeight();
20021                     //if (this.toolbars[i].footer) {
20022                     //    tbh += this.toolbars[i].footer.el.getHeight();
20023                     //}
20024                 }
20025               
20026                 
20027                 
20028                 
20029                 
20030                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
20031                 ah -= 5; // knock a few pixes off for look..
20032                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
20033                 var eh = ah;
20034             }
20035         }
20036         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
20037         this.editorcore.onResize(ew,eh);
20038         
20039     },
20040
20041     /**
20042      * Toggles the editor between standard and source edit mode.
20043      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20044      */
20045     toggleSourceEdit : function(sourceEditMode)
20046     {
20047         this.editorcore.toggleSourceEdit(sourceEditMode);
20048         
20049         if(this.editorcore.sourceEditMode){
20050             Roo.log('editor - showing textarea');
20051             
20052 //            Roo.log('in');
20053 //            Roo.log(this.syncValue());
20054             this.syncValue();
20055             this.inputEl().removeClass(['hide', 'x-hidden']);
20056             this.inputEl().dom.removeAttribute('tabIndex');
20057             this.inputEl().focus();
20058         }else{
20059             Roo.log('editor - hiding textarea');
20060 //            Roo.log('out')
20061 //            Roo.log(this.pushValue()); 
20062             this.pushValue();
20063             
20064             this.inputEl().addClass(['hide', 'x-hidden']);
20065             this.inputEl().dom.setAttribute('tabIndex', -1);
20066             //this.deferFocus();
20067         }
20068          
20069         if(this.resizable){
20070             this.setSize(this.wrap.getSize());
20071         }
20072         
20073         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
20074     },
20075  
20076     // private (for BoxComponent)
20077     adjustSize : Roo.BoxComponent.prototype.adjustSize,
20078
20079     // private (for BoxComponent)
20080     getResizeEl : function(){
20081         return this.wrap;
20082     },
20083
20084     // private (for BoxComponent)
20085     getPositionEl : function(){
20086         return this.wrap;
20087     },
20088
20089     // private
20090     initEvents : function(){
20091         this.originalValue = this.getValue();
20092     },
20093
20094 //    /**
20095 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20096 //     * @method
20097 //     */
20098 //    markInvalid : Roo.emptyFn,
20099 //    /**
20100 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20101 //     * @method
20102 //     */
20103 //    clearInvalid : Roo.emptyFn,
20104
20105     setValue : function(v){
20106         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
20107         this.editorcore.pushValue();
20108     },
20109
20110      
20111     // private
20112     deferFocus : function(){
20113         this.focus.defer(10, this);
20114     },
20115
20116     // doc'ed in Field
20117     focus : function(){
20118         this.editorcore.focus();
20119         
20120     },
20121       
20122
20123     // private
20124     onDestroy : function(){
20125         
20126         
20127         
20128         if(this.rendered){
20129             
20130             for (var i =0; i < this.toolbars.length;i++) {
20131                 // fixme - ask toolbars for heights?
20132                 this.toolbars[i].onDestroy();
20133             }
20134             
20135             this.wrap.dom.innerHTML = '';
20136             this.wrap.remove();
20137         }
20138     },
20139
20140     // private
20141     onFirstFocus : function(){
20142         //Roo.log("onFirstFocus");
20143         this.editorcore.onFirstFocus();
20144          for (var i =0; i < this.toolbars.length;i++) {
20145             this.toolbars[i].onFirstFocus();
20146         }
20147         
20148     },
20149     
20150     // private
20151     syncValue : function()
20152     {   
20153         this.editorcore.syncValue();
20154     },
20155     
20156     pushValue : function()
20157     {   
20158         this.editorcore.pushValue();
20159     }
20160      
20161     
20162     // hide stuff that is not compatible
20163     /**
20164      * @event blur
20165      * @hide
20166      */
20167     /**
20168      * @event change
20169      * @hide
20170      */
20171     /**
20172      * @event focus
20173      * @hide
20174      */
20175     /**
20176      * @event specialkey
20177      * @hide
20178      */
20179     /**
20180      * @cfg {String} fieldClass @hide
20181      */
20182     /**
20183      * @cfg {String} focusClass @hide
20184      */
20185     /**
20186      * @cfg {String} autoCreate @hide
20187      */
20188     /**
20189      * @cfg {String} inputType @hide
20190      */
20191     /**
20192      * @cfg {String} invalidClass @hide
20193      */
20194     /**
20195      * @cfg {String} invalidText @hide
20196      */
20197     /**
20198      * @cfg {String} msgFx @hide
20199      */
20200     /**
20201      * @cfg {String} validateOnBlur @hide
20202      */
20203 });
20204  
20205     
20206    
20207    
20208    
20209       
20210 Roo.namespace('Roo.bootstrap.htmleditor');
20211 /**
20212  * @class Roo.bootstrap.HtmlEditorToolbar1
20213  * Basic Toolbar
20214  * 
20215  * Usage:
20216  *
20217  new Roo.bootstrap.HtmlEditor({
20218     ....
20219     toolbars : [
20220         new Roo.bootstrap.HtmlEditorToolbar1({
20221             disable : { fonts: 1 , format: 1, ..., ... , ...],
20222             btns : [ .... ]
20223         })
20224     }
20225      
20226  * 
20227  * @cfg {Object} disable List of elements to disable..
20228  * @cfg {Array} btns List of additional buttons.
20229  * 
20230  * 
20231  * NEEDS Extra CSS? 
20232  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
20233  */
20234  
20235 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
20236 {
20237     
20238     Roo.apply(this, config);
20239     
20240     // default disabled, based on 'good practice'..
20241     this.disable = this.disable || {};
20242     Roo.applyIf(this.disable, {
20243         fontSize : true,
20244         colors : true,
20245         specialElements : true
20246     });
20247     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
20248     
20249     this.editor = config.editor;
20250     this.editorcore = config.editor.editorcore;
20251     
20252     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
20253     
20254     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
20255     // dont call parent... till later.
20256 }
20257 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
20258      
20259     bar : true,
20260     
20261     editor : false,
20262     editorcore : false,
20263     
20264     
20265     formats : [
20266         "p" ,  
20267         "h1","h2","h3","h4","h5","h6", 
20268         "pre", "code", 
20269         "abbr", "acronym", "address", "cite", "samp", "var",
20270         'div','span'
20271     ],
20272     
20273     onRender : function(ct, position)
20274     {
20275        // Roo.log("Call onRender: " + this.xtype);
20276         
20277        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
20278        Roo.log(this.el);
20279        this.el.dom.style.marginBottom = '0';
20280        var _this = this;
20281        var editorcore = this.editorcore;
20282        var editor= this.editor;
20283        
20284        var children = [];
20285        var btn = function(id,cmd , toggle, handler){
20286        
20287             var  event = toggle ? 'toggle' : 'click';
20288        
20289             var a = {
20290                 size : 'sm',
20291                 xtype: 'Button',
20292                 xns: Roo.bootstrap,
20293                 glyphicon : id,
20294                 cmd : id || cmd,
20295                 enableToggle:toggle !== false,
20296                 //html : 'submit'
20297                 pressed : toggle ? false : null,
20298                 listeners : {}
20299             }
20300             a.listeners[toggle ? 'toggle' : 'click'] = function() {
20301                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
20302             }
20303             children.push(a);
20304             return a;
20305        }
20306         
20307         var style = {
20308                 xtype: 'Button',
20309                 size : 'sm',
20310                 xns: Roo.bootstrap,
20311                 glyphicon : 'font',
20312                 //html : 'submit'
20313                 menu : {
20314                     xtype: 'Menu',
20315                     xns: Roo.bootstrap,
20316                     items:  []
20317                 }
20318         };
20319         Roo.each(this.formats, function(f) {
20320             style.menu.items.push({
20321                 xtype :'MenuItem',
20322                 xns: Roo.bootstrap,
20323                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
20324                 tagname : f,
20325                 listeners : {
20326                     click : function()
20327                     {
20328                         editorcore.insertTag(this.tagname);
20329                         editor.focus();
20330                     }
20331                 }
20332                 
20333             });
20334         });
20335          children.push(style);   
20336             
20337             
20338         btn('bold',false,true);
20339         btn('italic',false,true);
20340         btn('align-left', 'justifyleft',true);
20341         btn('align-center', 'justifycenter',true);
20342         btn('align-right' , 'justifyright',true);
20343         btn('link', false, false, function(btn) {
20344             //Roo.log("create link?");
20345             var url = prompt(this.createLinkText, this.defaultLinkValue);
20346             if(url && url != 'http:/'+'/'){
20347                 this.editorcore.relayCmd('createlink', url);
20348             }
20349         }),
20350         btn('list','insertunorderedlist',true);
20351         btn('pencil', false,true, function(btn){
20352                 Roo.log(this);
20353                 
20354                 this.toggleSourceEdit(btn.pressed);
20355         });
20356         /*
20357         var cog = {
20358                 xtype: 'Button',
20359                 size : 'sm',
20360                 xns: Roo.bootstrap,
20361                 glyphicon : 'cog',
20362                 //html : 'submit'
20363                 menu : {
20364                     xtype: 'Menu',
20365                     xns: Roo.bootstrap,
20366                     items:  []
20367                 }
20368         };
20369         
20370         cog.menu.items.push({
20371             xtype :'MenuItem',
20372             xns: Roo.bootstrap,
20373             html : Clean styles,
20374             tagname : f,
20375             listeners : {
20376                 click : function()
20377                 {
20378                     editorcore.insertTag(this.tagname);
20379                     editor.focus();
20380                 }
20381             }
20382             
20383         });
20384        */
20385         
20386          
20387        this.xtype = 'NavSimplebar';
20388         
20389         for(var i=0;i< children.length;i++) {
20390             
20391             this.buttons.add(this.addxtypeChild(children[i]));
20392             
20393         }
20394         
20395         editor.on('editorevent', this.updateToolbar, this);
20396     },
20397     onBtnClick : function(id)
20398     {
20399        this.editorcore.relayCmd(id);
20400        this.editorcore.focus();
20401     },
20402     
20403     /**
20404      * Protected method that will not generally be called directly. It triggers
20405      * a toolbar update by reading the markup state of the current selection in the editor.
20406      */
20407     updateToolbar: function(){
20408
20409         if(!this.editorcore.activated){
20410             this.editor.onFirstFocus(); // is this neeed?
20411             return;
20412         }
20413
20414         var btns = this.buttons; 
20415         var doc = this.editorcore.doc;
20416         btns.get('bold').setActive(doc.queryCommandState('bold'));
20417         btns.get('italic').setActive(doc.queryCommandState('italic'));
20418         //btns.get('underline').setActive(doc.queryCommandState('underline'));
20419         
20420         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
20421         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
20422         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
20423         
20424         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
20425         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
20426          /*
20427         
20428         var ans = this.editorcore.getAllAncestors();
20429         if (this.formatCombo) {
20430             
20431             
20432             var store = this.formatCombo.store;
20433             this.formatCombo.setValue("");
20434             for (var i =0; i < ans.length;i++) {
20435                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
20436                     // select it..
20437                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
20438                     break;
20439                 }
20440             }
20441         }
20442         
20443         
20444         
20445         // hides menus... - so this cant be on a menu...
20446         Roo.bootstrap.MenuMgr.hideAll();
20447         */
20448         Roo.bootstrap.MenuMgr.hideAll();
20449         //this.editorsyncValue();
20450     },
20451     onFirstFocus: function() {
20452         this.buttons.each(function(item){
20453            item.enable();
20454         });
20455     },
20456     toggleSourceEdit : function(sourceEditMode){
20457         
20458           
20459         if(sourceEditMode){
20460             Roo.log("disabling buttons");
20461            this.buttons.each( function(item){
20462                 if(item.cmd != 'pencil'){
20463                     item.disable();
20464                 }
20465             });
20466           
20467         }else{
20468             Roo.log("enabling buttons");
20469             if(this.editorcore.initialized){
20470                 this.buttons.each( function(item){
20471                     item.enable();
20472                 });
20473             }
20474             
20475         }
20476         Roo.log("calling toggole on editor");
20477         // tell the editor that it's been pressed..
20478         this.editor.toggleSourceEdit(sourceEditMode);
20479        
20480     }
20481 });
20482
20483
20484
20485
20486
20487 /**
20488  * @class Roo.bootstrap.Table.AbstractSelectionModel
20489  * @extends Roo.util.Observable
20490  * Abstract base class for grid SelectionModels.  It provides the interface that should be
20491  * implemented by descendant classes.  This class should not be directly instantiated.
20492  * @constructor
20493  */
20494 Roo.bootstrap.Table.AbstractSelectionModel = function(){
20495     this.locked = false;
20496     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
20497 };
20498
20499
20500 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
20501     /** @ignore Called by the grid automatically. Do not call directly. */
20502     init : function(grid){
20503         this.grid = grid;
20504         this.initEvents();
20505     },
20506
20507     /**
20508      * Locks the selections.
20509      */
20510     lock : function(){
20511         this.locked = true;
20512     },
20513
20514     /**
20515      * Unlocks the selections.
20516      */
20517     unlock : function(){
20518         this.locked = false;
20519     },
20520
20521     /**
20522      * Returns true if the selections are locked.
20523      * @return {Boolean}
20524      */
20525     isLocked : function(){
20526         return this.locked;
20527     }
20528 });
20529 /**
20530  * @extends Roo.bootstrap.Table.AbstractSelectionModel
20531  * @class Roo.bootstrap.Table.RowSelectionModel
20532  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
20533  * It supports multiple selections and keyboard selection/navigation. 
20534  * @constructor
20535  * @param {Object} config
20536  */
20537
20538 Roo.bootstrap.Table.RowSelectionModel = function(config){
20539     Roo.apply(this, config);
20540     this.selections = new Roo.util.MixedCollection(false, function(o){
20541         return o.id;
20542     });
20543
20544     this.last = false;
20545     this.lastActive = false;
20546
20547     this.addEvents({
20548         /**
20549              * @event selectionchange
20550              * Fires when the selection changes
20551              * @param {SelectionModel} this
20552              */
20553             "selectionchange" : true,
20554         /**
20555              * @event afterselectionchange
20556              * Fires after the selection changes (eg. by key press or clicking)
20557              * @param {SelectionModel} this
20558              */
20559             "afterselectionchange" : true,
20560         /**
20561              * @event beforerowselect
20562              * Fires when a row is selected being selected, return false to cancel.
20563              * @param {SelectionModel} this
20564              * @param {Number} rowIndex The selected index
20565              * @param {Boolean} keepExisting False if other selections will be cleared
20566              */
20567             "beforerowselect" : true,
20568         /**
20569              * @event rowselect
20570              * Fires when a row is selected.
20571              * @param {SelectionModel} this
20572              * @param {Number} rowIndex The selected index
20573              * @param {Roo.data.Record} r The record
20574              */
20575             "rowselect" : true,
20576         /**
20577              * @event rowdeselect
20578              * Fires when a row is deselected.
20579              * @param {SelectionModel} this
20580              * @param {Number} rowIndex The selected index
20581              */
20582         "rowdeselect" : true
20583     });
20584     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
20585     this.locked = false;
20586 };
20587
20588 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
20589     /**
20590      * @cfg {Boolean} singleSelect
20591      * True to allow selection of only one row at a time (defaults to false)
20592      */
20593     singleSelect : false,
20594
20595     // private
20596     initEvents : function(){
20597
20598         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
20599             this.grid.on("mousedown", this.handleMouseDown, this);
20600         }else{ // allow click to work like normal
20601             this.grid.on("rowclick", this.handleDragableRowClick, this);
20602         }
20603
20604         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
20605             "up" : function(e){
20606                 if(!e.shiftKey){
20607                     this.selectPrevious(e.shiftKey);
20608                 }else if(this.last !== false && this.lastActive !== false){
20609                     var last = this.last;
20610                     this.selectRange(this.last,  this.lastActive-1);
20611                     this.grid.getView().focusRow(this.lastActive);
20612                     if(last !== false){
20613                         this.last = last;
20614                     }
20615                 }else{
20616                     this.selectFirstRow();
20617                 }
20618                 this.fireEvent("afterselectionchange", this);
20619             },
20620             "down" : function(e){
20621                 if(!e.shiftKey){
20622                     this.selectNext(e.shiftKey);
20623                 }else if(this.last !== false && this.lastActive !== false){
20624                     var last = this.last;
20625                     this.selectRange(this.last,  this.lastActive+1);
20626                     this.grid.getView().focusRow(this.lastActive);
20627                     if(last !== false){
20628                         this.last = last;
20629                     }
20630                 }else{
20631                     this.selectFirstRow();
20632                 }
20633                 this.fireEvent("afterselectionchange", this);
20634             },
20635             scope: this
20636         });
20637
20638         var view = this.grid.view;
20639         view.on("refresh", this.onRefresh, this);
20640         view.on("rowupdated", this.onRowUpdated, this);
20641         view.on("rowremoved", this.onRemove, this);
20642     },
20643
20644     // private
20645     onRefresh : function(){
20646         var ds = this.grid.dataSource, i, v = this.grid.view;
20647         var s = this.selections;
20648         s.each(function(r){
20649             if((i = ds.indexOfId(r.id)) != -1){
20650                 v.onRowSelect(i);
20651             }else{
20652                 s.remove(r);
20653             }
20654         });
20655     },
20656
20657     // private
20658     onRemove : function(v, index, r){
20659         this.selections.remove(r);
20660     },
20661
20662     // private
20663     onRowUpdated : function(v, index, r){
20664         if(this.isSelected(r)){
20665             v.onRowSelect(index);
20666         }
20667     },
20668
20669     /**
20670      * Select records.
20671      * @param {Array} records The records to select
20672      * @param {Boolean} keepExisting (optional) True to keep existing selections
20673      */
20674     selectRecords : function(records, keepExisting){
20675         if(!keepExisting){
20676             this.clearSelections();
20677         }
20678         var ds = this.grid.dataSource;
20679         for(var i = 0, len = records.length; i < len; i++){
20680             this.selectRow(ds.indexOf(records[i]), true);
20681         }
20682     },
20683
20684     /**
20685      * Gets the number of selected rows.
20686      * @return {Number}
20687      */
20688     getCount : function(){
20689         return this.selections.length;
20690     },
20691
20692     /**
20693      * Selects the first row in the grid.
20694      */
20695     selectFirstRow : function(){
20696         this.selectRow(0);
20697     },
20698
20699     /**
20700      * Select the last row.
20701      * @param {Boolean} keepExisting (optional) True to keep existing selections
20702      */
20703     selectLastRow : function(keepExisting){
20704         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
20705     },
20706
20707     /**
20708      * Selects the row immediately following the last selected row.
20709      * @param {Boolean} keepExisting (optional) True to keep existing selections
20710      */
20711     selectNext : function(keepExisting){
20712         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
20713             this.selectRow(this.last+1, keepExisting);
20714             this.grid.getView().focusRow(this.last);
20715         }
20716     },
20717
20718     /**
20719      * Selects the row that precedes the last selected row.
20720      * @param {Boolean} keepExisting (optional) True to keep existing selections
20721      */
20722     selectPrevious : function(keepExisting){
20723         if(this.last){
20724             this.selectRow(this.last-1, keepExisting);
20725             this.grid.getView().focusRow(this.last);
20726         }
20727     },
20728
20729     /**
20730      * Returns the selected records
20731      * @return {Array} Array of selected records
20732      */
20733     getSelections : function(){
20734         return [].concat(this.selections.items);
20735     },
20736
20737     /**
20738      * Returns the first selected record.
20739      * @return {Record}
20740      */
20741     getSelected : function(){
20742         return this.selections.itemAt(0);
20743     },
20744
20745
20746     /**
20747      * Clears all selections.
20748      */
20749     clearSelections : function(fast){
20750         if(this.locked) return;
20751         if(fast !== true){
20752             var ds = this.grid.dataSource;
20753             var s = this.selections;
20754             s.each(function(r){
20755                 this.deselectRow(ds.indexOfId(r.id));
20756             }, this);
20757             s.clear();
20758         }else{
20759             this.selections.clear();
20760         }
20761         this.last = false;
20762     },
20763
20764
20765     /**
20766      * Selects all rows.
20767      */
20768     selectAll : function(){
20769         if(this.locked) return;
20770         this.selections.clear();
20771         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
20772             this.selectRow(i, true);
20773         }
20774     },
20775
20776     /**
20777      * Returns True if there is a selection.
20778      * @return {Boolean}
20779      */
20780     hasSelection : function(){
20781         return this.selections.length > 0;
20782     },
20783
20784     /**
20785      * Returns True if the specified row is selected.
20786      * @param {Number/Record} record The record or index of the record to check
20787      * @return {Boolean}
20788      */
20789     isSelected : function(index){
20790         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
20791         return (r && this.selections.key(r.id) ? true : false);
20792     },
20793
20794     /**
20795      * Returns True if the specified record id is selected.
20796      * @param {String} id The id of record to check
20797      * @return {Boolean}
20798      */
20799     isIdSelected : function(id){
20800         return (this.selections.key(id) ? true : false);
20801     },
20802
20803     // private
20804     handleMouseDown : function(e, t){
20805         var view = this.grid.getView(), rowIndex;
20806         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
20807             return;
20808         };
20809         if(e.shiftKey && this.last !== false){
20810             var last = this.last;
20811             this.selectRange(last, rowIndex, e.ctrlKey);
20812             this.last = last; // reset the last
20813             view.focusRow(rowIndex);
20814         }else{
20815             var isSelected = this.isSelected(rowIndex);
20816             if(e.button !== 0 && isSelected){
20817                 view.focusRow(rowIndex);
20818             }else if(e.ctrlKey && isSelected){
20819                 this.deselectRow(rowIndex);
20820             }else if(!isSelected){
20821                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
20822                 view.focusRow(rowIndex);
20823             }
20824         }
20825         this.fireEvent("afterselectionchange", this);
20826     },
20827     // private
20828     handleDragableRowClick :  function(grid, rowIndex, e) 
20829     {
20830         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
20831             this.selectRow(rowIndex, false);
20832             grid.view.focusRow(rowIndex);
20833              this.fireEvent("afterselectionchange", this);
20834         }
20835     },
20836     
20837     /**
20838      * Selects multiple rows.
20839      * @param {Array} rows Array of the indexes of the row to select
20840      * @param {Boolean} keepExisting (optional) True to keep existing selections
20841      */
20842     selectRows : function(rows, keepExisting){
20843         if(!keepExisting){
20844             this.clearSelections();
20845         }
20846         for(var i = 0, len = rows.length; i < len; i++){
20847             this.selectRow(rows[i], true);
20848         }
20849     },
20850
20851     /**
20852      * Selects a range of rows. All rows in between startRow and endRow are also selected.
20853      * @param {Number} startRow The index of the first row in the range
20854      * @param {Number} endRow The index of the last row in the range
20855      * @param {Boolean} keepExisting (optional) True to retain existing selections
20856      */
20857     selectRange : function(startRow, endRow, keepExisting){
20858         if(this.locked) return;
20859         if(!keepExisting){
20860             this.clearSelections();
20861         }
20862         if(startRow <= endRow){
20863             for(var i = startRow; i <= endRow; i++){
20864                 this.selectRow(i, true);
20865             }
20866         }else{
20867             for(var i = startRow; i >= endRow; i--){
20868                 this.selectRow(i, true);
20869             }
20870         }
20871     },
20872
20873     /**
20874      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
20875      * @param {Number} startRow The index of the first row in the range
20876      * @param {Number} endRow The index of the last row in the range
20877      */
20878     deselectRange : function(startRow, endRow, preventViewNotify){
20879         if(this.locked) return;
20880         for(var i = startRow; i <= endRow; i++){
20881             this.deselectRow(i, preventViewNotify);
20882         }
20883     },
20884
20885     /**
20886      * Selects a row.
20887      * @param {Number} row The index of the row to select
20888      * @param {Boolean} keepExisting (optional) True to keep existing selections
20889      */
20890     selectRow : function(index, keepExisting, preventViewNotify){
20891         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
20892         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
20893             if(!keepExisting || this.singleSelect){
20894                 this.clearSelections();
20895             }
20896             var r = this.grid.dataSource.getAt(index);
20897             this.selections.add(r);
20898             this.last = this.lastActive = index;
20899             if(!preventViewNotify){
20900                 this.grid.getView().onRowSelect(index);
20901             }
20902             this.fireEvent("rowselect", this, index, r);
20903             this.fireEvent("selectionchange", this);
20904         }
20905     },
20906
20907     /**
20908      * Deselects a row.
20909      * @param {Number} row The index of the row to deselect
20910      */
20911     deselectRow : function(index, preventViewNotify){
20912         if(this.locked) return;
20913         if(this.last == index){
20914             this.last = false;
20915         }
20916         if(this.lastActive == index){
20917             this.lastActive = false;
20918         }
20919         var r = this.grid.dataSource.getAt(index);
20920         this.selections.remove(r);
20921         if(!preventViewNotify){
20922             this.grid.getView().onRowDeselect(index);
20923         }
20924         this.fireEvent("rowdeselect", this, index);
20925         this.fireEvent("selectionchange", this);
20926     },
20927
20928     // private
20929     restoreLast : function(){
20930         if(this._last){
20931             this.last = this._last;
20932         }
20933     },
20934
20935     // private
20936     acceptsNav : function(row, col, cm){
20937         return !cm.isHidden(col) && cm.isCellEditable(col, row);
20938     },
20939
20940     // private
20941     onEditorKey : function(field, e){
20942         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
20943         if(k == e.TAB){
20944             e.stopEvent();
20945             ed.completeEdit();
20946             if(e.shiftKey){
20947                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
20948             }else{
20949                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
20950             }
20951         }else if(k == e.ENTER && !e.ctrlKey){
20952             e.stopEvent();
20953             ed.completeEdit();
20954             if(e.shiftKey){
20955                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
20956             }else{
20957                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
20958             }
20959         }else if(k == e.ESC){
20960             ed.cancelEdit();
20961         }
20962         if(newCell){
20963             g.startEditing(newCell[0], newCell[1]);
20964         }
20965     }
20966 });/*
20967  * Based on:
20968  * Ext JS Library 1.1.1
20969  * Copyright(c) 2006-2007, Ext JS, LLC.
20970  *
20971  * Originally Released Under LGPL - original licence link has changed is not relivant.
20972  *
20973  * Fork - LGPL
20974  * <script type="text/javascript">
20975  */
20976  
20977 /**
20978  * @class Roo.bootstrap.PagingToolbar
20979  * @extends Roo.Row
20980  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
20981  * @constructor
20982  * Create a new PagingToolbar
20983  * @param {Object} config The config object
20984  */
20985 Roo.bootstrap.PagingToolbar = function(config)
20986 {
20987     // old args format still supported... - xtype is prefered..
20988         // created from xtype...
20989     var ds = config.dataSource;
20990     this.toolbarItems = [];
20991     if (config.items) {
20992         this.toolbarItems = config.items;
20993 //        config.items = [];
20994     }
20995     
20996     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
20997     this.ds = ds;
20998     this.cursor = 0;
20999     if (ds) { 
21000         this.bind(ds);
21001     }
21002     
21003     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
21004     
21005 };
21006
21007 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
21008     /**
21009      * @cfg {Roo.data.Store} dataSource
21010      * The underlying data store providing the paged data
21011      */
21012     /**
21013      * @cfg {String/HTMLElement/Element} container
21014      * container The id or element that will contain the toolbar
21015      */
21016     /**
21017      * @cfg {Boolean} displayInfo
21018      * True to display the displayMsg (defaults to false)
21019      */
21020     /**
21021      * @cfg {Number} pageSize
21022      * The number of records to display per page (defaults to 20)
21023      */
21024     pageSize: 20,
21025     /**
21026      * @cfg {String} displayMsg
21027      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
21028      */
21029     displayMsg : 'Displaying {0} - {1} of {2}',
21030     /**
21031      * @cfg {String} emptyMsg
21032      * The message to display when no records are found (defaults to "No data to display")
21033      */
21034     emptyMsg : 'No data to display',
21035     /**
21036      * Customizable piece of the default paging text (defaults to "Page")
21037      * @type String
21038      */
21039     beforePageText : "Page",
21040     /**
21041      * Customizable piece of the default paging text (defaults to "of %0")
21042      * @type String
21043      */
21044     afterPageText : "of {0}",
21045     /**
21046      * Customizable piece of the default paging text (defaults to "First Page")
21047      * @type String
21048      */
21049     firstText : "First Page",
21050     /**
21051      * Customizable piece of the default paging text (defaults to "Previous Page")
21052      * @type String
21053      */
21054     prevText : "Previous Page",
21055     /**
21056      * Customizable piece of the default paging text (defaults to "Next Page")
21057      * @type String
21058      */
21059     nextText : "Next Page",
21060     /**
21061      * Customizable piece of the default paging text (defaults to "Last Page")
21062      * @type String
21063      */
21064     lastText : "Last Page",
21065     /**
21066      * Customizable piece of the default paging text (defaults to "Refresh")
21067      * @type String
21068      */
21069     refreshText : "Refresh",
21070
21071     buttons : false,
21072     // private
21073     onRender : function(ct, position) 
21074     {
21075         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
21076         this.navgroup.parentId = this.id;
21077         this.navgroup.onRender(this.el, null);
21078         // add the buttons to the navgroup
21079         
21080         if(this.displayInfo){
21081             Roo.log(this.el.select('ul.navbar-nav',true).first());
21082             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
21083             this.displayEl = this.el.select('.x-paging-info', true).first();
21084 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
21085 //            this.displayEl = navel.el.select('span',true).first();
21086         }
21087         
21088         var _this = this;
21089         
21090         if(this.buttons){
21091             Roo.each(_this.buttons, function(e){
21092                Roo.factory(e).onRender(_this.el, null);
21093             });
21094         }
21095             
21096         Roo.each(_this.toolbarItems, function(e) {
21097             _this.navgroup.addItem(e);
21098         });
21099         
21100         
21101         this.first = this.navgroup.addItem({
21102             tooltip: this.firstText,
21103             cls: "prev",
21104             icon : 'fa fa-backward',
21105             disabled: true,
21106             preventDefault: true,
21107             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
21108         });
21109         
21110         this.prev =  this.navgroup.addItem({
21111             tooltip: this.prevText,
21112             cls: "prev",
21113             icon : 'fa fa-step-backward',
21114             disabled: true,
21115             preventDefault: true,
21116             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
21117         });
21118     //this.addSeparator();
21119         
21120         
21121         var field = this.navgroup.addItem( {
21122             tagtype : 'span',
21123             cls : 'x-paging-position',
21124             
21125             html : this.beforePageText  +
21126                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
21127                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
21128          } ); //?? escaped?
21129         
21130         this.field = field.el.select('input', true).first();
21131         this.field.on("keydown", this.onPagingKeydown, this);
21132         this.field.on("focus", function(){this.dom.select();});
21133     
21134     
21135         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
21136         //this.field.setHeight(18);
21137         //this.addSeparator();
21138         this.next = this.navgroup.addItem({
21139             tooltip: this.nextText,
21140             cls: "next",
21141             html : ' <i class="fa fa-step-forward">',
21142             disabled: true,
21143             preventDefault: true,
21144             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
21145         });
21146         this.last = this.navgroup.addItem({
21147             tooltip: this.lastText,
21148             icon : 'fa fa-forward',
21149             cls: "next",
21150             disabled: true,
21151             preventDefault: true,
21152             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
21153         });
21154     //this.addSeparator();
21155         this.loading = this.navgroup.addItem({
21156             tooltip: this.refreshText,
21157             icon: 'fa fa-refresh',
21158             preventDefault: true,
21159             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
21160         });
21161
21162     },
21163
21164     // private
21165     updateInfo : function(){
21166         if(this.displayEl){
21167             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
21168             var msg = count == 0 ?
21169                 this.emptyMsg :
21170                 String.format(
21171                     this.displayMsg,
21172                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
21173                 );
21174             this.displayEl.update(msg);
21175         }
21176     },
21177
21178     // private
21179     onLoad : function(ds, r, o){
21180        this.cursor = o.params ? o.params.start : 0;
21181        var d = this.getPageData(),
21182             ap = d.activePage,
21183             ps = d.pages;
21184         
21185        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
21186        this.field.dom.value = ap;
21187        this.first.setDisabled(ap == 1);
21188        this.prev.setDisabled(ap == 1);
21189        this.next.setDisabled(ap == ps);
21190        this.last.setDisabled(ap == ps);
21191        this.loading.enable();
21192        this.updateInfo();
21193     },
21194
21195     // private
21196     getPageData : function(){
21197         var total = this.ds.getTotalCount();
21198         return {
21199             total : total,
21200             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
21201             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
21202         };
21203     },
21204
21205     // private
21206     onLoadError : function(){
21207         this.loading.enable();
21208     },
21209
21210     // private
21211     onPagingKeydown : function(e){
21212         var k = e.getKey();
21213         var d = this.getPageData();
21214         if(k == e.RETURN){
21215             var v = this.field.dom.value, pageNum;
21216             if(!v || isNaN(pageNum = parseInt(v, 10))){
21217                 this.field.dom.value = d.activePage;
21218                 return;
21219             }
21220             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
21221             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
21222             e.stopEvent();
21223         }
21224         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))
21225         {
21226           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
21227           this.field.dom.value = pageNum;
21228           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
21229           e.stopEvent();
21230         }
21231         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
21232         {
21233           var v = this.field.dom.value, pageNum; 
21234           var increment = (e.shiftKey) ? 10 : 1;
21235           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
21236             increment *= -1;
21237           if(!v || isNaN(pageNum = parseInt(v, 10))) {
21238             this.field.dom.value = d.activePage;
21239             return;
21240           }
21241           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
21242           {
21243             this.field.dom.value = parseInt(v, 10) + increment;
21244             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
21245             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
21246           }
21247           e.stopEvent();
21248         }
21249     },
21250
21251     // private
21252     beforeLoad : function(){
21253         if(this.loading){
21254             this.loading.disable();
21255         }
21256     },
21257
21258     // private
21259     onClick : function(which){
21260         
21261         var ds = this.ds;
21262         if (!ds) {
21263             return;
21264         }
21265         
21266         switch(which){
21267             case "first":
21268                 ds.load({params:{start: 0, limit: this.pageSize}});
21269             break;
21270             case "prev":
21271                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
21272             break;
21273             case "next":
21274                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
21275             break;
21276             case "last":
21277                 var total = ds.getTotalCount();
21278                 var extra = total % this.pageSize;
21279                 var lastStart = extra ? (total - extra) : total-this.pageSize;
21280                 ds.load({params:{start: lastStart, limit: this.pageSize}});
21281             break;
21282             case "refresh":
21283                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
21284             break;
21285         }
21286     },
21287
21288     /**
21289      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
21290      * @param {Roo.data.Store} store The data store to unbind
21291      */
21292     unbind : function(ds){
21293         ds.un("beforeload", this.beforeLoad, this);
21294         ds.un("load", this.onLoad, this);
21295         ds.un("loadexception", this.onLoadError, this);
21296         ds.un("remove", this.updateInfo, this);
21297         ds.un("add", this.updateInfo, this);
21298         this.ds = undefined;
21299     },
21300
21301     /**
21302      * Binds the paging toolbar to the specified {@link Roo.data.Store}
21303      * @param {Roo.data.Store} store The data store to bind
21304      */
21305     bind : function(ds){
21306         ds.on("beforeload", this.beforeLoad, this);
21307         ds.on("load", this.onLoad, this);
21308         ds.on("loadexception", this.onLoadError, this);
21309         ds.on("remove", this.updateInfo, this);
21310         ds.on("add", this.updateInfo, this);
21311         this.ds = ds;
21312     }
21313 });/*
21314  * - LGPL
21315  *
21316  * element
21317  * 
21318  */
21319
21320 /**
21321  * @class Roo.bootstrap.MessageBar
21322  * @extends Roo.bootstrap.Component
21323  * Bootstrap MessageBar class
21324  * @cfg {String} html contents of the MessageBar
21325  * @cfg {String} weight (info | success | warning | danger) default info
21326  * @cfg {String} beforeClass insert the bar before the given class
21327  * @cfg {Boolean} closable (true | false) default false
21328  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
21329  * 
21330  * @constructor
21331  * Create a new Element
21332  * @param {Object} config The config object
21333  */
21334
21335 Roo.bootstrap.MessageBar = function(config){
21336     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
21337 };
21338
21339 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
21340     
21341     html: '',
21342     weight: 'info',
21343     closable: false,
21344     fixed: false,
21345     beforeClass: 'bootstrap-sticky-wrap',
21346     
21347     getAutoCreate : function(){
21348         
21349         var cfg = {
21350             tag: 'div',
21351             cls: 'alert alert-dismissable alert-' + this.weight,
21352             cn: [
21353                 {
21354                     tag: 'span',
21355                     cls: 'message',
21356                     html: this.html || ''
21357                 }
21358             ]
21359         }
21360         
21361         if(this.fixed){
21362             cfg.cls += ' alert-messages-fixed';
21363         }
21364         
21365         if(this.closable){
21366             cfg.cn.push({
21367                 tag: 'button',
21368                 cls: 'close',
21369                 html: 'x'
21370             });
21371         }
21372         
21373         return cfg;
21374     },
21375     
21376     onRender : function(ct, position)
21377     {
21378         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21379         
21380         if(!this.el){
21381             var cfg = Roo.apply({},  this.getAutoCreate());
21382             cfg.id = Roo.id();
21383             
21384             if (this.cls) {
21385                 cfg.cls += ' ' + this.cls;
21386             }
21387             if (this.style) {
21388                 cfg.style = this.style;
21389             }
21390             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
21391             
21392             this.el.setVisibilityMode(Roo.Element.DISPLAY);
21393         }
21394         
21395         this.el.select('>button.close').on('click', this.hide, this);
21396         
21397     },
21398     
21399     show : function()
21400     {
21401         if (!this.rendered) {
21402             this.render();
21403         }
21404         
21405         this.el.show();
21406         
21407         this.fireEvent('show', this);
21408         
21409     },
21410     
21411     hide : function()
21412     {
21413         if (!this.rendered) {
21414             this.render();
21415         }
21416         
21417         this.el.hide();
21418         
21419         this.fireEvent('hide', this);
21420     },
21421     
21422     update : function()
21423     {
21424 //        var e = this.el.dom.firstChild;
21425 //        
21426 //        if(this.closable){
21427 //            e = e.nextSibling;
21428 //        }
21429 //        
21430 //        e.data = this.html || '';
21431
21432         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
21433     }
21434    
21435 });
21436
21437  
21438
21439      /*
21440  * - LGPL
21441  *
21442  * Graph
21443  * 
21444  */
21445
21446
21447 /**
21448  * @class Roo.bootstrap.Graph
21449  * @extends Roo.bootstrap.Component
21450  * Bootstrap Graph class
21451 > Prameters
21452  -sm {number} sm 4
21453  -md {number} md 5
21454  @cfg {String} graphtype  bar | vbar | pie
21455  @cfg {number} g_x coodinator | centre x (pie)
21456  @cfg {number} g_y coodinator | centre y (pie)
21457  @cfg {number} g_r radius (pie)
21458  @cfg {number} g_height height of the chart (respected by all elements in the set)
21459  @cfg {number} g_width width of the chart (respected by all elements in the set)
21460  @cfg {Object} title The title of the chart
21461     
21462  -{Array}  values
21463  -opts (object) options for the chart 
21464      o {
21465      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
21466      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
21467      o vgutter (number)
21468      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.
21469      o stacked (boolean) whether or not to tread values as in a stacked bar chart
21470      o to
21471      o stretch (boolean)
21472      o }
21473  -opts (object) options for the pie
21474      o{
21475      o cut
21476      o startAngle (number)
21477      o endAngle (number)
21478      } 
21479  *
21480  * @constructor
21481  * Create a new Input
21482  * @param {Object} config The config object
21483  */
21484
21485 Roo.bootstrap.Graph = function(config){
21486     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
21487     
21488     this.addEvents({
21489         // img events
21490         /**
21491          * @event click
21492          * The img click event for the img.
21493          * @param {Roo.EventObject} e
21494          */
21495         "click" : true
21496     });
21497 };
21498
21499 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
21500     
21501     sm: 4,
21502     md: 5,
21503     graphtype: 'bar',
21504     g_height: 250,
21505     g_width: 400,
21506     g_x: 50,
21507     g_y: 50,
21508     g_r: 30,
21509     opts:{
21510         //g_colors: this.colors,
21511         g_type: 'soft',
21512         g_gutter: '20%'
21513
21514     },
21515     title : false,
21516
21517     getAutoCreate : function(){
21518         
21519         var cfg = {
21520             tag: 'div',
21521             html : null
21522         }
21523         
21524         
21525         return  cfg;
21526     },
21527
21528     onRender : function(ct,position){
21529         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
21530         this.raphael = Raphael(this.el.dom);
21531         
21532                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
21533                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
21534                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
21535                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
21536                 /*
21537                 r.text(160, 10, "Single Series Chart").attr(txtattr);
21538                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
21539                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
21540                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
21541                 
21542                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
21543                 r.barchart(330, 10, 300, 220, data1);
21544                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
21545                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
21546                 */
21547                 
21548                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
21549                 // r.barchart(30, 30, 560, 250,  xdata, {
21550                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
21551                 //     axis : "0 0 1 1",
21552                 //     axisxlabels :  xdata
21553                 //     //yvalues : cols,
21554                    
21555                 // });
21556 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
21557 //        
21558 //        this.load(null,xdata,{
21559 //                axis : "0 0 1 1",
21560 //                axisxlabels :  xdata
21561 //                });
21562
21563     },
21564
21565     load : function(graphtype,xdata,opts){
21566         this.raphael.clear();
21567         if(!graphtype) {
21568             graphtype = this.graphtype;
21569         }
21570         if(!opts){
21571             opts = this.opts;
21572         }
21573         var r = this.raphael,
21574             fin = function () {
21575                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
21576             },
21577             fout = function () {
21578                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
21579             },
21580             pfin = function() {
21581                 this.sector.stop();
21582                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
21583
21584                 if (this.label) {
21585                     this.label[0].stop();
21586                     this.label[0].attr({ r: 7.5 });
21587                     this.label[1].attr({ "font-weight": 800 });
21588                 }
21589             },
21590             pfout = function() {
21591                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
21592
21593                 if (this.label) {
21594                     this.label[0].animate({ r: 5 }, 500, "bounce");
21595                     this.label[1].attr({ "font-weight": 400 });
21596                 }
21597             };
21598
21599         switch(graphtype){
21600             case 'bar':
21601                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
21602                 break;
21603             case 'hbar':
21604                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
21605                 break;
21606             case 'pie':
21607 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
21608 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
21609 //            
21610                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
21611                 
21612                 break;
21613
21614         }
21615         
21616         if(this.title){
21617             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
21618         }
21619         
21620     },
21621     
21622     setTitle: function(o)
21623     {
21624         this.title = o;
21625     },
21626     
21627     initEvents: function() {
21628         
21629         if(!this.href){
21630             this.el.on('click', this.onClick, this);
21631         }
21632     },
21633     
21634     onClick : function(e)
21635     {
21636         Roo.log('img onclick');
21637         this.fireEvent('click', this, e);
21638     }
21639    
21640 });
21641
21642  
21643 /*
21644  * - LGPL
21645  *
21646  * numberBox
21647  * 
21648  */
21649 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21650
21651 /**
21652  * @class Roo.bootstrap.dash.NumberBox
21653  * @extends Roo.bootstrap.Component
21654  * Bootstrap NumberBox class
21655  * @cfg {String} headline Box headline
21656  * @cfg {String} content Box content
21657  * @cfg {String} icon Box icon
21658  * @cfg {String} footer Footer text
21659  * @cfg {String} fhref Footer href
21660  * 
21661  * @constructor
21662  * Create a new NumberBox
21663  * @param {Object} config The config object
21664  */
21665
21666
21667 Roo.bootstrap.dash.NumberBox = function(config){
21668     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
21669     
21670 };
21671
21672 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
21673     
21674     headline : '',
21675     content : '',
21676     icon : '',
21677     footer : '',
21678     fhref : '',
21679     ficon : '',
21680     
21681     getAutoCreate : function(){
21682         
21683         var cfg = {
21684             tag : 'div',
21685             cls : 'small-box ',
21686             cn : [
21687                 {
21688                     tag : 'div',
21689                     cls : 'inner',
21690                     cn :[
21691                         {
21692                             tag : 'h3',
21693                             cls : 'roo-headline',
21694                             html : this.headline
21695                         },
21696                         {
21697                             tag : 'p',
21698                             cls : 'roo-content',
21699                             html : this.content
21700                         }
21701                     ]
21702                 }
21703             ]
21704         }
21705         
21706         if(this.icon){
21707             cfg.cn.push({
21708                 tag : 'div',
21709                 cls : 'icon',
21710                 cn :[
21711                     {
21712                         tag : 'i',
21713                         cls : 'ion ' + this.icon
21714                     }
21715                 ]
21716             });
21717         }
21718         
21719         if(this.footer){
21720             var footer = {
21721                 tag : 'a',
21722                 cls : 'small-box-footer',
21723                 href : this.fhref || '#',
21724                 html : this.footer
21725             };
21726             
21727             cfg.cn.push(footer);
21728             
21729         }
21730         
21731         return  cfg;
21732     },
21733
21734     onRender : function(ct,position){
21735         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
21736
21737
21738        
21739                 
21740     },
21741
21742     setHeadline: function (value)
21743     {
21744         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
21745     },
21746     
21747     setFooter: function (value, href)
21748     {
21749         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
21750         
21751         if(href){
21752             this.el.select('a.small-box-footer',true).first().attr('href', href);
21753         }
21754         
21755     },
21756
21757     setContent: function (value)
21758     {
21759         this.el.select('.roo-content',true).first().dom.innerHTML = value;
21760     },
21761
21762     initEvents: function() 
21763     {   
21764         
21765     }
21766     
21767 });
21768
21769  
21770 /*
21771  * - LGPL
21772  *
21773  * TabBox
21774  * 
21775  */
21776 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21777
21778 /**
21779  * @class Roo.bootstrap.dash.TabBox
21780  * @extends Roo.bootstrap.Component
21781  * Bootstrap TabBox class
21782  * @cfg {String} title Title of the TabBox
21783  * @cfg {String} icon Icon of the TabBox
21784  * @cfg {Boolean} showtabs (true|false) show the tabs default true
21785  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
21786  * 
21787  * @constructor
21788  * Create a new TabBox
21789  * @param {Object} config The config object
21790  */
21791
21792
21793 Roo.bootstrap.dash.TabBox = function(config){
21794     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
21795     this.addEvents({
21796         // raw events
21797         /**
21798          * @event addpane
21799          * When a pane is added
21800          * @param {Roo.bootstrap.dash.TabPane} pane
21801          */
21802         "addpane" : true,
21803         /**
21804          * @event activatepane
21805          * When a pane is activated
21806          * @param {Roo.bootstrap.dash.TabPane} pane
21807          */
21808         "activatepane" : true
21809         
21810          
21811     });
21812     
21813     this.panes = [];
21814 };
21815
21816 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
21817
21818     title : '',
21819     icon : false,
21820     showtabs : true,
21821     tabScrollable : false,
21822     
21823     getChildContainer : function()
21824     {
21825         return this.el.select('.tab-content', true).first();
21826     },
21827     
21828     getAutoCreate : function(){
21829         
21830         var header = {
21831             tag: 'li',
21832             cls: 'pull-left header',
21833             html: this.title,
21834             cn : []
21835         };
21836         
21837         if(this.icon){
21838             header.cn.push({
21839                 tag: 'i',
21840                 cls: 'fa ' + this.icon
21841             });
21842         }
21843         
21844         var h = {
21845             tag: 'ul',
21846             cls: 'nav nav-tabs pull-right',
21847             cn: [
21848                 header
21849             ]
21850         };
21851         
21852         if(this.tabScrollable){
21853             h = {
21854                 tag: 'div',
21855                 cls: 'tab-header',
21856                 cn: [
21857                     {
21858                         tag: 'ul',
21859                         cls: 'nav nav-tabs pull-right',
21860                         cn: [
21861                             header
21862                         ]
21863                     }
21864                 ]
21865             }
21866         }
21867         
21868         var cfg = {
21869             tag: 'div',
21870             cls: 'nav-tabs-custom',
21871             cn: [
21872                 h,
21873                 {
21874                     tag: 'div',
21875                     cls: 'tab-content no-padding',
21876                     cn: []
21877                 }
21878             ]
21879         }
21880
21881         return  cfg;
21882     },
21883     initEvents : function()
21884     {
21885         //Roo.log('add add pane handler');
21886         this.on('addpane', this.onAddPane, this);
21887     },
21888      /**
21889      * Updates the box title
21890      * @param {String} html to set the title to.
21891      */
21892     setTitle : function(value)
21893     {
21894         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
21895     },
21896     onAddPane : function(pane)
21897     {
21898         this.panes.push(pane);
21899         //Roo.log('addpane');
21900         //Roo.log(pane);
21901         // tabs are rendere left to right..
21902         if(!this.showtabs){
21903             return;
21904         }
21905         
21906         var ctr = this.el.select('.nav-tabs', true).first();
21907          
21908          
21909         var existing = ctr.select('.nav-tab',true);
21910         var qty = existing.getCount();;
21911         
21912         
21913         var tab = ctr.createChild({
21914             tag : 'li',
21915             cls : 'nav-tab' + (qty ? '' : ' active'),
21916             cn : [
21917                 {
21918                     tag : 'a',
21919                     href:'#',
21920                     html : pane.title
21921                 }
21922             ]
21923         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
21924         pane.tab = tab;
21925         
21926         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
21927         if (!qty) {
21928             pane.el.addClass('active');
21929         }
21930         
21931                 
21932     },
21933     onTabClick : function(ev,un,ob,pane)
21934     {
21935         //Roo.log('tab - prev default');
21936         ev.preventDefault();
21937         
21938         
21939         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
21940         pane.tab.addClass('active');
21941         //Roo.log(pane.title);
21942         this.getChildContainer().select('.tab-pane',true).removeClass('active');
21943         // technically we should have a deactivate event.. but maybe add later.
21944         // and it should not de-activate the selected tab...
21945         this.fireEvent('activatepane', pane);
21946         pane.el.addClass('active');
21947         pane.fireEvent('activate');
21948         
21949         
21950     },
21951     
21952     getActivePane : function()
21953     {
21954         var r = false;
21955         Roo.each(this.panes, function(p) {
21956             if(p.el.hasClass('active')){
21957                 r = p;
21958                 return false;
21959             }
21960             
21961             return;
21962         });
21963         
21964         return r;
21965     }
21966     
21967     
21968 });
21969
21970  
21971 /*
21972  * - LGPL
21973  *
21974  * Tab pane
21975  * 
21976  */
21977 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21978 /**
21979  * @class Roo.bootstrap.TabPane
21980  * @extends Roo.bootstrap.Component
21981  * Bootstrap TabPane class
21982  * @cfg {Boolean} active (false | true) Default false
21983  * @cfg {String} title title of panel
21984
21985  * 
21986  * @constructor
21987  * Create a new TabPane
21988  * @param {Object} config The config object
21989  */
21990
21991 Roo.bootstrap.dash.TabPane = function(config){
21992     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
21993     
21994     this.addEvents({
21995         // raw events
21996         /**
21997          * @event activate
21998          * When a pane is activated
21999          * @param {Roo.bootstrap.dash.TabPane} pane
22000          */
22001         "activate" : true
22002          
22003     });
22004 };
22005
22006 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
22007     
22008     active : false,
22009     title : '',
22010     
22011     // the tabBox that this is attached to.
22012     tab : false,
22013      
22014     getAutoCreate : function() 
22015     {
22016         var cfg = {
22017             tag: 'div',
22018             cls: 'tab-pane'
22019         }
22020         
22021         if(this.active){
22022             cfg.cls += ' active';
22023         }
22024         
22025         return cfg;
22026     },
22027     initEvents  : function()
22028     {
22029         //Roo.log('trigger add pane handler');
22030         this.parent().fireEvent('addpane', this)
22031     },
22032     
22033      /**
22034      * Updates the tab title 
22035      * @param {String} html to set the title to.
22036      */
22037     setTitle: function(str)
22038     {
22039         if (!this.tab) {
22040             return;
22041         }
22042         this.title = str;
22043         this.tab.select('a', true).first().dom.innerHTML = str;
22044         
22045     }
22046     
22047     
22048     
22049 });
22050
22051  
22052
22053
22054  /*
22055  * - LGPL
22056  *
22057  * menu
22058  * 
22059  */
22060 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22061
22062 /**
22063  * @class Roo.bootstrap.menu.Menu
22064  * @extends Roo.bootstrap.Component
22065  * Bootstrap Menu class - container for Menu
22066  * @cfg {String} html Text of the menu
22067  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
22068  * @cfg {String} icon Font awesome icon
22069  * @cfg {String} pos Menu align to (top | bottom) default bottom
22070  * 
22071  * 
22072  * @constructor
22073  * Create a new Menu
22074  * @param {Object} config The config object
22075  */
22076
22077
22078 Roo.bootstrap.menu.Menu = function(config){
22079     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
22080     
22081     this.addEvents({
22082         /**
22083          * @event beforeshow
22084          * Fires before this menu is displayed
22085          * @param {Roo.bootstrap.menu.Menu} this
22086          */
22087         beforeshow : true,
22088         /**
22089          * @event beforehide
22090          * Fires before this menu is hidden
22091          * @param {Roo.bootstrap.menu.Menu} this
22092          */
22093         beforehide : true,
22094         /**
22095          * @event show
22096          * Fires after this menu is displayed
22097          * @param {Roo.bootstrap.menu.Menu} this
22098          */
22099         show : true,
22100         /**
22101          * @event hide
22102          * Fires after this menu is hidden
22103          * @param {Roo.bootstrap.menu.Menu} this
22104          */
22105         hide : true,
22106         /**
22107          * @event click
22108          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
22109          * @param {Roo.bootstrap.menu.Menu} this
22110          * @param {Roo.EventObject} e
22111          */
22112         click : true
22113     });
22114     
22115 };
22116
22117 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
22118     
22119     submenu : false,
22120     html : '',
22121     weight : 'default',
22122     icon : false,
22123     pos : 'bottom',
22124     
22125     
22126     getChildContainer : function() {
22127         if(this.isSubMenu){
22128             return this.el;
22129         }
22130         
22131         return this.el.select('ul.dropdown-menu', true).first();  
22132     },
22133     
22134     getAutoCreate : function()
22135     {
22136         var text = [
22137             {
22138                 tag : 'span',
22139                 cls : 'roo-menu-text',
22140                 html : this.html
22141             }
22142         ];
22143         
22144         if(this.icon){
22145             text.unshift({
22146                 tag : 'i',
22147                 cls : 'fa ' + this.icon
22148             })
22149         }
22150         
22151         
22152         var cfg = {
22153             tag : 'div',
22154             cls : 'btn-group',
22155             cn : [
22156                 {
22157                     tag : 'button',
22158                     cls : 'dropdown-button btn btn-' + this.weight,
22159                     cn : text
22160                 },
22161                 {
22162                     tag : 'button',
22163                     cls : 'dropdown-toggle btn btn-' + this.weight,
22164                     cn : [
22165                         {
22166                             tag : 'span',
22167                             cls : 'caret'
22168                         }
22169                     ]
22170                 },
22171                 {
22172                     tag : 'ul',
22173                     cls : 'dropdown-menu'
22174                 }
22175             ]
22176             
22177         };
22178         
22179         if(this.pos == 'top'){
22180             cfg.cls += ' dropup';
22181         }
22182         
22183         if(this.isSubMenu){
22184             cfg = {
22185                 tag : 'ul',
22186                 cls : 'dropdown-menu'
22187             }
22188         }
22189         
22190         return cfg;
22191     },
22192     
22193     onRender : function(ct, position)
22194     {
22195         this.isSubMenu = ct.hasClass('dropdown-submenu');
22196         
22197         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
22198     },
22199     
22200     initEvents : function() 
22201     {
22202         if(this.isSubMenu){
22203             return;
22204         }
22205         
22206         this.hidden = true;
22207         
22208         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
22209         this.triggerEl.on('click', this.onTriggerPress, this);
22210         
22211         this.buttonEl = this.el.select('button.dropdown-button', true).first();
22212         this.buttonEl.on('click', this.onClick, this);
22213         
22214     },
22215     
22216     list : function()
22217     {
22218         if(this.isSubMenu){
22219             return this.el;
22220         }
22221         
22222         return this.el.select('ul.dropdown-menu', true).first();
22223     },
22224     
22225     onClick : function(e)
22226     {
22227         this.fireEvent("click", this, e);
22228     },
22229     
22230     onTriggerPress  : function(e)
22231     {   
22232         if (this.isVisible()) {
22233             this.hide();
22234         } else {
22235             this.show();
22236         }
22237     },
22238     
22239     isVisible : function(){
22240         return !this.hidden;
22241     },
22242     
22243     show : function()
22244     {
22245         this.fireEvent("beforeshow", this);
22246         
22247         this.hidden = false;
22248         this.el.addClass('open');
22249         
22250         Roo.get(document).on("mouseup", this.onMouseUp, this);
22251         
22252         this.fireEvent("show", this);
22253         
22254         
22255     },
22256     
22257     hide : function()
22258     {
22259         this.fireEvent("beforehide", this);
22260         
22261         this.hidden = true;
22262         this.el.removeClass('open');
22263         
22264         Roo.get(document).un("mouseup", this.onMouseUp);
22265         
22266         this.fireEvent("hide", this);
22267     },
22268     
22269     onMouseUp : function()
22270     {
22271         this.hide();
22272     }
22273     
22274 });
22275
22276  
22277  /*
22278  * - LGPL
22279  *
22280  * menu item
22281  * 
22282  */
22283 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22284
22285 /**
22286  * @class Roo.bootstrap.menu.Item
22287  * @extends Roo.bootstrap.Component
22288  * Bootstrap MenuItem class
22289  * @cfg {Boolean} submenu (true | false) default false
22290  * @cfg {String} html text of the item
22291  * @cfg {String} href the link
22292  * @cfg {Boolean} disable (true | false) default false
22293  * @cfg {Boolean} preventDefault (true | false) default true
22294  * @cfg {String} icon Font awesome icon
22295  * @cfg {String} pos Submenu align to (left | right) default right 
22296  * 
22297  * 
22298  * @constructor
22299  * Create a new Item
22300  * @param {Object} config The config object
22301  */
22302
22303
22304 Roo.bootstrap.menu.Item = function(config){
22305     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
22306     this.addEvents({
22307         /**
22308          * @event mouseover
22309          * Fires when the mouse is hovering over this menu
22310          * @param {Roo.bootstrap.menu.Item} this
22311          * @param {Roo.EventObject} e
22312          */
22313         mouseover : true,
22314         /**
22315          * @event mouseout
22316          * Fires when the mouse exits this menu
22317          * @param {Roo.bootstrap.menu.Item} this
22318          * @param {Roo.EventObject} e
22319          */
22320         mouseout : true,
22321         // raw events
22322         /**
22323          * @event click
22324          * The raw click event for the entire grid.
22325          * @param {Roo.EventObject} e
22326          */
22327         click : true
22328     });
22329 };
22330
22331 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
22332     
22333     submenu : false,
22334     href : '',
22335     html : '',
22336     preventDefault: true,
22337     disable : false,
22338     icon : false,
22339     pos : 'right',
22340     
22341     getAutoCreate : function()
22342     {
22343         var text = [
22344             {
22345                 tag : 'span',
22346                 cls : 'roo-menu-item-text',
22347                 html : this.html
22348             }
22349         ];
22350         
22351         if(this.icon){
22352             text.unshift({
22353                 tag : 'i',
22354                 cls : 'fa ' + this.icon
22355             })
22356         }
22357         
22358         var cfg = {
22359             tag : 'li',
22360             cn : [
22361                 {
22362                     tag : 'a',
22363                     href : this.href || '#',
22364                     cn : text
22365                 }
22366             ]
22367         };
22368         
22369         if(this.disable){
22370             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
22371         }
22372         
22373         if(this.submenu){
22374             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
22375             
22376             if(this.pos == 'left'){
22377                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
22378             }
22379         }
22380         
22381         return cfg;
22382     },
22383     
22384     initEvents : function() 
22385     {
22386         this.el.on('mouseover', this.onMouseOver, this);
22387         this.el.on('mouseout', this.onMouseOut, this);
22388         
22389         this.el.select('a', true).first().on('click', this.onClick, this);
22390         
22391     },
22392     
22393     onClick : function(e)
22394     {
22395         if(this.preventDefault){
22396             e.preventDefault();
22397         }
22398         
22399         this.fireEvent("click", this, e);
22400     },
22401     
22402     onMouseOver : function(e)
22403     {
22404         if(this.submenu && this.pos == 'left'){
22405             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
22406         }
22407         
22408         this.fireEvent("mouseover", this, e);
22409     },
22410     
22411     onMouseOut : function(e)
22412     {
22413         this.fireEvent("mouseout", this, e);
22414     }
22415 });
22416
22417  
22418
22419  /*
22420  * - LGPL
22421  *
22422  * menu separator
22423  * 
22424  */
22425 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22426
22427 /**
22428  * @class Roo.bootstrap.menu.Separator
22429  * @extends Roo.bootstrap.Component
22430  * Bootstrap Separator class
22431  * 
22432  * @constructor
22433  * Create a new Separator
22434  * @param {Object} config The config object
22435  */
22436
22437
22438 Roo.bootstrap.menu.Separator = function(config){
22439     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
22440 };
22441
22442 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
22443     
22444     getAutoCreate : function(){
22445         var cfg = {
22446             tag : 'li',
22447             cls: 'divider'
22448         };
22449         
22450         return cfg;
22451     }
22452    
22453 });
22454
22455  
22456
22457  /*
22458  * - LGPL
22459  *
22460  * Tooltip
22461  * 
22462  */
22463
22464 /**
22465  * @class Roo.bootstrap.Tooltip
22466  * Bootstrap Tooltip class
22467  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
22468  * to determine which dom element triggers the tooltip.
22469  * 
22470  * It needs to add support for additional attributes like tooltip-position
22471  * 
22472  * @constructor
22473  * Create a new Toolti
22474  * @param {Object} config The config object
22475  */
22476
22477 Roo.bootstrap.Tooltip = function(config){
22478     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
22479 };
22480
22481 Roo.apply(Roo.bootstrap.Tooltip, {
22482     /**
22483      * @function init initialize tooltip monitoring.
22484      * @static
22485      */
22486     currentEl : false,
22487     currentTip : false,
22488     currentRegion : false,
22489     
22490     //  init : delay?
22491     
22492     init : function()
22493     {
22494         Roo.get(document).on('mouseover', this.enter ,this);
22495         Roo.get(document).on('mouseout', this.leave, this);
22496          
22497         
22498         this.currentTip = new Roo.bootstrap.Tooltip();
22499     },
22500     
22501     enter : function(ev)
22502     {
22503         var dom = ev.getTarget();
22504         
22505         //Roo.log(['enter',dom]);
22506         var el = Roo.fly(dom);
22507         if (this.currentEl) {
22508             //Roo.log(dom);
22509             //Roo.log(this.currentEl);
22510             //Roo.log(this.currentEl.contains(dom));
22511             if (this.currentEl == el) {
22512                 return;
22513             }
22514             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
22515                 return;
22516             }
22517
22518         }
22519         
22520         
22521         
22522         if (this.currentTip.el) {
22523             this.currentTip.el.hide(); // force hiding...
22524         }    
22525         //Roo.log(ev);
22526         var bindEl = el;
22527         
22528         // you can not look for children, as if el is the body.. then everythign is the child..
22529         if (!el.attr('tooltip')) { //
22530             if (!el.select("[tooltip]").elements.length) {
22531                 return;
22532             }
22533             // is the mouse over this child...?
22534             bindEl = el.select("[tooltip]").first();
22535             var xy = ev.getXY();
22536             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
22537                 //Roo.log("not in region.");
22538                 return;
22539             }
22540             //Roo.log("child element over..");
22541             
22542         }
22543         this.currentEl = bindEl;
22544         this.currentTip.bind(bindEl);
22545         this.currentRegion = Roo.lib.Region.getRegion(dom);
22546         this.currentTip.enter();
22547         
22548     },
22549     leave : function(ev)
22550     {
22551         var dom = ev.getTarget();
22552         //Roo.log(['leave',dom]);
22553         if (!this.currentEl) {
22554             return;
22555         }
22556         
22557         
22558         if (dom != this.currentEl.dom) {
22559             return;
22560         }
22561         var xy = ev.getXY();
22562         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
22563             return;
22564         }
22565         // only activate leave if mouse cursor is outside... bounding box..
22566         
22567         
22568         
22569         
22570         if (this.currentTip) {
22571             this.currentTip.leave();
22572         }
22573         //Roo.log('clear currentEl');
22574         this.currentEl = false;
22575         
22576         
22577     },
22578     alignment : {
22579         'left' : ['r-l', [-2,0], 'right'],
22580         'right' : ['l-r', [2,0], 'left'],
22581         'bottom' : ['t-b', [0,2], 'top'],
22582         'top' : [ 'b-t', [0,-2], 'bottom']
22583     }
22584     
22585 });
22586
22587
22588 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
22589     
22590     
22591     bindEl : false,
22592     
22593     delay : null, // can be { show : 300 , hide: 500}
22594     
22595     timeout : null,
22596     
22597     hoverState : null, //???
22598     
22599     placement : 'bottom', 
22600     
22601     getAutoCreate : function(){
22602     
22603         var cfg = {
22604            cls : 'tooltip',
22605            role : 'tooltip',
22606            cn : [
22607                 {
22608                     cls : 'tooltip-arrow'
22609                 },
22610                 {
22611                     cls : 'tooltip-inner'
22612                 }
22613            ]
22614         };
22615         
22616         return cfg;
22617     },
22618     bind : function(el)
22619     {
22620         this.bindEl = el;
22621     },
22622       
22623     
22624     enter : function () {
22625        
22626         if (this.timeout != null) {
22627             clearTimeout(this.timeout);
22628         }
22629         
22630         this.hoverState = 'in';
22631          //Roo.log("enter - show");
22632         if (!this.delay || !this.delay.show) {
22633             this.show();
22634             return;
22635         }
22636         var _t = this;
22637         this.timeout = setTimeout(function () {
22638             if (_t.hoverState == 'in') {
22639                 _t.show();
22640             }
22641         }, this.delay.show);
22642     },
22643     leave : function()
22644     {
22645         clearTimeout(this.timeout);
22646     
22647         this.hoverState = 'out';
22648          if (!this.delay || !this.delay.hide) {
22649             this.hide();
22650             return;
22651         }
22652        
22653         var _t = this;
22654         this.timeout = setTimeout(function () {
22655             //Roo.log("leave - timeout");
22656             
22657             if (_t.hoverState == 'out') {
22658                 _t.hide();
22659                 Roo.bootstrap.Tooltip.currentEl = false;
22660             }
22661         }, delay);
22662     },
22663     
22664     show : function ()
22665     {
22666         if (!this.el) {
22667             this.render(document.body);
22668         }
22669         // set content.
22670         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
22671         
22672         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
22673         
22674         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
22675         
22676         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
22677         
22678         var placement = typeof this.placement == 'function' ?
22679             this.placement.call(this, this.el, on_el) :
22680             this.placement;
22681             
22682         var autoToken = /\s?auto?\s?/i;
22683         var autoPlace = autoToken.test(placement);
22684         if (autoPlace) {
22685             placement = placement.replace(autoToken, '') || 'top';
22686         }
22687         
22688         //this.el.detach()
22689         //this.el.setXY([0,0]);
22690         this.el.show();
22691         //this.el.dom.style.display='block';
22692         this.el.addClass(placement);
22693         
22694         //this.el.appendTo(on_el);
22695         
22696         var p = this.getPosition();
22697         var box = this.el.getBox();
22698         
22699         if (autoPlace) {
22700             // fixme..
22701         }
22702         var align = Roo.bootstrap.Tooltip.alignment[placement];
22703         this.el.alignTo(this.bindEl, align[0],align[1]);
22704         //var arrow = this.el.select('.arrow',true).first();
22705         //arrow.set(align[2], 
22706         
22707         this.el.addClass('in fade');
22708         this.hoverState = null;
22709         
22710         if (this.el.hasClass('fade')) {
22711             // fade it?
22712         }
22713         
22714     },
22715     hide : function()
22716     {
22717          
22718         if (!this.el) {
22719             return;
22720         }
22721         //this.el.setXY([0,0]);
22722         this.el.removeClass('in');
22723         //this.el.hide();
22724         
22725     }
22726     
22727 });
22728  
22729
22730  /*
22731  * - LGPL
22732  *
22733  * Location Picker
22734  * 
22735  */
22736
22737 /**
22738  * @class Roo.bootstrap.LocationPicker
22739  * @extends Roo.bootstrap.Component
22740  * Bootstrap LocationPicker class
22741  * @cfg {Number} latitude Position when init default 0
22742  * @cfg {Number} longitude Position when init default 0
22743  * @cfg {Number} zoom default 15
22744  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
22745  * @cfg {Boolean} mapTypeControl default false
22746  * @cfg {Boolean} disableDoubleClickZoom default false
22747  * @cfg {Boolean} scrollwheel default true
22748  * @cfg {Boolean} streetViewControl default false
22749  * @cfg {Number} radius default 0
22750  * @cfg {String} locationName
22751  * @cfg {Boolean} draggable default true
22752  * @cfg {Boolean} enableAutocomplete default false
22753  * @cfg {Boolean} enableReverseGeocode default true
22754  * @cfg {String} markerTitle
22755  * 
22756  * @constructor
22757  * Create a new LocationPicker
22758  * @param {Object} config The config object
22759  */
22760
22761
22762 Roo.bootstrap.LocationPicker = function(config){
22763     
22764     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
22765     
22766     this.addEvents({
22767         /**
22768          * @event initial
22769          * Fires when the picker initialized.
22770          * @param {Roo.bootstrap.LocationPicker} this
22771          * @param {Google Location} location
22772          */
22773         initial : true,
22774         /**
22775          * @event positionchanged
22776          * Fires when the picker position changed.
22777          * @param {Roo.bootstrap.LocationPicker} this
22778          * @param {Google Location} location
22779          */
22780         positionchanged : true,
22781         /**
22782          * @event resize
22783          * Fires when the map resize.
22784          * @param {Roo.bootstrap.LocationPicker} this
22785          */
22786         resize : true,
22787         /**
22788          * @event show
22789          * Fires when the map show.
22790          * @param {Roo.bootstrap.LocationPicker} this
22791          */
22792         show : true,
22793         /**
22794          * @event hide
22795          * Fires when the map hide.
22796          * @param {Roo.bootstrap.LocationPicker} this
22797          */
22798         hide : true,
22799         /**
22800          * @event mapClick
22801          * Fires when click the map.
22802          * @param {Roo.bootstrap.LocationPicker} this
22803          * @param {Map event} e
22804          */
22805         mapClick : true,
22806         /**
22807          * @event mapRightClick
22808          * Fires when right click the map.
22809          * @param {Roo.bootstrap.LocationPicker} this
22810          * @param {Map event} e
22811          */
22812         mapRightClick : true,
22813         /**
22814          * @event markerClick
22815          * Fires when click the marker.
22816          * @param {Roo.bootstrap.LocationPicker} this
22817          * @param {Map event} e
22818          */
22819         markerClick : true,
22820         /**
22821          * @event markerRightClick
22822          * Fires when right click the marker.
22823          * @param {Roo.bootstrap.LocationPicker} this
22824          * @param {Map event} e
22825          */
22826         markerRightClick : true,
22827         /**
22828          * @event OverlayViewDraw
22829          * Fires when OverlayView Draw
22830          * @param {Roo.bootstrap.LocationPicker} this
22831          */
22832         OverlayViewDraw : true,
22833         /**
22834          * @event OverlayViewOnAdd
22835          * Fires when OverlayView Draw
22836          * @param {Roo.bootstrap.LocationPicker} this
22837          */
22838         OverlayViewOnAdd : true,
22839         /**
22840          * @event OverlayViewOnRemove
22841          * Fires when OverlayView Draw
22842          * @param {Roo.bootstrap.LocationPicker} this
22843          */
22844         OverlayViewOnRemove : true,
22845         /**
22846          * @event OverlayViewShow
22847          * Fires when OverlayView Draw
22848          * @param {Roo.bootstrap.LocationPicker} this
22849          * @param {Pixel} cpx
22850          */
22851         OverlayViewShow : true,
22852         /**
22853          * @event OverlayViewHide
22854          * Fires when OverlayView Draw
22855          * @param {Roo.bootstrap.LocationPicker} this
22856          */
22857         OverlayViewHide : true
22858     });
22859         
22860 };
22861
22862 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
22863     
22864     gMapContext: false,
22865     
22866     latitude: 0,
22867     longitude: 0,
22868     zoom: 15,
22869     mapTypeId: false,
22870     mapTypeControl: false,
22871     disableDoubleClickZoom: false,
22872     scrollwheel: true,
22873     streetViewControl: false,
22874     radius: 0,
22875     locationName: '',
22876     draggable: true,
22877     enableAutocomplete: false,
22878     enableReverseGeocode: true,
22879     markerTitle: '',
22880     
22881     getAutoCreate: function()
22882     {
22883
22884         var cfg = {
22885             tag: 'div',
22886             cls: 'roo-location-picker'
22887         };
22888         
22889         return cfg
22890     },
22891     
22892     initEvents: function(ct, position)
22893     {       
22894         if(!this.el.getWidth() || this.isApplied()){
22895             return;
22896         }
22897         
22898         this.el.setVisibilityMode(Roo.Element.DISPLAY);
22899         
22900         this.initial();
22901     },
22902     
22903     initial: function()
22904     {
22905         if(!this.mapTypeId){
22906             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
22907         }
22908         
22909         this.gMapContext = this.GMapContext();
22910         
22911         this.initOverlayView();
22912         
22913         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
22914         
22915         var _this = this;
22916                 
22917         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
22918             _this.setPosition(_this.gMapContext.marker.position);
22919         });
22920         
22921         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
22922             _this.fireEvent('mapClick', this, event);
22923             
22924         });
22925
22926         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
22927             _this.fireEvent('mapRightClick', this, event);
22928             
22929         });
22930         
22931         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
22932             _this.fireEvent('markerClick', this, event);
22933             
22934         });
22935
22936         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
22937             _this.fireEvent('markerRightClick', this, event);
22938             
22939         });
22940         
22941         this.setPosition(this.gMapContext.location);
22942         
22943         this.fireEvent('initial', this, this.gMapContext.location);
22944     },
22945     
22946     initOverlayView: function()
22947     {
22948         var _this = this;
22949         
22950         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
22951             
22952             draw: function()
22953             {
22954                 _this.fireEvent('OverlayViewDraw', _this);
22955             },
22956             
22957             onAdd: function()
22958             {
22959                 _this.fireEvent('OverlayViewOnAdd', _this);
22960             },
22961             
22962             onRemove: function()
22963             {
22964                 _this.fireEvent('OverlayViewOnRemove', _this);
22965             },
22966             
22967             show: function(cpx)
22968             {
22969                 _this.fireEvent('OverlayViewShow', _this, cpx);
22970             },
22971             
22972             hide: function()
22973             {
22974                 _this.fireEvent('OverlayViewHide', _this);
22975             }
22976             
22977         });
22978     },
22979     
22980     fromLatLngToContainerPixel: function(event)
22981     {
22982         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
22983     },
22984     
22985     isApplied: function() 
22986     {
22987         return this.getGmapContext() == false ? false : true;
22988     },
22989     
22990     getGmapContext: function() 
22991     {
22992         return this.gMapContext
22993     },
22994     
22995     GMapContext: function() 
22996     {
22997         var position = new google.maps.LatLng(this.latitude, this.longitude);
22998         
22999         var _map = new google.maps.Map(this.el.dom, {
23000             center: position,
23001             zoom: this.zoom,
23002             mapTypeId: this.mapTypeId,
23003             mapTypeControl: this.mapTypeControl,
23004             disableDoubleClickZoom: this.disableDoubleClickZoom,
23005             scrollwheel: this.scrollwheel,
23006             streetViewControl: this.streetViewControl,
23007             locationName: this.locationName,
23008             draggable: this.draggable,
23009             enableAutocomplete: this.enableAutocomplete,
23010             enableReverseGeocode: this.enableReverseGeocode
23011         });
23012         
23013         var _marker = new google.maps.Marker({
23014             position: position,
23015             map: _map,
23016             title: this.markerTitle,
23017             draggable: this.draggable
23018         });
23019         
23020         return {
23021             map: _map,
23022             marker: _marker,
23023             circle: null,
23024             location: position,
23025             radius: this.radius,
23026             locationName: this.locationName,
23027             addressComponents: {
23028                 formatted_address: null,
23029                 addressLine1: null,
23030                 addressLine2: null,
23031                 streetName: null,
23032                 streetNumber: null,
23033                 city: null,
23034                 district: null,
23035                 state: null,
23036                 stateOrProvince: null
23037             },
23038             settings: this,
23039             domContainer: this.el.dom,
23040             geodecoder: new google.maps.Geocoder()
23041         };
23042     },
23043     
23044     drawCircle: function(center, radius, options) 
23045     {
23046         if (this.gMapContext.circle != null) {
23047             this.gMapContext.circle.setMap(null);
23048         }
23049         if (radius > 0) {
23050             radius *= 1;
23051             options = Roo.apply({}, options, {
23052                 strokeColor: "#0000FF",
23053                 strokeOpacity: .35,
23054                 strokeWeight: 2,
23055                 fillColor: "#0000FF",
23056                 fillOpacity: .2
23057             });
23058             
23059             options.map = this.gMapContext.map;
23060             options.radius = radius;
23061             options.center = center;
23062             this.gMapContext.circle = new google.maps.Circle(options);
23063             return this.gMapContext.circle;
23064         }
23065         
23066         return null;
23067     },
23068     
23069     setPosition: function(location) 
23070     {
23071         this.gMapContext.location = location;
23072         this.gMapContext.marker.setPosition(location);
23073         this.gMapContext.map.panTo(location);
23074         this.drawCircle(location, this.gMapContext.radius, {});
23075         
23076         var _this = this;
23077         
23078         if (this.gMapContext.settings.enableReverseGeocode) {
23079             this.gMapContext.geodecoder.geocode({
23080                 latLng: this.gMapContext.location
23081             }, function(results, status) {
23082                 
23083                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
23084                     _this.gMapContext.locationName = results[0].formatted_address;
23085                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
23086                     
23087                     _this.fireEvent('positionchanged', this, location);
23088                 }
23089             });
23090             
23091             return;
23092         }
23093         
23094         this.fireEvent('positionchanged', this, location);
23095     },
23096     
23097     resize: function()
23098     {
23099         google.maps.event.trigger(this.gMapContext.map, "resize");
23100         
23101         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
23102         
23103         this.fireEvent('resize', this);
23104     },
23105     
23106     setPositionByLatLng: function(latitude, longitude)
23107     {
23108         this.setPosition(new google.maps.LatLng(latitude, longitude));
23109     },
23110     
23111     getCurrentPosition: function() 
23112     {
23113         return {
23114             latitude: this.gMapContext.location.lat(),
23115             longitude: this.gMapContext.location.lng()
23116         };
23117     },
23118     
23119     getAddressName: function() 
23120     {
23121         return this.gMapContext.locationName;
23122     },
23123     
23124     getAddressComponents: function() 
23125     {
23126         return this.gMapContext.addressComponents;
23127     },
23128     
23129     address_component_from_google_geocode: function(address_components) 
23130     {
23131         var result = {};
23132         
23133         for (var i = 0; i < address_components.length; i++) {
23134             var component = address_components[i];
23135             if (component.types.indexOf("postal_code") >= 0) {
23136                 result.postalCode = component.short_name;
23137             } else if (component.types.indexOf("street_number") >= 0) {
23138                 result.streetNumber = component.short_name;
23139             } else if (component.types.indexOf("route") >= 0) {
23140                 result.streetName = component.short_name;
23141             } else if (component.types.indexOf("neighborhood") >= 0) {
23142                 result.city = component.short_name;
23143             } else if (component.types.indexOf("locality") >= 0) {
23144                 result.city = component.short_name;
23145             } else if (component.types.indexOf("sublocality") >= 0) {
23146                 result.district = component.short_name;
23147             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
23148                 result.stateOrProvince = component.short_name;
23149             } else if (component.types.indexOf("country") >= 0) {
23150                 result.country = component.short_name;
23151             }
23152         }
23153         
23154         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
23155         result.addressLine2 = "";
23156         return result;
23157     },
23158     
23159     setZoomLevel: function(zoom)
23160     {
23161         this.gMapContext.map.setZoom(zoom);
23162     },
23163     
23164     show: function()
23165     {
23166         if(!this.el){
23167             return;
23168         }
23169         
23170         this.el.show();
23171         
23172         this.resize();
23173         
23174         this.fireEvent('show', this);
23175     },
23176     
23177     hide: function()
23178     {
23179         if(!this.el){
23180             return;
23181         }
23182         
23183         this.el.hide();
23184         
23185         this.fireEvent('hide', this);
23186     }
23187     
23188 });
23189
23190 Roo.apply(Roo.bootstrap.LocationPicker, {
23191     
23192     OverlayView : function(map, options)
23193     {
23194         options = options || {};
23195         
23196         this.setMap(map);
23197     }
23198     
23199     
23200 });/*
23201  * - LGPL
23202  *
23203  * Alert
23204  * 
23205  */
23206
23207 /**
23208  * @class Roo.bootstrap.Alert
23209  * @extends Roo.bootstrap.Component
23210  * Bootstrap Alert class
23211  * @cfg {String} title The title of alert
23212  * @cfg {String} html The content of alert
23213  * @cfg {String} weight (  success | info | warning | danger )
23214  * @cfg {String} faicon font-awesomeicon
23215  * 
23216  * @constructor
23217  * Create a new alert
23218  * @param {Object} config The config object
23219  */
23220
23221
23222 Roo.bootstrap.Alert = function(config){
23223     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
23224     
23225 };
23226
23227 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
23228     
23229     title: '',
23230     html: '',
23231     weight: false,
23232     faicon: false,
23233     
23234     getAutoCreate : function()
23235     {
23236         
23237         var cfg = {
23238             tag : 'div',
23239             cls : 'alert',
23240             cn : [
23241                 {
23242                     tag : 'i',
23243                     cls : 'roo-alert-icon'
23244                     
23245                 },
23246                 {
23247                     tag : 'b',
23248                     cls : 'roo-alert-title',
23249                     html : this.title
23250                 },
23251                 {
23252                     tag : 'span',
23253                     cls : 'roo-alert-text',
23254                     html : this.html
23255                 }
23256             ]
23257         };
23258         
23259         if(this.faicon){
23260             cfg.cn[0].cls += ' fa ' + this.faicon;
23261         }
23262         
23263         if(this.weight){
23264             cfg.cls += ' alert-' + this.weight;
23265         }
23266         
23267         return cfg;
23268     },
23269     
23270     initEvents: function() 
23271     {
23272         this.el.setVisibilityMode(Roo.Element.DISPLAY);
23273     },
23274     
23275     setTitle : function(str)
23276     {
23277         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
23278     },
23279     
23280     setText : function(str)
23281     {
23282         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
23283     },
23284     
23285     setWeight : function(weight)
23286     {
23287         if(this.weight){
23288             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
23289         }
23290         
23291         this.weight = weight;
23292         
23293         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
23294     },
23295     
23296     setIcon : function(icon)
23297     {
23298         if(this.faicon){
23299             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
23300         }
23301         
23302         this.faicon = icon
23303         
23304         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
23305     },
23306     
23307     hide: function() 
23308     {
23309         this.el.hide();   
23310     },
23311     
23312     show: function() 
23313     {  
23314         this.el.show();   
23315     }
23316     
23317 });
23318
23319