docs/default.css
[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  * 
1330  * @constructor
1331  * Create a new Input
1332  * @param {Object} config The config object
1333  */
1334
1335 Roo.bootstrap.Img = function(config){
1336     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1337     
1338     this.addEvents({
1339         // img events
1340         /**
1341          * @event click
1342          * The img click event for the img.
1343          * @param {Roo.EventObject} e
1344          */
1345         "click" : true
1346     });
1347 };
1348
1349 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1350     
1351     imgResponsive: true,
1352     border: '',
1353     src: '',
1354     href: false,
1355     target: false,
1356
1357     getAutoCreate : function(){
1358         
1359         var cfg = {
1360             tag: 'img',
1361             cls: (this.imgResponsive) ? 'img-responsive' : '',
1362             html : null
1363         }
1364         
1365         cfg.html = this.html || cfg.html;
1366         
1367         cfg.src = this.src || cfg.src;
1368         
1369         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1370             cfg.cls += ' img-' + this.border;
1371         }
1372         
1373         if(this.alt){
1374             cfg.alt = this.alt;
1375         }
1376         
1377         if(this.href){
1378             var a = {
1379                 tag: 'a',
1380                 href: this.href,
1381                 cn: [
1382                     cfg
1383                 ]
1384             }
1385             
1386             if(this.target){
1387                 a.target = this.target;
1388             }
1389             
1390         }
1391         
1392         
1393         return (this.href) ? a : cfg;
1394     },
1395     
1396     initEvents: function() {
1397         
1398         if(!this.href){
1399             this.el.on('click', this.onClick, this);
1400         }
1401     },
1402     
1403     onClick : function(e)
1404     {
1405         Roo.log('img onclick');
1406         this.fireEvent('click', this, e);
1407     }
1408    
1409 });
1410
1411  /*
1412  * - LGPL
1413  *
1414  * image
1415  * 
1416  */
1417
1418
1419 /**
1420  * @class Roo.bootstrap.Link
1421  * @extends Roo.bootstrap.Component
1422  * Bootstrap Link Class
1423  * @cfg {String} alt image alternative text
1424  * @cfg {String} href a tag href
1425  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1426  * @cfg {String} html the content of the link.
1427  * @cfg {String} anchor name for the anchor link
1428
1429  * @cfg {Boolean} preventDefault (true | false) default false
1430
1431  * 
1432  * @constructor
1433  * Create a new Input
1434  * @param {Object} config The config object
1435  */
1436
1437 Roo.bootstrap.Link = function(config){
1438     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1439     
1440     this.addEvents({
1441         // img events
1442         /**
1443          * @event click
1444          * The img click event for the img.
1445          * @param {Roo.EventObject} e
1446          */
1447         "click" : true
1448     });
1449 };
1450
1451 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1452     
1453     href: false,
1454     target: false,
1455     preventDefault: false,
1456     anchor : false,
1457     alt : false,
1458
1459     getAutoCreate : function()
1460     {
1461         
1462         var cfg = {
1463             tag: 'a'
1464         };
1465         // anchor's do not require html/href...
1466         if (this.anchor === false) {
1467             cfg.html = this.html || 'html-missing';
1468             cfg.href = this.href || '#';
1469         } else {
1470             cfg.name = this.anchor;
1471             if (this.html !== false) {
1472                 cfg.html = this.html;
1473             }
1474             if (this.href !== false) {
1475                 cfg.href = this.href;
1476             }
1477         }
1478         
1479         if(this.alt !== false){
1480             cfg.alt = this.alt;
1481         }
1482         
1483         
1484         if(this.target !== false) {
1485             cfg.target = this.target;
1486         }
1487         
1488         return cfg;
1489     },
1490     
1491     initEvents: function() {
1492         
1493         if(!this.href || this.preventDefault){
1494             this.el.on('click', this.onClick, this);
1495         }
1496     },
1497     
1498     onClick : function(e)
1499     {
1500         if(this.preventDefault){
1501             e.preventDefault();
1502         }
1503         //Roo.log('img onclick');
1504         this.fireEvent('click', this, e);
1505     }
1506    
1507 });
1508
1509  /*
1510  * - LGPL
1511  *
1512  * header
1513  * 
1514  */
1515
1516 /**
1517  * @class Roo.bootstrap.Header
1518  * @extends Roo.bootstrap.Component
1519  * Bootstrap Header class
1520  * @cfg {String} html content of header
1521  * @cfg {Number} level (1|2|3|4|5|6) default 1
1522  * 
1523  * @constructor
1524  * Create a new Header
1525  * @param {Object} config The config object
1526  */
1527
1528
1529 Roo.bootstrap.Header  = function(config){
1530     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1531 };
1532
1533 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1534     
1535     //href : false,
1536     html : false,
1537     level : 1,
1538     
1539     
1540     
1541     getAutoCreate : function(){
1542         
1543         
1544         
1545         var cfg = {
1546             tag: 'h' + (1 *this.level),
1547             html: this.html || ''
1548         } ;
1549         
1550         return cfg;
1551     }
1552    
1553 });
1554
1555  
1556
1557  /*
1558  * Based on:
1559  * Ext JS Library 1.1.1
1560  * Copyright(c) 2006-2007, Ext JS, LLC.
1561  *
1562  * Originally Released Under LGPL - original licence link has changed is not relivant.
1563  *
1564  * Fork - LGPL
1565  * <script type="text/javascript">
1566  */
1567  
1568 /**
1569  * @class Roo.bootstrap.MenuMgr
1570  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1571  * @singleton
1572  */
1573 Roo.bootstrap.MenuMgr = function(){
1574    var menus, active, groups = {}, attached = false, lastShow = new Date();
1575
1576    // private - called when first menu is created
1577    function init(){
1578        menus = {};
1579        active = new Roo.util.MixedCollection();
1580        Roo.get(document).addKeyListener(27, function(){
1581            if(active.length > 0){
1582                hideAll();
1583            }
1584        });
1585    }
1586
1587    // private
1588    function hideAll(){
1589        if(active && active.length > 0){
1590            var c = active.clone();
1591            c.each(function(m){
1592                m.hide();
1593            });
1594        }
1595    }
1596
1597    // private
1598    function onHide(m){
1599        active.remove(m);
1600        if(active.length < 1){
1601            Roo.get(document).un("mouseup", onMouseDown);
1602             
1603            attached = false;
1604        }
1605    }
1606
1607    // private
1608    function onShow(m){
1609        var last = active.last();
1610        lastShow = new Date();
1611        active.add(m);
1612        if(!attached){
1613           Roo.get(document).on("mouseup", onMouseDown);
1614            
1615            attached = true;
1616        }
1617        if(m.parentMenu){
1618           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1619           m.parentMenu.activeChild = m;
1620        }else if(last && last.isVisible()){
1621           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1622        }
1623    }
1624
1625    // private
1626    function onBeforeHide(m){
1627        if(m.activeChild){
1628            m.activeChild.hide();
1629        }
1630        if(m.autoHideTimer){
1631            clearTimeout(m.autoHideTimer);
1632            delete m.autoHideTimer;
1633        }
1634    }
1635
1636    // private
1637    function onBeforeShow(m){
1638        var pm = m.parentMenu;
1639        if(!pm && !m.allowOtherMenus){
1640            hideAll();
1641        }else if(pm && pm.activeChild && active != m){
1642            pm.activeChild.hide();
1643        }
1644    }
1645
1646    // private this should really trigger on mouseup..
1647    function onMouseDown(e){
1648         Roo.log("on Mouse Up");
1649         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1650             Roo.log("hideAll");
1651             hideAll();
1652             e.stopEvent();
1653         }
1654         
1655         
1656    }
1657
1658    // private
1659    function onBeforeCheck(mi, state){
1660        if(state){
1661            var g = groups[mi.group];
1662            for(var i = 0, l = g.length; i < l; i++){
1663                if(g[i] != mi){
1664                    g[i].setChecked(false);
1665                }
1666            }
1667        }
1668    }
1669
1670    return {
1671
1672        /**
1673         * Hides all menus that are currently visible
1674         */
1675        hideAll : function(){
1676             hideAll();  
1677        },
1678
1679        // private
1680        register : function(menu){
1681            if(!menus){
1682                init();
1683            }
1684            menus[menu.id] = menu;
1685            menu.on("beforehide", onBeforeHide);
1686            menu.on("hide", onHide);
1687            menu.on("beforeshow", onBeforeShow);
1688            menu.on("show", onShow);
1689            var g = menu.group;
1690            if(g && menu.events["checkchange"]){
1691                if(!groups[g]){
1692                    groups[g] = [];
1693                }
1694                groups[g].push(menu);
1695                menu.on("checkchange", onCheck);
1696            }
1697        },
1698
1699         /**
1700          * Returns a {@link Roo.menu.Menu} object
1701          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1702          * be used to generate and return a new Menu instance.
1703          */
1704        get : function(menu){
1705            if(typeof menu == "string"){ // menu id
1706                return menus[menu];
1707            }else if(menu.events){  // menu instance
1708                return menu;
1709            }
1710            /*else if(typeof menu.length == 'number'){ // array of menu items?
1711                return new Roo.bootstrap.Menu({items:menu});
1712            }else{ // otherwise, must be a config
1713                return new Roo.bootstrap.Menu(menu);
1714            }
1715            */
1716            return false;
1717        },
1718
1719        // private
1720        unregister : function(menu){
1721            delete menus[menu.id];
1722            menu.un("beforehide", onBeforeHide);
1723            menu.un("hide", onHide);
1724            menu.un("beforeshow", onBeforeShow);
1725            menu.un("show", onShow);
1726            var g = menu.group;
1727            if(g && menu.events["checkchange"]){
1728                groups[g].remove(menu);
1729                menu.un("checkchange", onCheck);
1730            }
1731        },
1732
1733        // private
1734        registerCheckable : function(menuItem){
1735            var g = menuItem.group;
1736            if(g){
1737                if(!groups[g]){
1738                    groups[g] = [];
1739                }
1740                groups[g].push(menuItem);
1741                menuItem.on("beforecheckchange", onBeforeCheck);
1742            }
1743        },
1744
1745        // private
1746        unregisterCheckable : function(menuItem){
1747            var g = menuItem.group;
1748            if(g){
1749                groups[g].remove(menuItem);
1750                menuItem.un("beforecheckchange", onBeforeCheck);
1751            }
1752        }
1753    };
1754 }();/*
1755  * - LGPL
1756  *
1757  * menu
1758  * 
1759  */
1760
1761 /**
1762  * @class Roo.bootstrap.Menu
1763  * @extends Roo.bootstrap.Component
1764  * Bootstrap Menu class - container for MenuItems
1765  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1766  * 
1767  * @constructor
1768  * Create a new Menu
1769  * @param {Object} config The config object
1770  */
1771
1772
1773 Roo.bootstrap.Menu = function(config){
1774     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1775     if (this.registerMenu) {
1776         Roo.bootstrap.MenuMgr.register(this);
1777     }
1778     this.addEvents({
1779         /**
1780          * @event beforeshow
1781          * Fires before this menu is displayed
1782          * @param {Roo.menu.Menu} this
1783          */
1784         beforeshow : true,
1785         /**
1786          * @event beforehide
1787          * Fires before this menu is hidden
1788          * @param {Roo.menu.Menu} this
1789          */
1790         beforehide : true,
1791         /**
1792          * @event show
1793          * Fires after this menu is displayed
1794          * @param {Roo.menu.Menu} this
1795          */
1796         show : true,
1797         /**
1798          * @event hide
1799          * Fires after this menu is hidden
1800          * @param {Roo.menu.Menu} this
1801          */
1802         hide : true,
1803         /**
1804          * @event click
1805          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1806          * @param {Roo.menu.Menu} this
1807          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1808          * @param {Roo.EventObject} e
1809          */
1810         click : true,
1811         /**
1812          * @event mouseover
1813          * Fires when the mouse is hovering over this menu
1814          * @param {Roo.menu.Menu} this
1815          * @param {Roo.EventObject} e
1816          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1817          */
1818         mouseover : true,
1819         /**
1820          * @event mouseout
1821          * Fires when the mouse exits this menu
1822          * @param {Roo.menu.Menu} this
1823          * @param {Roo.EventObject} e
1824          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1825          */
1826         mouseout : true,
1827         /**
1828          * @event itemclick
1829          * Fires when a menu item contained in this menu is clicked
1830          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1831          * @param {Roo.EventObject} e
1832          */
1833         itemclick: true
1834     });
1835     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1836 };
1837
1838 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1839     
1840    /// html : false,
1841     //align : '',
1842     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1843     type: false,
1844     /**
1845      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1846      */
1847     registerMenu : true,
1848     
1849     menuItems :false, // stores the menu items..
1850     
1851     hidden:true,
1852     
1853     parentMenu : false,
1854     
1855     getChildContainer : function() {
1856         return this.el;  
1857     },
1858     
1859     getAutoCreate : function(){
1860          
1861         //if (['right'].indexOf(this.align)!==-1) {
1862         //    cfg.cn[1].cls += ' pull-right'
1863         //}
1864         
1865         
1866         var cfg = {
1867             tag : 'ul',
1868             cls : 'dropdown-menu' ,
1869             style : 'z-index:1000'
1870             
1871         }
1872         
1873         if (this.type === 'submenu') {
1874             cfg.cls = 'submenu active';
1875         }
1876         if (this.type === 'treeview') {
1877             cfg.cls = 'treeview-menu';
1878         }
1879         
1880         return cfg;
1881     },
1882     initEvents : function() {
1883         
1884        // Roo.log("ADD event");
1885        // Roo.log(this.triggerEl.dom);
1886         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
1887         
1888         this.triggerEl.addClass('dropdown-toggle');
1889         this.el.on(Roo.isTouch ? 'touchstart' : 'click' , this.onClick, this);
1890
1891         this.el.on("mouseover", this.onMouseOver, this);
1892         this.el.on("mouseout", this.onMouseOut, this);
1893         
1894         
1895     },
1896     findTargetItem : function(e){
1897         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1898         if(!t){
1899             return false;
1900         }
1901         //Roo.log(t);         Roo.log(t.id);
1902         if(t && t.id){
1903             //Roo.log(this.menuitems);
1904             return this.menuitems.get(t.id);
1905             
1906             //return this.items.get(t.menuItemId);
1907         }
1908         
1909         return false;
1910     },
1911     onClick : function(e){
1912         Roo.log("menu.onClick");
1913         var t = this.findTargetItem(e);
1914         if(!t || t.isContainer){
1915             return;
1916         }
1917         Roo.log(e);
1918         /*
1919         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1920             if(t == this.activeItem && t.shouldDeactivate(e)){
1921                 this.activeItem.deactivate();
1922                 delete this.activeItem;
1923                 return;
1924             }
1925             if(t.canActivate){
1926                 this.setActiveItem(t, true);
1927             }
1928             return;
1929             
1930             
1931         }
1932         */
1933        
1934         Roo.log('pass click event');
1935         
1936         t.onClick(e);
1937         
1938         this.fireEvent("click", this, t, e);
1939         
1940         this.hide();
1941     },
1942      onMouseOver : function(e){
1943         var t  = this.findTargetItem(e);
1944         //Roo.log(t);
1945         //if(t){
1946         //    if(t.canActivate && !t.disabled){
1947         //        this.setActiveItem(t, true);
1948         //    }
1949         //}
1950         
1951         this.fireEvent("mouseover", this, e, t);
1952     },
1953     isVisible : function(){
1954         return !this.hidden;
1955     },
1956      onMouseOut : function(e){
1957         var t  = this.findTargetItem(e);
1958         
1959         //if(t ){
1960         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1961         //        this.activeItem.deactivate();
1962         //        delete this.activeItem;
1963         //    }
1964         //}
1965         this.fireEvent("mouseout", this, e, t);
1966     },
1967     
1968     
1969     /**
1970      * Displays this menu relative to another element
1971      * @param {String/HTMLElement/Roo.Element} element The element to align to
1972      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1973      * the element (defaults to this.defaultAlign)
1974      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1975      */
1976     show : function(el, pos, parentMenu){
1977         this.parentMenu = parentMenu;
1978         if(!this.el){
1979             this.render();
1980         }
1981         this.fireEvent("beforeshow", this);
1982         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1983     },
1984      /**
1985      * Displays this menu at a specific xy position
1986      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1987      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1988      */
1989     showAt : function(xy, parentMenu, /* private: */_e){
1990         this.parentMenu = parentMenu;
1991         if(!this.el){
1992             this.render();
1993         }
1994         if(_e !== false){
1995             this.fireEvent("beforeshow", this);
1996             //xy = this.el.adjustForConstraints(xy);
1997         }
1998         
1999         //this.el.show();
2000         this.hideMenuItems();
2001         this.hidden = false;
2002         this.triggerEl.addClass('open');
2003         
2004         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2005             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2006         }
2007         
2008         this.el.setXY(xy);
2009         this.focus();
2010         this.fireEvent("show", this);
2011     },
2012     
2013     focus : function(){
2014         return;
2015         if(!this.hidden){
2016             this.doFocus.defer(50, this);
2017         }
2018     },
2019
2020     doFocus : function(){
2021         if(!this.hidden){
2022             this.focusEl.focus();
2023         }
2024     },
2025
2026     /**
2027      * Hides this menu and optionally all parent menus
2028      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2029      */
2030     hide : function(deep){
2031         
2032         this.hideMenuItems();
2033         if(this.el && this.isVisible()){
2034             this.fireEvent("beforehide", this);
2035             if(this.activeItem){
2036                 this.activeItem.deactivate();
2037                 this.activeItem = null;
2038             }
2039             this.triggerEl.removeClass('open');;
2040             this.hidden = true;
2041             this.fireEvent("hide", this);
2042         }
2043         if(deep === true && this.parentMenu){
2044             this.parentMenu.hide(true);
2045         }
2046     },
2047     
2048     onTriggerPress  : function(e)
2049     {
2050         
2051         Roo.log('trigger press');
2052         //Roo.log(e.getTarget());
2053        // Roo.log(this.triggerEl.dom);
2054         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
2055             return;
2056         }
2057         
2058         if (this.isVisible()) {
2059             Roo.log('hide');
2060             this.hide();
2061         } else {
2062             Roo.log('show');
2063             this.show(this.triggerEl, false, false);
2064         }
2065         
2066         e.stopEvent();
2067     },
2068     
2069          
2070        
2071     
2072     hideMenuItems : function()
2073     {
2074         //$(backdrop).remove()
2075         Roo.select('.open',true).each(function(aa) {
2076             
2077             aa.removeClass('open');
2078           //var parent = getParent($(this))
2079           //var relatedTarget = { relatedTarget: this }
2080           
2081            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2082           //if (e.isDefaultPrevented()) return
2083            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2084         })
2085     },
2086     addxtypeChild : function (tree, cntr) {
2087         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2088           
2089         this.menuitems.add(comp);
2090         return comp;
2091
2092     },
2093     getEl : function()
2094     {
2095         Roo.log(this.el);
2096         return this.el;
2097     }
2098 });
2099
2100  
2101  /*
2102  * - LGPL
2103  *
2104  * menu item
2105  * 
2106  */
2107
2108
2109 /**
2110  * @class Roo.bootstrap.MenuItem
2111  * @extends Roo.bootstrap.Component
2112  * Bootstrap MenuItem class
2113  * @cfg {String} html the menu label
2114  * @cfg {String} href the link
2115  * @cfg {Boolean} preventDefault (true | false) default true
2116  * @cfg {Boolean} isContainer (true | false) default false
2117  * 
2118  * 
2119  * @constructor
2120  * Create a new MenuItem
2121  * @param {Object} config The config object
2122  */
2123
2124
2125 Roo.bootstrap.MenuItem = function(config){
2126     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2127     this.addEvents({
2128         // raw events
2129         /**
2130          * @event click
2131          * The raw click event for the entire grid.
2132          * @param {Roo.bootstrap.MenuItem} this
2133          * @param {Roo.EventObject} e
2134          */
2135         "click" : true
2136     });
2137 };
2138
2139 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2140     
2141     href : false,
2142     html : false,
2143     preventDefault: true,
2144     isContainer : false,
2145     
2146     getAutoCreate : function(){
2147         
2148         if(this.isContainer){
2149             return {
2150                 tag: 'li',
2151                 cls: 'dropdown-menu-item'
2152             };
2153         }
2154         
2155         var cfg= {
2156             tag: 'li',
2157             cls: 'dropdown-menu-item',
2158             cn: [
2159                     {
2160                         tag : 'a',
2161                         href : '#',
2162                         html : 'Link'
2163                     }
2164                 ]
2165         };
2166         if (this.parent().type == 'treeview') {
2167             cfg.cls = 'treeview-menu';
2168         }
2169         
2170         cfg.cn[0].href = this.href || cfg.cn[0].href ;
2171         cfg.cn[0].html = this.html || cfg.cn[0].html ;
2172         return cfg;
2173     },
2174     
2175     initEvents: function() {
2176         
2177         //this.el.select('a').on('click', this.onClick, this);
2178         
2179     },
2180     onClick : function(e)
2181     {
2182         Roo.log('item on click ');
2183         //if(this.preventDefault){
2184         //    e.preventDefault();
2185         //}
2186         //this.parent().hideMenuItems();
2187         
2188         this.fireEvent('click', this, e);
2189     },
2190     getEl : function()
2191     {
2192         return this.el;
2193     }
2194 });
2195
2196  
2197
2198  /*
2199  * - LGPL
2200  *
2201  * menu separator
2202  * 
2203  */
2204
2205
2206 /**
2207  * @class Roo.bootstrap.MenuSeparator
2208  * @extends Roo.bootstrap.Component
2209  * Bootstrap MenuSeparator class
2210  * 
2211  * @constructor
2212  * Create a new MenuItem
2213  * @param {Object} config The config object
2214  */
2215
2216
2217 Roo.bootstrap.MenuSeparator = function(config){
2218     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2219 };
2220
2221 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2222     
2223     getAutoCreate : function(){
2224         var cfg = {
2225             cls: 'divider',
2226             tag : 'li'
2227         };
2228         
2229         return cfg;
2230     }
2231    
2232 });
2233
2234  
2235
2236  
2237 /*
2238 * Licence: LGPL
2239 */
2240
2241 /**
2242  * @class Roo.bootstrap.Modal
2243  * @extends Roo.bootstrap.Component
2244  * Bootstrap Modal class
2245  * @cfg {String} title Title of dialog
2246  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2247  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn 
2248  * @cfg {Boolean} specificTitle default false
2249  * @cfg {Array} buttons Array of buttons or standard button set..
2250  * @cfg {String} buttonPosition (left|right|center) default right
2251  * @cfg {Boolean} animate default true
2252  * @cfg {Boolean} allow_close default true
2253  * 
2254  * @constructor
2255  * Create a new Modal Dialog
2256  * @param {Object} config The config object
2257  */
2258
2259 Roo.bootstrap.Modal = function(config){
2260     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2261     this.addEvents({
2262         // raw events
2263         /**
2264          * @event btnclick
2265          * The raw btnclick event for the button
2266          * @param {Roo.EventObject} e
2267          */
2268         "btnclick" : true
2269     });
2270     this.buttons = this.buttons || [];
2271      
2272     if (this.tmpl) {
2273         this.tmpl = Roo.factory(this.tmpl);
2274     }
2275     
2276 };
2277
2278 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2279     
2280     title : 'test dialog',
2281    
2282     buttons : false,
2283     
2284     // set on load...
2285      
2286     html: false,
2287     
2288     tmp: false,
2289     
2290     specificTitle: false,
2291     
2292     buttonPosition: 'right',
2293     
2294     allow_close : true,
2295     
2296     animate : true,
2297     
2298     
2299      // private
2300     bodyEl:  false,
2301     footerEl:  false,
2302     titleEl:  false,
2303     closeEl:  false,
2304     
2305     
2306     onRender : function(ct, position)
2307     {
2308         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2309      
2310         if(!this.el){
2311             var cfg = Roo.apply({},  this.getAutoCreate());
2312             cfg.id = Roo.id();
2313             //if(!cfg.name){
2314             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2315             //}
2316             //if (!cfg.name.length) {
2317             //    delete cfg.name;
2318            // }
2319             if (this.cls) {
2320                 cfg.cls += ' ' + this.cls;
2321             }
2322             if (this.style) {
2323                 cfg.style = this.style;
2324             }
2325             this.el = Roo.get(document.body).createChild(cfg, position);
2326         }
2327         //var type = this.el.dom.type;
2328         
2329         
2330         
2331         
2332         if(this.tabIndex !== undefined){
2333             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2334         }
2335         
2336         
2337         this.bodyEl = this.el.select('.modal-body',true).first();
2338         this.closeEl = this.el.select('.modal-header .close', true).first();
2339         this.footerEl = this.el.select('.modal-footer',true).first();
2340         this.titleEl = this.el.select('.modal-title',true).first();
2341         
2342         
2343          
2344         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2345         this.maskEl.enableDisplayMode("block");
2346         this.maskEl.hide();
2347         //this.el.addClass("x-dlg-modal");
2348     
2349         if (this.buttons.length) {
2350             Roo.each(this.buttons, function(bb) {
2351                 b = Roo.apply({}, bb);
2352                 b.xns = b.xns || Roo.bootstrap;
2353                 b.xtype = b.xtype || 'Button';
2354                 if (typeof(b.listeners) == 'undefined') {
2355                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2356                 }
2357                 
2358                 var btn = Roo.factory(b);
2359                 
2360                 btn.onRender(this.el.select('.modal-footer div').first());
2361                 
2362             },this);
2363         }
2364         // render the children.
2365         var nitems = [];
2366         
2367         if(typeof(this.items) != 'undefined'){
2368             var items = this.items;
2369             delete this.items;
2370
2371             for(var i =0;i < items.length;i++) {
2372                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2373             }
2374         }
2375         
2376         this.items = nitems;
2377         
2378         // where are these used - they used to be body/close/footer
2379         
2380        
2381         this.initEvents();
2382         //this.el.addClass([this.fieldClass, this.cls]);
2383         
2384     },
2385     getAutoCreate : function(){
2386         
2387         
2388         var bdy = {
2389                 cls : 'modal-body',
2390                 html : this.html || ''
2391         };
2392         
2393         var title = {
2394             tag: 'h4',
2395             cls : 'modal-title',
2396             html : this.title
2397         };
2398         
2399         if(this.specificTitle){
2400             title = this.title;
2401             
2402         };
2403         
2404         var header = [];
2405         if (this.allow_close) {
2406             header.push({
2407                 tag: 'button',
2408                 cls : 'close',
2409                 html : '&times'
2410             });
2411         }
2412         header.push(title);
2413         
2414         var modal = {
2415             cls: "modal",
2416             style : 'display: none',
2417             cn : [
2418                 {
2419                     cls: "modal-dialog",
2420                     cn : [
2421                         {
2422                             cls : "modal-content",
2423                             cn : [
2424                                 {
2425                                     cls : 'modal-header',
2426                                     cn : header
2427                                 },
2428                                 bdy,
2429                                 {
2430                                     cls : 'modal-footer',
2431                                     cn : [
2432                                         {
2433                                             tag: 'div',
2434                                             cls: 'btn-' + this.buttonPosition
2435                                         }
2436                                     ]
2437                                     
2438                                 }
2439                                 
2440                                 
2441                             ]
2442                             
2443                         }
2444                     ]
2445                         
2446                 }
2447             ]
2448         };
2449         
2450         if(this.animate){
2451             modal.cls += ' fade';
2452         }
2453         
2454         return modal;
2455           
2456     },
2457     getChildContainer : function() {
2458          
2459          return this.bodyEl;
2460         
2461     },
2462     getButtonContainer : function() {
2463          return this.el.select('.modal-footer div',true).first();
2464         
2465     },
2466     initEvents : function()
2467     {
2468         if (this.allow_close) {
2469             this.closeEl.on('click', this.hide, this);
2470         }
2471
2472     },
2473     show : function() {
2474         
2475         if (!this.rendered) {
2476             this.render();
2477         }
2478         
2479         this.el.setStyle('display', 'block');
2480         
2481         if(this.animate){
2482             var _this = this;
2483             (function(){ _this.el.addClass('in'); }).defer(50);
2484         }else{
2485             this.el.addClass('in');
2486         }
2487         
2488         // not sure how we can show data in here.. 
2489         //if (this.tmpl) {
2490         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2491         //}
2492         
2493         Roo.get(document.body).addClass("x-body-masked");
2494         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2495         this.maskEl.show();
2496         this.el.setStyle('zIndex', '10001');
2497        
2498         this.fireEvent('show', this);
2499         
2500         
2501     },
2502     hide : function()
2503     {
2504         this.maskEl.hide();
2505         Roo.get(document.body).removeClass("x-body-masked");
2506         this.el.removeClass('in');
2507         
2508         if(this.animate){
2509             var _this = this;
2510             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2511         }else{
2512             this.el.setStyle('display', 'none');
2513         }
2514         
2515         this.fireEvent('hide', this);
2516     },
2517     
2518     addButton : function(str, cb)
2519     {
2520          
2521         
2522         var b = Roo.apply({}, { html : str } );
2523         b.xns = b.xns || Roo.bootstrap;
2524         b.xtype = b.xtype || 'Button';
2525         if (typeof(b.listeners) == 'undefined') {
2526             b.listeners = { click : cb.createDelegate(this)  };
2527         }
2528         
2529         var btn = Roo.factory(b);
2530            
2531         btn.onRender(this.el.select('.modal-footer div').first());
2532         
2533         return btn;   
2534        
2535     },
2536     
2537     setDefaultButton : function(btn)
2538     {
2539         //this.el.select('.modal-footer').()
2540     },
2541     resizeTo: function(w,h)
2542     {
2543         // skip..
2544     },
2545     setContentSize  : function(w, h)
2546     {
2547         
2548     },
2549     onButtonClick: function(btn,e)
2550     {
2551         //Roo.log([a,b,c]);
2552         this.fireEvent('btnclick', btn.name, e);
2553     },
2554      /**
2555      * Set the title of the Dialog
2556      * @param {String} str new Title
2557      */
2558     setTitle: function(str) {
2559         this.titleEl.dom.innerHTML = str;    
2560     },
2561     /**
2562      * Set the body of the Dialog
2563      * @param {String} str new Title
2564      */
2565     setBody: function(str) {
2566         this.bodyEl.dom.innerHTML = str;    
2567     },
2568     /**
2569      * Set the body of the Dialog using the template
2570      * @param {Obj} data - apply this data to the template and replace the body contents.
2571      */
2572     applyBody: function(obj)
2573     {
2574         if (!this.tmpl) {
2575             Roo.log("Error - using apply Body without a template");
2576             //code
2577         }
2578         this.tmpl.overwrite(this.bodyEl, obj);
2579     }
2580     
2581 });
2582
2583
2584 Roo.apply(Roo.bootstrap.Modal,  {
2585     /**
2586          * Button config that displays a single OK button
2587          * @type Object
2588          */
2589         OK :  [{
2590             name : 'ok',
2591             weight : 'primary',
2592             html : 'OK'
2593         }], 
2594         /**
2595          * Button config that displays Yes and No buttons
2596          * @type Object
2597          */
2598         YESNO : [
2599             {
2600                 name  : 'no',
2601                 html : 'No'
2602             },
2603             {
2604                 name  :'yes',
2605                 weight : 'primary',
2606                 html : 'Yes'
2607             }
2608         ],
2609         
2610         /**
2611          * Button config that displays OK and Cancel buttons
2612          * @type Object
2613          */
2614         OKCANCEL : [
2615             {
2616                name : 'cancel',
2617                 html : 'Cancel'
2618             },
2619             {
2620                 name : 'ok',
2621                 weight : 'primary',
2622                 html : 'OK'
2623             }
2624         ],
2625         /**
2626          * Button config that displays Yes, No and Cancel buttons
2627          * @type Object
2628          */
2629         YESNOCANCEL : [
2630             {
2631                 name : 'yes',
2632                 weight : 'primary',
2633                 html : 'Yes'
2634             },
2635             {
2636                 name : 'no',
2637                 html : 'No'
2638             },
2639             {
2640                 name : 'cancel',
2641                 html : 'Cancel'
2642             }
2643         ]
2644 });
2645  
2646  /*
2647  * - LGPL
2648  *
2649  * messagebox - can be used as a replace
2650  * 
2651  */
2652 /**
2653  * @class Roo.MessageBox
2654  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2655  * Example usage:
2656  *<pre><code>
2657 // Basic alert:
2658 Roo.Msg.alert('Status', 'Changes saved successfully.');
2659
2660 // Prompt for user data:
2661 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2662     if (btn == 'ok'){
2663         // process text value...
2664     }
2665 });
2666
2667 // Show a dialog using config options:
2668 Roo.Msg.show({
2669    title:'Save Changes?',
2670    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2671    buttons: Roo.Msg.YESNOCANCEL,
2672    fn: processResult,
2673    animEl: 'elId'
2674 });
2675 </code></pre>
2676  * @singleton
2677  */
2678 Roo.bootstrap.MessageBox = function(){
2679     var dlg, opt, mask, waitTimer;
2680     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2681     var buttons, activeTextEl, bwidth;
2682
2683     
2684     // private
2685     var handleButton = function(button){
2686         dlg.hide();
2687         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2688     };
2689
2690     // private
2691     var handleHide = function(){
2692         if(opt && opt.cls){
2693             dlg.el.removeClass(opt.cls);
2694         }
2695         //if(waitTimer){
2696         //    Roo.TaskMgr.stop(waitTimer);
2697         //    waitTimer = null;
2698         //}
2699     };
2700
2701     // private
2702     var updateButtons = function(b){
2703         var width = 0;
2704         if(!b){
2705             buttons["ok"].hide();
2706             buttons["cancel"].hide();
2707             buttons["yes"].hide();
2708             buttons["no"].hide();
2709             //dlg.footer.dom.style.display = 'none';
2710             return width;
2711         }
2712         dlg.footerEl.dom.style.display = '';
2713         for(var k in buttons){
2714             if(typeof buttons[k] != "function"){
2715                 if(b[k]){
2716                     buttons[k].show();
2717                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2718                     width += buttons[k].el.getWidth()+15;
2719                 }else{
2720                     buttons[k].hide();
2721                 }
2722             }
2723         }
2724         return width;
2725     };
2726
2727     // private
2728     var handleEsc = function(d, k, e){
2729         if(opt && opt.closable !== false){
2730             dlg.hide();
2731         }
2732         if(e){
2733             e.stopEvent();
2734         }
2735     };
2736
2737     return {
2738         /**
2739          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2740          * @return {Roo.BasicDialog} The BasicDialog element
2741          */
2742         getDialog : function(){
2743            if(!dlg){
2744                 dlg = new Roo.bootstrap.Modal( {
2745                     //draggable: true,
2746                     //resizable:false,
2747                     //constraintoviewport:false,
2748                     //fixedcenter:true,
2749                     //collapsible : false,
2750                     //shim:true,
2751                     //modal: true,
2752                   //  width:400,
2753                   //  height:100,
2754                     //buttonAlign:"center",
2755                     closeClick : function(){
2756                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2757                             handleButton("no");
2758                         }else{
2759                             handleButton("cancel");
2760                         }
2761                     }
2762                 });
2763                 dlg.render();
2764                 dlg.on("hide", handleHide);
2765                 mask = dlg.mask;
2766                 //dlg.addKeyListener(27, handleEsc);
2767                 buttons = {};
2768                 this.buttons = buttons;
2769                 var bt = this.buttonText;
2770                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2771                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2772                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2773                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2774                 Roo.log(buttons)
2775                 bodyEl = dlg.bodyEl.createChild({
2776
2777                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2778                         '<textarea class="roo-mb-textarea"></textarea>' +
2779                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2780                 });
2781                 msgEl = bodyEl.dom.firstChild;
2782                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2783                 textboxEl.enableDisplayMode();
2784                 textboxEl.addKeyListener([10,13], function(){
2785                     if(dlg.isVisible() && opt && opt.buttons){
2786                         if(opt.buttons.ok){
2787                             handleButton("ok");
2788                         }else if(opt.buttons.yes){
2789                             handleButton("yes");
2790                         }
2791                     }
2792                 });
2793                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2794                 textareaEl.enableDisplayMode();
2795                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2796                 progressEl.enableDisplayMode();
2797                 var pf = progressEl.dom.firstChild;
2798                 if (pf) {
2799                     pp = Roo.get(pf.firstChild);
2800                     pp.setHeight(pf.offsetHeight);
2801                 }
2802                 
2803             }
2804             return dlg;
2805         },
2806
2807         /**
2808          * Updates the message box body text
2809          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2810          * the XHTML-compliant non-breaking space character '&amp;#160;')
2811          * @return {Roo.MessageBox} This message box
2812          */
2813         updateText : function(text){
2814             if(!dlg.isVisible() && !opt.width){
2815                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2816             }
2817             msgEl.innerHTML = text || '&#160;';
2818       
2819             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2820             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2821             var w = Math.max(
2822                     Math.min(opt.width || cw , this.maxWidth), 
2823                     Math.max(opt.minWidth || this.minWidth, bwidth)
2824             );
2825             if(opt.prompt){
2826                 activeTextEl.setWidth(w);
2827             }
2828             if(dlg.isVisible()){
2829                 dlg.fixedcenter = false;
2830             }
2831             // to big, make it scroll. = But as usual stupid IE does not support
2832             // !important..
2833             
2834             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2835                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2836                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2837             } else {
2838                 bodyEl.dom.style.height = '';
2839                 bodyEl.dom.style.overflowY = '';
2840             }
2841             if (cw > w) {
2842                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2843             } else {
2844                 bodyEl.dom.style.overflowX = '';
2845             }
2846             
2847             dlg.setContentSize(w, bodyEl.getHeight());
2848             if(dlg.isVisible()){
2849                 dlg.fixedcenter = true;
2850             }
2851             return this;
2852         },
2853
2854         /**
2855          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2856          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2857          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2858          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2859          * @return {Roo.MessageBox} This message box
2860          */
2861         updateProgress : function(value, text){
2862             if(text){
2863                 this.updateText(text);
2864             }
2865             if (pp) { // weird bug on my firefox - for some reason this is not defined
2866                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2867             }
2868             return this;
2869         },        
2870
2871         /**
2872          * Returns true if the message box is currently displayed
2873          * @return {Boolean} True if the message box is visible, else false
2874          */
2875         isVisible : function(){
2876             return dlg && dlg.isVisible();  
2877         },
2878
2879         /**
2880          * Hides the message box if it is displayed
2881          */
2882         hide : function(){
2883             if(this.isVisible()){
2884                 dlg.hide();
2885             }  
2886         },
2887
2888         /**
2889          * Displays a new message box, or reinitializes an existing message box, based on the config options
2890          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2891          * The following config object properties are supported:
2892          * <pre>
2893 Property    Type             Description
2894 ----------  ---------------  ------------------------------------------------------------------------------------
2895 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2896                                    closes (defaults to undefined)
2897 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2898                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2899 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2900                                    progress and wait dialogs will ignore this property and always hide the
2901                                    close button as they can only be closed programmatically.
2902 cls               String           A custom CSS class to apply to the message box element
2903 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2904                                    displayed (defaults to 75)
2905 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2906                                    function will be btn (the name of the button that was clicked, if applicable,
2907                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2908                                    Progress and wait dialogs will ignore this option since they do not respond to
2909                                    user actions and can only be closed programmatically, so any required function
2910                                    should be called by the same code after it closes the dialog.
2911 icon              String           A CSS class that provides a background image to be used as an icon for
2912                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2913 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2914 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2915 modal             Boolean          False to allow user interaction with the page while the message box is
2916                                    displayed (defaults to true)
2917 msg               String           A string that will replace the existing message box body text (defaults
2918                                    to the XHTML-compliant non-breaking space character '&#160;')
2919 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2920 progress          Boolean          True to display a progress bar (defaults to false)
2921 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2922 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2923 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2924 title             String           The title text
2925 value             String           The string value to set into the active textbox element if displayed
2926 wait              Boolean          True to display a progress bar (defaults to false)
2927 width             Number           The width of the dialog in pixels
2928 </pre>
2929          *
2930          * Example usage:
2931          * <pre><code>
2932 Roo.Msg.show({
2933    title: 'Address',
2934    msg: 'Please enter your address:',
2935    width: 300,
2936    buttons: Roo.MessageBox.OKCANCEL,
2937    multiline: true,
2938    fn: saveAddress,
2939    animEl: 'addAddressBtn'
2940 });
2941 </code></pre>
2942          * @param {Object} config Configuration options
2943          * @return {Roo.MessageBox} This message box
2944          */
2945         show : function(options)
2946         {
2947             
2948             // this causes nightmares if you show one dialog after another
2949             // especially on callbacks..
2950              
2951             if(this.isVisible()){
2952                 
2953                 this.hide();
2954                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2955                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2956                 Roo.log("New Dialog Message:" +  options.msg )
2957                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2958                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2959                 
2960             }
2961             var d = this.getDialog();
2962             opt = options;
2963             d.setTitle(opt.title || "&#160;");
2964             d.closeEl.setDisplayed(opt.closable !== false);
2965             activeTextEl = textboxEl;
2966             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2967             if(opt.prompt){
2968                 if(opt.multiline){
2969                     textboxEl.hide();
2970                     textareaEl.show();
2971                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2972                         opt.multiline : this.defaultTextHeight);
2973                     activeTextEl = textareaEl;
2974                 }else{
2975                     textboxEl.show();
2976                     textareaEl.hide();
2977                 }
2978             }else{
2979                 textboxEl.hide();
2980                 textareaEl.hide();
2981             }
2982             progressEl.setDisplayed(opt.progress === true);
2983             this.updateProgress(0);
2984             activeTextEl.dom.value = opt.value || "";
2985             if(opt.prompt){
2986                 dlg.setDefaultButton(activeTextEl);
2987             }else{
2988                 var bs = opt.buttons;
2989                 var db = null;
2990                 if(bs && bs.ok){
2991                     db = buttons["ok"];
2992                 }else if(bs && bs.yes){
2993                     db = buttons["yes"];
2994                 }
2995                 dlg.setDefaultButton(db);
2996             }
2997             bwidth = updateButtons(opt.buttons);
2998             this.updateText(opt.msg);
2999             if(opt.cls){
3000                 d.el.addClass(opt.cls);
3001             }
3002             d.proxyDrag = opt.proxyDrag === true;
3003             d.modal = opt.modal !== false;
3004             d.mask = opt.modal !== false ? mask : false;
3005             if(!d.isVisible()){
3006                 // force it to the end of the z-index stack so it gets a cursor in FF
3007                 document.body.appendChild(dlg.el.dom);
3008                 d.animateTarget = null;
3009                 d.show(options.animEl);
3010             }
3011             return this;
3012         },
3013
3014         /**
3015          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3016          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3017          * and closing the message box when the process is complete.
3018          * @param {String} title The title bar text
3019          * @param {String} msg The message box body text
3020          * @return {Roo.MessageBox} This message box
3021          */
3022         progress : function(title, msg){
3023             this.show({
3024                 title : title,
3025                 msg : msg,
3026                 buttons: false,
3027                 progress:true,
3028                 closable:false,
3029                 minWidth: this.minProgressWidth,
3030                 modal : true
3031             });
3032             return this;
3033         },
3034
3035         /**
3036          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3037          * If a callback function is passed it will be called after the user clicks the button, and the
3038          * id of the button that was clicked will be passed as the only parameter to the callback
3039          * (could also be the top-right close button).
3040          * @param {String} title The title bar text
3041          * @param {String} msg The message box body text
3042          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3043          * @param {Object} scope (optional) The scope of the callback function
3044          * @return {Roo.MessageBox} This message box
3045          */
3046         alert : function(title, msg, fn, scope){
3047             this.show({
3048                 title : title,
3049                 msg : msg,
3050                 buttons: this.OK,
3051                 fn: fn,
3052                 scope : scope,
3053                 modal : true
3054             });
3055             return this;
3056         },
3057
3058         /**
3059          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3060          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3061          * You are responsible for closing the message box when the process is complete.
3062          * @param {String} msg The message box body text
3063          * @param {String} title (optional) The title bar text
3064          * @return {Roo.MessageBox} This message box
3065          */
3066         wait : function(msg, title){
3067             this.show({
3068                 title : title,
3069                 msg : msg,
3070                 buttons: false,
3071                 closable:false,
3072                 progress:true,
3073                 modal:true,
3074                 width:300,
3075                 wait:true
3076             });
3077             waitTimer = Roo.TaskMgr.start({
3078                 run: function(i){
3079                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3080                 },
3081                 interval: 1000
3082             });
3083             return this;
3084         },
3085
3086         /**
3087          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3088          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3089          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3090          * @param {String} title The title bar text
3091          * @param {String} msg The message box body text
3092          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3093          * @param {Object} scope (optional) The scope of the callback function
3094          * @return {Roo.MessageBox} This message box
3095          */
3096         confirm : function(title, msg, fn, scope){
3097             this.show({
3098                 title : title,
3099                 msg : msg,
3100                 buttons: this.YESNO,
3101                 fn: fn,
3102                 scope : scope,
3103                 modal : true
3104             });
3105             return this;
3106         },
3107
3108         /**
3109          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3110          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3111          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3112          * (could also be the top-right close button) and the text that was entered will be passed as the two
3113          * parameters to the callback.
3114          * @param {String} title The title bar text
3115          * @param {String} msg The message box body text
3116          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3117          * @param {Object} scope (optional) The scope of the callback function
3118          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3119          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3120          * @return {Roo.MessageBox} This message box
3121          */
3122         prompt : function(title, msg, fn, scope, multiline){
3123             this.show({
3124                 title : title,
3125                 msg : msg,
3126                 buttons: this.OKCANCEL,
3127                 fn: fn,
3128                 minWidth:250,
3129                 scope : scope,
3130                 prompt:true,
3131                 multiline: multiline,
3132                 modal : true
3133             });
3134             return this;
3135         },
3136
3137         /**
3138          * Button config that displays a single OK button
3139          * @type Object
3140          */
3141         OK : {ok:true},
3142         /**
3143          * Button config that displays Yes and No buttons
3144          * @type Object
3145          */
3146         YESNO : {yes:true, no:true},
3147         /**
3148          * Button config that displays OK and Cancel buttons
3149          * @type Object
3150          */
3151         OKCANCEL : {ok:true, cancel:true},
3152         /**
3153          * Button config that displays Yes, No and Cancel buttons
3154          * @type Object
3155          */
3156         YESNOCANCEL : {yes:true, no:true, cancel:true},
3157
3158         /**
3159          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3160          * @type Number
3161          */
3162         defaultTextHeight : 75,
3163         /**
3164          * The maximum width in pixels of the message box (defaults to 600)
3165          * @type Number
3166          */
3167         maxWidth : 600,
3168         /**
3169          * The minimum width in pixels of the message box (defaults to 100)
3170          * @type Number
3171          */
3172         minWidth : 100,
3173         /**
3174          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3175          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3176          * @type Number
3177          */
3178         minProgressWidth : 250,
3179         /**
3180          * An object containing the default button text strings that can be overriden for localized language support.
3181          * Supported properties are: ok, cancel, yes and no.
3182          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3183          * @type Object
3184          */
3185         buttonText : {
3186             ok : "OK",
3187             cancel : "Cancel",
3188             yes : "Yes",
3189             no : "No"
3190         }
3191     };
3192 }();
3193
3194 /**
3195  * Shorthand for {@link Roo.MessageBox}
3196  */
3197 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3198 Roo.Msg = Roo.Msg || Roo.MessageBox;
3199 /*
3200  * - LGPL
3201  *
3202  * navbar
3203  * 
3204  */
3205
3206 /**
3207  * @class Roo.bootstrap.Navbar
3208  * @extends Roo.bootstrap.Component
3209  * Bootstrap Navbar class
3210
3211  * @constructor
3212  * Create a new Navbar
3213  * @param {Object} config The config object
3214  */
3215
3216
3217 Roo.bootstrap.Navbar = function(config){
3218     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3219     
3220 };
3221
3222 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3223     
3224     
3225    
3226     // private
3227     navItems : false,
3228     loadMask : false,
3229     
3230     
3231     getAutoCreate : function(){
3232         
3233         
3234         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3235         
3236     },
3237     
3238     initEvents :function ()
3239     {
3240         //Roo.log(this.el.select('.navbar-toggle',true));
3241         this.el.select('.navbar-toggle',true).on('click', function() {
3242            // Roo.log('click');
3243             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3244         }, this);
3245         
3246         var mark = {
3247             tag: "div",
3248             cls:"x-dlg-mask"
3249         }
3250         
3251         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3252         
3253         var size = this.el.getSize();
3254         this.maskEl.setSize(size.width, size.height);
3255         this.maskEl.enableDisplayMode("block");
3256         this.maskEl.hide();
3257         
3258         if(this.loadMask){
3259             this.maskEl.show();
3260         }
3261     },
3262     
3263     
3264     getChildContainer : function()
3265     {
3266         if (this.el.select('.collapse').getCount()) {
3267             return this.el.select('.collapse',true).first();
3268         }
3269         
3270         return this.el;
3271     },
3272     
3273     mask : function()
3274     {
3275         this.maskEl.show();
3276     },
3277     
3278     unmask : function()
3279     {
3280         this.maskEl.hide();
3281     } 
3282     
3283     
3284     
3285     
3286 });
3287
3288
3289
3290  
3291
3292  /*
3293  * - LGPL
3294  *
3295  * navbar
3296  * 
3297  */
3298
3299 /**
3300  * @class Roo.bootstrap.NavSimplebar
3301  * @extends Roo.bootstrap.Navbar
3302  * Bootstrap Sidebar class
3303  *
3304  * @cfg {Boolean} inverse is inverted color
3305  * 
3306  * @cfg {String} type (nav | pills | tabs)
3307  * @cfg {Boolean} arrangement stacked | justified
3308  * @cfg {String} align (left | right) alignment
3309  * 
3310  * @cfg {Boolean} main (true|false) main nav bar? default false
3311  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3312  * 
3313  * @cfg {String} tag (header|footer|nav|div) default is nav 
3314
3315  * 
3316  * 
3317  * 
3318  * @constructor
3319  * Create a new Sidebar
3320  * @param {Object} config The config object
3321  */
3322
3323
3324 Roo.bootstrap.NavSimplebar = function(config){
3325     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3326 };
3327
3328 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3329     
3330     inverse: false,
3331     
3332     type: false,
3333     arrangement: '',
3334     align : false,
3335     
3336     
3337     
3338     main : false,
3339     
3340     
3341     tag : false,
3342     
3343     
3344     getAutoCreate : function(){
3345         
3346         
3347         var cfg = {
3348             tag : this.tag || 'div',
3349             cls : 'navbar'
3350         };
3351           
3352         
3353         cfg.cn = [
3354             {
3355                 cls: 'nav',
3356                 tag : 'ul'
3357             }
3358         ];
3359         
3360          
3361         this.type = this.type || 'nav';
3362         if (['tabs','pills'].indexOf(this.type)!==-1) {
3363             cfg.cn[0].cls += ' nav-' + this.type
3364         
3365         
3366         } else {
3367             if (this.type!=='nav') {
3368                 Roo.log('nav type must be nav/tabs/pills')
3369             }
3370             cfg.cn[0].cls += ' navbar-nav'
3371         }
3372         
3373         
3374         
3375         
3376         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3377             cfg.cn[0].cls += ' nav-' + this.arrangement;
3378         }
3379         
3380         
3381         if (this.align === 'right') {
3382             cfg.cn[0].cls += ' navbar-right';
3383         }
3384         
3385         if (this.inverse) {
3386             cfg.cls += ' navbar-inverse';
3387             
3388         }
3389         
3390         
3391         return cfg;
3392     
3393         
3394     }
3395     
3396     
3397     
3398 });
3399
3400
3401
3402  
3403
3404  
3405        /*
3406  * - LGPL
3407  *
3408  * navbar
3409  * 
3410  */
3411
3412 /**
3413  * @class Roo.bootstrap.NavHeaderbar
3414  * @extends Roo.bootstrap.NavSimplebar
3415  * Bootstrap Sidebar class
3416  *
3417  * @cfg {String} brand what is brand
3418  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3419  * @cfg {String} brand_href href of the brand
3420  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3421  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3422  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3423  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3424  * 
3425  * @constructor
3426  * Create a new Sidebar
3427  * @param {Object} config The config object
3428  */
3429
3430
3431 Roo.bootstrap.NavHeaderbar = function(config){
3432     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3433       
3434 };
3435
3436 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3437     
3438     position: '',
3439     brand: '',
3440     brand_href: false,
3441     srButton : true,
3442     autohide : false,
3443     desktopCenter : false,
3444    
3445     
3446     getAutoCreate : function(){
3447         
3448         var   cfg = {
3449             tag: this.nav || 'nav',
3450             cls: 'navbar',
3451             role: 'navigation',
3452             cn: []
3453         };
3454         
3455         var cn = cfg.cn;
3456         if (this.desktopCenter) {
3457             cn.push({cls : 'container', cn : []});
3458             cn = cn[0].cn;
3459         }
3460         
3461         if(this.srButton){
3462             cn.push({
3463                 tag: 'div',
3464                 cls: 'navbar-header',
3465                 cn: [
3466                     {
3467                         tag: 'button',
3468                         type: 'button',
3469                         cls: 'navbar-toggle',
3470                         'data-toggle': 'collapse',
3471                         cn: [
3472                             {
3473                                 tag: 'span',
3474                                 cls: 'sr-only',
3475                                 html: 'Toggle navigation'
3476                             },
3477                             {
3478                                 tag: 'span',
3479                                 cls: 'icon-bar'
3480                             },
3481                             {
3482                                 tag: 'span',
3483                                 cls: 'icon-bar'
3484                             },
3485                             {
3486                                 tag: 'span',
3487                                 cls: 'icon-bar'
3488                             }
3489                         ]
3490                     }
3491                 ]
3492             });
3493         }
3494         
3495         cn.push({
3496             tag: 'div',
3497             cls: 'collapse navbar-collapse',
3498             cn : []
3499         });
3500         
3501         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3502         
3503         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3504             cfg.cls += ' navbar-' + this.position;
3505             
3506             // tag can override this..
3507             
3508             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3509         }
3510         
3511         if (this.brand !== '') {
3512             cn[0].cn.push({
3513                 tag: 'a',
3514                 href: this.brand_href ? this.brand_href : '#',
3515                 cls: 'navbar-brand',
3516                 cn: [
3517                 this.brand
3518                 ]
3519             });
3520         }
3521         
3522         if(this.main){
3523             cfg.cls += ' main-nav';
3524         }
3525         
3526         
3527         return cfg;
3528
3529         
3530     },
3531     getHeaderChildContainer : function()
3532     {
3533         if (this.el.select('.navbar-header').getCount()) {
3534             return this.el.select('.navbar-header',true).first();
3535         }
3536         
3537         return this.getChildContainer();
3538     },
3539     
3540     
3541     initEvents : function()
3542     {
3543         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3544         
3545         if (this.autohide) {
3546             
3547             var prevScroll = 0;
3548             var ft = this.el;
3549             
3550             Roo.get(document).on('scroll',function(e) {
3551                 var ns = Roo.get(document).getScroll().top;
3552                 var os = prevScroll;
3553                 prevScroll = ns;
3554                 
3555                 if(ns > os){
3556                     ft.removeClass('slideDown');
3557                     ft.addClass('slideUp');
3558                     return;
3559                 }
3560                 ft.removeClass('slideUp');
3561                 ft.addClass('slideDown');
3562                  
3563               
3564           },this);
3565         }
3566     }    
3567     
3568 });
3569
3570
3571
3572  
3573
3574  /*
3575  * - LGPL
3576  *
3577  * navbar
3578  * 
3579  */
3580
3581 /**
3582  * @class Roo.bootstrap.NavSidebar
3583  * @extends Roo.bootstrap.Navbar
3584  * Bootstrap Sidebar class
3585  * 
3586  * @constructor
3587  * Create a new Sidebar
3588  * @param {Object} config The config object
3589  */
3590
3591
3592 Roo.bootstrap.NavSidebar = function(config){
3593     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3594 };
3595
3596 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3597     
3598     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3599     
3600     getAutoCreate : function(){
3601         
3602         
3603         return  {
3604             tag: 'div',
3605             cls: 'sidebar sidebar-nav'
3606         };
3607     
3608         
3609     }
3610     
3611     
3612     
3613 });
3614
3615
3616
3617  
3618
3619  /*
3620  * - LGPL
3621  *
3622  * nav group
3623  * 
3624  */
3625
3626 /**
3627  * @class Roo.bootstrap.NavGroup
3628  * @extends Roo.bootstrap.Component
3629  * Bootstrap NavGroup class
3630  * @cfg {String} align (left|right)
3631  * @cfg {Boolean} inverse
3632  * @cfg {String} type (nav|pills|tab) default nav
3633  * @cfg {String} navId - reference Id for navbar.
3634
3635  * 
3636  * @constructor
3637  * Create a new nav group
3638  * @param {Object} config The config object
3639  */
3640
3641 Roo.bootstrap.NavGroup = function(config){
3642     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3643     this.navItems = [];
3644    
3645     Roo.bootstrap.NavGroup.register(this);
3646      this.addEvents({
3647         /**
3648              * @event changed
3649              * Fires when the active item changes
3650              * @param {Roo.bootstrap.NavGroup} this
3651              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3652              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3653          */
3654         'changed': true
3655      });
3656     
3657 };
3658
3659 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3660     
3661     align: '',
3662     inverse: false,
3663     form: false,
3664     type: 'nav',
3665     navId : '',
3666     // private
3667     
3668     navItems : false, 
3669     
3670     getAutoCreate : function()
3671     {
3672         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3673         
3674         cfg = {
3675             tag : 'ul',
3676             cls: 'nav' 
3677         }
3678         
3679         if (['tabs','pills'].indexOf(this.type)!==-1) {
3680             cfg.cls += ' nav-' + this.type
3681         } else {
3682             if (this.type!=='nav') {
3683                 Roo.log('nav type must be nav/tabs/pills')
3684             }
3685             cfg.cls += ' navbar-nav'
3686         }
3687         
3688         if (this.parent().sidebar) {
3689             cfg = {
3690                 tag: 'ul',
3691                 cls: 'dashboard-menu sidebar-menu'
3692             }
3693             
3694             return cfg;
3695         }
3696         
3697         if (this.form === true) {
3698             cfg = {
3699                 tag: 'form',
3700                 cls: 'navbar-form'
3701             }
3702             
3703             if (this.align === 'right') {
3704                 cfg.cls += ' navbar-right';
3705             } else {
3706                 cfg.cls += ' navbar-left';
3707             }
3708         }
3709         
3710         if (this.align === 'right') {
3711             cfg.cls += ' navbar-right';
3712         }
3713         
3714         if (this.inverse) {
3715             cfg.cls += ' navbar-inverse';
3716             
3717         }
3718         
3719         
3720         return cfg;
3721     },
3722     /**
3723     * sets the active Navigation item
3724     * @param {Roo.bootstrap.NavItem} the new current navitem
3725     */
3726     setActiveItem : function(item)
3727     {
3728         var prev = false;
3729         Roo.each(this.navItems, function(v){
3730             if (v == item) {
3731                 return ;
3732             }
3733             if (v.isActive()) {
3734                 v.setActive(false, true);
3735                 prev = v;
3736                 
3737             }
3738             
3739         });
3740
3741         item.setActive(true, true);
3742         this.fireEvent('changed', this, item, prev);
3743         
3744         
3745     },
3746     /**
3747     * gets the active Navigation item
3748     * @return {Roo.bootstrap.NavItem} the current navitem
3749     */
3750     getActive : function()
3751     {
3752         
3753         var prev = false;
3754         Roo.each(this.navItems, function(v){
3755             
3756             if (v.isActive()) {
3757                 prev = v;
3758                 
3759             }
3760             
3761         });
3762         return prev;
3763     },
3764     
3765     indexOfNav : function()
3766     {
3767         
3768         var prev = false;
3769         Roo.each(this.navItems, function(v,i){
3770             
3771             if (v.isActive()) {
3772                 prev = i;
3773                 
3774             }
3775             
3776         });
3777         return prev;
3778     },
3779     /**
3780     * adds a Navigation item
3781     * @param {Roo.bootstrap.NavItem} the navitem to add
3782     */
3783     addItem : function(cfg)
3784     {
3785         var cn = new Roo.bootstrap.NavItem(cfg);
3786         this.register(cn);
3787         cn.parentId = this.id;
3788         cn.onRender(this.el, null);
3789         return cn;
3790     },
3791     /**
3792     * register a Navigation item
3793     * @param {Roo.bootstrap.NavItem} the navitem to add
3794     */
3795     register : function(item)
3796     {
3797         this.navItems.push( item);
3798         item.navId = this.navId;
3799     
3800     },
3801     
3802     /**
3803     * clear all the Navigation item
3804     */
3805    
3806     clearAll : function()
3807     {
3808         this.navItems = [];
3809         this.el.dom.innerHTML = '';
3810     },
3811     
3812     getNavItem: function(tabId)
3813     {
3814         var ret = false;
3815         Roo.each(this.navItems, function(e) {
3816             if (e.tabId == tabId) {
3817                ret =  e;
3818                return false;
3819             }
3820             return true;
3821             
3822         });
3823         return ret;
3824     },
3825     
3826     setActiveNext : function()
3827     {
3828         var i = this.indexOfNav(this.getActive());
3829         if (i > this.navItems.length) {
3830             return;
3831         }
3832         this.setActiveItem(this.navItems[i+1]);
3833     },
3834     setActivePrev : function()
3835     {
3836         var i = this.indexOfNav(this.getActive());
3837         if (i  < 1) {
3838             return;
3839         }
3840         this.setActiveItem(this.navItems[i-1]);
3841     },
3842     clearWasActive : function(except) {
3843         Roo.each(this.navItems, function(e) {
3844             if (e.tabId != except.tabId && e.was_active) {
3845                e.was_active = false;
3846                return false;
3847             }
3848             return true;
3849             
3850         });
3851     },
3852     getWasActive : function ()
3853     {
3854         var r = false;
3855         Roo.each(this.navItems, function(e) {
3856             if (e.was_active) {
3857                r = e;
3858                return false;
3859             }
3860             return true;
3861             
3862         });
3863         return r;
3864     }
3865     
3866     
3867 });
3868
3869  
3870 Roo.apply(Roo.bootstrap.NavGroup, {
3871     
3872     groups: {},
3873      /**
3874     * register a Navigation Group
3875     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3876     */
3877     register : function(navgrp)
3878     {
3879         this.groups[navgrp.navId] = navgrp;
3880         
3881     },
3882     /**
3883     * fetch a Navigation Group based on the navigation ID
3884     * @param {string} the navgroup to add
3885     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3886     */
3887     get: function(navId) {
3888         if (typeof(this.groups[navId]) == 'undefined') {
3889             return false;
3890             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3891         }
3892         return this.groups[navId] ;
3893     }
3894     
3895     
3896     
3897 });
3898
3899  /*
3900  * - LGPL
3901  *
3902  * row
3903  * 
3904  */
3905
3906 /**
3907  * @class Roo.bootstrap.NavItem
3908  * @extends Roo.bootstrap.Component
3909  * Bootstrap Navbar.NavItem class
3910  * @cfg {String} href  link to
3911  * @cfg {String} html content of button
3912  * @cfg {String} badge text inside badge
3913  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3914  * @cfg {String} glyphicon name of glyphicon
3915  * @cfg {String} icon name of font awesome icon
3916  * @cfg {Boolean} active Is item active
3917  * @cfg {Boolean} disabled Is item disabled
3918  
3919  * @cfg {Boolean} preventDefault (true | false) default false
3920  * @cfg {String} tabId the tab that this item activates.
3921  * @cfg {String} tagtype (a|span) render as a href or span?
3922  * @cfg {Boolean} animateRef (true|false) link to element default false  
3923   
3924  * @constructor
3925  * Create a new Navbar Item
3926  * @param {Object} config The config object
3927  */
3928 Roo.bootstrap.NavItem = function(config){
3929     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3930     this.addEvents({
3931         // raw events
3932         /**
3933          * @event click
3934          * The raw click event for the entire grid.
3935          * @param {Roo.EventObject} e
3936          */
3937         "click" : true,
3938          /**
3939             * @event changed
3940             * Fires when the active item active state changes
3941             * @param {Roo.bootstrap.NavItem} this
3942             * @param {boolean} state the new state
3943              
3944          */
3945         'changed': true,
3946         /**
3947             * @event scrollto
3948             * Fires when scroll to element
3949             * @param {Roo.bootstrap.NavItem} this
3950             * @param {Object} options
3951             * @param {Roo.EventObject} e
3952              
3953          */
3954         'scrollto': true
3955     });
3956    
3957 };
3958
3959 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3960     
3961     href: false,
3962     html: '',
3963     badge: '',
3964     icon: false,
3965     glyphicon: false,
3966     active: false,
3967     preventDefault : false,
3968     tabId : false,
3969     tagtype : 'a',
3970     disabled : false,
3971     animateRef : false,
3972     was_active : false,
3973     
3974     getAutoCreate : function(){
3975          
3976         var cfg = {
3977             tag: 'li',
3978             cls: 'nav-item'
3979             
3980         }
3981         if (this.active) {
3982             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3983         }
3984         if (this.disabled) {
3985             cfg.cls += ' disabled';
3986         }
3987         
3988         if (this.href || this.html || this.glyphicon || this.icon) {
3989             cfg.cn = [
3990                 {
3991                     tag: this.tagtype,
3992                     href : this.href || "#",
3993                     html: this.html || ''
3994                 }
3995             ];
3996             
3997             if (this.icon) {
3998                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3999             }
4000
4001             if(this.glyphicon) {
4002                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4003             }
4004             
4005             if (this.menu) {
4006                 
4007                 cfg.cn[0].html += " <span class='caret'></span>";
4008              
4009             }
4010             
4011             if (this.badge !== '') {
4012                  
4013                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4014             }
4015         }
4016         
4017         
4018         
4019         return cfg;
4020     },
4021     initEvents: function() 
4022     {
4023         if (typeof (this.menu) != 'undefined') {
4024             this.menu.parentType = this.xtype;
4025             this.menu.triggerEl = this.el;
4026             this.menu = this.addxtype(Roo.apply({}, this.menu));
4027         }
4028         
4029         this.el.select('a',true).on('click', this.onClick, this);
4030         
4031         if(this.tagtype == 'span'){
4032             this.el.select('span',true).on('click', this.onClick, this);
4033         }
4034        
4035         // at this point parent should be available..
4036         this.parent().register(this);
4037     },
4038     
4039     onClick : function(e)
4040     {
4041         if(
4042                 this.preventDefault || 
4043                 this.href == '#' 
4044         ){
4045             
4046             e.preventDefault();
4047         }
4048         
4049         if (this.disabled) {
4050             return;
4051         }
4052         
4053         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4054         if (tg && tg.transition) {
4055             Roo.log("waiting for the transitionend");
4056             return;
4057         }
4058         
4059         
4060         
4061         //Roo.log("fire event clicked");
4062         if(this.fireEvent('click', this, e) === false){
4063             return;
4064         };
4065         
4066         if(this.tagtype == 'span'){
4067             return;
4068         }
4069         
4070         //Roo.log(this.href);
4071         var ael = this.el.select('a',true).first();
4072         //Roo.log(ael);
4073         
4074         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4075             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4076             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4077                 return; // ignore... - it's a 'hash' to another page.
4078             }
4079             
4080             e.preventDefault();
4081             this.scrollToElement(e);
4082         }
4083         
4084         
4085         var p =  this.parent();
4086    
4087         if (['tabs','pills'].indexOf(p.type)!==-1) {
4088             if (typeof(p.setActiveItem) !== 'undefined') {
4089                 p.setActiveItem(this);
4090             }
4091         }
4092         
4093         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4094         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4095             // remove the collapsed menu expand...
4096             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4097         }
4098     },
4099     
4100     isActive: function () {
4101         return this.active
4102     },
4103     setActive : function(state, fire, is_was_active)
4104     {
4105         if (this.active && !state & this.navId) {
4106             this.was_active = true;
4107             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4108             if (nv) {
4109                 nv.clearWasActive(this);
4110             }
4111             
4112         }
4113         this.active = state;
4114         
4115         if (!state ) {
4116             this.el.removeClass('active');
4117         } else if (!this.el.hasClass('active')) {
4118             this.el.addClass('active');
4119         }
4120         if (fire) {
4121             this.fireEvent('changed', this, state);
4122         }
4123         
4124         // show a panel if it's registered and related..
4125         
4126         if (!this.navId || !this.tabId || !state || is_was_active) {
4127             return;
4128         }
4129         
4130         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4131         if (!tg) {
4132             return;
4133         }
4134         var pan = tg.getPanelByName(this.tabId);
4135         if (!pan) {
4136             return;
4137         }
4138         // if we can not flip to new panel - go back to old nav highlight..
4139         if (false == tg.showPanel(pan)) {
4140             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4141             if (nv) {
4142                 var onav = nv.getWasActive();
4143                 if (onav) {
4144                     onav.setActive(true, false, true);
4145                 }
4146             }
4147             
4148         }
4149         
4150         
4151         
4152     },
4153      // this should not be here...
4154     setDisabled : function(state)
4155     {
4156         this.disabled = state;
4157         if (!state ) {
4158             this.el.removeClass('disabled');
4159         } else if (!this.el.hasClass('disabled')) {
4160             this.el.addClass('disabled');
4161         }
4162         
4163     },
4164     
4165     /**
4166      * Fetch the element to display the tooltip on.
4167      * @return {Roo.Element} defaults to this.el
4168      */
4169     tooltipEl : function()
4170     {
4171         return this.el.select('' + this.tagtype + '', true).first();
4172     },
4173     
4174     scrollToElement : function(e)
4175     {
4176         var c = document.body;
4177         
4178         /*
4179          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4180          */
4181         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4182             c = document.documentElement;
4183         }
4184         
4185         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4186         
4187         if(!target){
4188             return;
4189         }
4190
4191         var o = target.calcOffsetsTo(c);
4192         
4193         var options = {
4194             target : target,
4195             value : o[1]
4196         }
4197         
4198         this.fireEvent('scrollto', this, options, e);
4199         
4200         Roo.get(c).scrollTo('top', options.value, true);
4201         
4202         return;
4203     }
4204 });
4205  
4206
4207  /*
4208  * - LGPL
4209  *
4210  * sidebar item
4211  *
4212  *  li
4213  *    <span> icon </span>
4214  *    <span> text </span>
4215  *    <span>badge </span>
4216  */
4217
4218 /**
4219  * @class Roo.bootstrap.NavSidebarItem
4220  * @extends Roo.bootstrap.NavItem
4221  * Bootstrap Navbar.NavSidebarItem class
4222  * @constructor
4223  * Create a new Navbar Button
4224  * @param {Object} config The config object
4225  */
4226 Roo.bootstrap.NavSidebarItem = function(config){
4227     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4228     this.addEvents({
4229         // raw events
4230         /**
4231          * @event click
4232          * The raw click event for the entire grid.
4233          * @param {Roo.EventObject} e
4234          */
4235         "click" : true,
4236          /**
4237             * @event changed
4238             * Fires when the active item active state changes
4239             * @param {Roo.bootstrap.NavSidebarItem} this
4240             * @param {boolean} state the new state
4241              
4242          */
4243         'changed': true
4244     });
4245    
4246 };
4247
4248 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4249     
4250     
4251     getAutoCreate : function(){
4252         
4253         
4254         var a = {
4255                 tag: 'a',
4256                 href : this.href || '#',
4257                 cls: '',
4258                 html : '',
4259                 cn : []
4260         };
4261         var cfg = {
4262             tag: 'li',
4263             cls: '',
4264             cn: [ a ]
4265         }
4266         var span = {
4267             tag: 'span',
4268             html : this.html || ''
4269         }
4270         
4271         
4272         if (this.active) {
4273             cfg.cls += ' active';
4274         }
4275         
4276         // left icon..
4277         if (this.glyphicon || this.icon) {
4278             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4279             a.cn.push({ tag : 'i', cls : c }) ;
4280         }
4281         // html..
4282         a.cn.push(span);
4283         // then badge..
4284         if (this.badge !== '') {
4285             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
4286         }
4287         // fi
4288         if (this.menu) {
4289             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4290             a.cls += 'dropdown-toggle treeview' ;
4291             
4292         }
4293         
4294         
4295         
4296         return cfg;
4297          
4298            
4299     }
4300    
4301      
4302  
4303 });
4304  
4305
4306  /*
4307  * - LGPL
4308  *
4309  * row
4310  * 
4311  */
4312
4313 /**
4314  * @class Roo.bootstrap.Row
4315  * @extends Roo.bootstrap.Component
4316  * Bootstrap Row class (contains columns...)
4317  * 
4318  * @constructor
4319  * Create a new Row
4320  * @param {Object} config The config object
4321  */
4322
4323 Roo.bootstrap.Row = function(config){
4324     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4325 };
4326
4327 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4328     
4329     getAutoCreate : function(){
4330        return {
4331             cls: 'row clearfix'
4332        };
4333     }
4334     
4335     
4336 });
4337
4338  
4339
4340  /*
4341  * - LGPL
4342  *
4343  * element
4344  * 
4345  */
4346
4347 /**
4348  * @class Roo.bootstrap.Element
4349  * @extends Roo.bootstrap.Component
4350  * Bootstrap Element class
4351  * @cfg {String} html contents of the element
4352  * @cfg {String} tag tag of the element
4353  * @cfg {String} cls class of the element
4354  * @cfg {Boolean} preventDefault (true|false) default false
4355  * @cfg {Boolean} clickable (true|false) default false
4356  * 
4357  * @constructor
4358  * Create a new Element
4359  * @param {Object} config The config object
4360  */
4361
4362 Roo.bootstrap.Element = function(config){
4363     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4364     
4365     this.addEvents({
4366         // raw events
4367         /**
4368          * @event click
4369          * When a element is chick
4370          * @param {Roo.bootstrap.Element} this
4371          * @param {Roo.EventObject} e
4372          */
4373         "click" : true
4374     });
4375 };
4376
4377 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4378     
4379     tag: 'div',
4380     cls: '',
4381     html: '',
4382     preventDefault: false, 
4383     clickable: false,
4384     
4385     getAutoCreate : function(){
4386         
4387         var cfg = {
4388             tag: this.tag,
4389             cls: this.cls,
4390             html: this.html
4391         }
4392         
4393         return cfg;
4394     },
4395     
4396     initEvents: function() 
4397     {
4398         Roo.bootstrap.Element.superclass.initEvents.call(this);
4399         
4400         if(this.clickable){
4401             this.el.on('click', this.onClick, this);
4402         }
4403         
4404     },
4405     
4406     onClick : function(e)
4407     {
4408         if(this.preventDefault){
4409             e.preventDefault();
4410         }
4411         
4412         this.fireEvent('click', this, e);
4413     },
4414     
4415     getValue : function()
4416     {
4417         return this.el.dom.innerHTML;
4418     },
4419     
4420     setValue : function(value)
4421     {
4422         this.el.dom.innerHTML = value;
4423     }
4424    
4425 });
4426
4427  
4428
4429  /*
4430  * - LGPL
4431  *
4432  * pagination
4433  * 
4434  */
4435
4436 /**
4437  * @class Roo.bootstrap.Pagination
4438  * @extends Roo.bootstrap.Component
4439  * Bootstrap Pagination class
4440  * @cfg {String} size xs | sm | md | lg
4441  * @cfg {Boolean} inverse false | true
4442  * 
4443  * @constructor
4444  * Create a new Pagination
4445  * @param {Object} config The config object
4446  */
4447
4448 Roo.bootstrap.Pagination = function(config){
4449     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4450 };
4451
4452 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4453     
4454     cls: false,
4455     size: false,
4456     inverse: false,
4457     
4458     getAutoCreate : function(){
4459         var cfg = {
4460             tag: 'ul',
4461                 cls: 'pagination'
4462         };
4463         if (this.inverse) {
4464             cfg.cls += ' inverse';
4465         }
4466         if (this.html) {
4467             cfg.html=this.html;
4468         }
4469         if (this.cls) {
4470             cfg.cls += " " + this.cls;
4471         }
4472         return cfg;
4473     }
4474    
4475 });
4476
4477  
4478
4479  /*
4480  * - LGPL
4481  *
4482  * Pagination item
4483  * 
4484  */
4485
4486
4487 /**
4488  * @class Roo.bootstrap.PaginationItem
4489  * @extends Roo.bootstrap.Component
4490  * Bootstrap PaginationItem class
4491  * @cfg {String} html text
4492  * @cfg {String} href the link
4493  * @cfg {Boolean} preventDefault (true | false) default true
4494  * @cfg {Boolean} active (true | false) default false
4495  * @cfg {Boolean} disabled default false
4496  * 
4497  * 
4498  * @constructor
4499  * Create a new PaginationItem
4500  * @param {Object} config The config object
4501  */
4502
4503
4504 Roo.bootstrap.PaginationItem = function(config){
4505     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4506     this.addEvents({
4507         // raw events
4508         /**
4509          * @event click
4510          * The raw click event for the entire grid.
4511          * @param {Roo.EventObject} e
4512          */
4513         "click" : true
4514     });
4515 };
4516
4517 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4518     
4519     href : false,
4520     html : false,
4521     preventDefault: true,
4522     active : false,
4523     cls : false,
4524     disabled: false,
4525     
4526     getAutoCreate : function(){
4527         var cfg= {
4528             tag: 'li',
4529             cn: [
4530                 {
4531                     tag : 'a',
4532                     href : this.href ? this.href : '#',
4533                     html : this.html ? this.html : ''
4534                 }
4535             ]
4536         };
4537         
4538         if(this.cls){
4539             cfg.cls = this.cls;
4540         }
4541         
4542         if(this.disabled){
4543             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4544         }
4545         
4546         if(this.active){
4547             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4548         }
4549         
4550         return cfg;
4551     },
4552     
4553     initEvents: function() {
4554         
4555         this.el.on('click', this.onClick, this);
4556         
4557     },
4558     onClick : function(e)
4559     {
4560         Roo.log('PaginationItem on click ');
4561         if(this.preventDefault){
4562             e.preventDefault();
4563         }
4564         
4565         if(this.disabled){
4566             return;
4567         }
4568         
4569         this.fireEvent('click', this, e);
4570     }
4571    
4572 });
4573
4574  
4575
4576  /*
4577  * - LGPL
4578  *
4579  * slider
4580  * 
4581  */
4582
4583
4584 /**
4585  * @class Roo.bootstrap.Slider
4586  * @extends Roo.bootstrap.Component
4587  * Bootstrap Slider class
4588  *    
4589  * @constructor
4590  * Create a new Slider
4591  * @param {Object} config The config object
4592  */
4593
4594 Roo.bootstrap.Slider = function(config){
4595     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4596 };
4597
4598 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4599     
4600     getAutoCreate : function(){
4601         
4602         var cfg = {
4603             tag: 'div',
4604             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4605             cn: [
4606                 {
4607                     tag: 'a',
4608                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4609                 }
4610             ]
4611         }
4612         
4613         return cfg;
4614     }
4615    
4616 });
4617
4618  /*
4619  * Based on:
4620  * Ext JS Library 1.1.1
4621  * Copyright(c) 2006-2007, Ext JS, LLC.
4622  *
4623  * Originally Released Under LGPL - original licence link has changed is not relivant.
4624  *
4625  * Fork - LGPL
4626  * <script type="text/javascript">
4627  */
4628  
4629
4630 /**
4631  * @class Roo.grid.ColumnModel
4632  * @extends Roo.util.Observable
4633  * This is the default implementation of a ColumnModel used by the Grid. It defines
4634  * the columns in the grid.
4635  * <br>Usage:<br>
4636  <pre><code>
4637  var colModel = new Roo.grid.ColumnModel([
4638         {header: "Ticker", width: 60, sortable: true, locked: true},
4639         {header: "Company Name", width: 150, sortable: true},
4640         {header: "Market Cap.", width: 100, sortable: true},
4641         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4642         {header: "Employees", width: 100, sortable: true, resizable: false}
4643  ]);
4644  </code></pre>
4645  * <p>
4646  
4647  * The config options listed for this class are options which may appear in each
4648  * individual column definition.
4649  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4650  * @constructor
4651  * @param {Object} config An Array of column config objects. See this class's
4652  * config objects for details.
4653 */
4654 Roo.grid.ColumnModel = function(config){
4655         /**
4656      * The config passed into the constructor
4657      */
4658     this.config = config;
4659     this.lookup = {};
4660
4661     // if no id, create one
4662     // if the column does not have a dataIndex mapping,
4663     // map it to the order it is in the config
4664     for(var i = 0, len = config.length; i < len; i++){
4665         var c = config[i];
4666         if(typeof c.dataIndex == "undefined"){
4667             c.dataIndex = i;
4668         }
4669         if(typeof c.renderer == "string"){
4670             c.renderer = Roo.util.Format[c.renderer];
4671         }
4672         if(typeof c.id == "undefined"){
4673             c.id = Roo.id();
4674         }
4675         if(c.editor && c.editor.xtype){
4676             c.editor  = Roo.factory(c.editor, Roo.grid);
4677         }
4678         if(c.editor && c.editor.isFormField){
4679             c.editor = new Roo.grid.GridEditor(c.editor);
4680         }
4681         this.lookup[c.id] = c;
4682     }
4683
4684     /**
4685      * The width of columns which have no width specified (defaults to 100)
4686      * @type Number
4687      */
4688     this.defaultWidth = 100;
4689
4690     /**
4691      * Default sortable of columns which have no sortable specified (defaults to false)
4692      * @type Boolean
4693      */
4694     this.defaultSortable = false;
4695
4696     this.addEvents({
4697         /**
4698              * @event widthchange
4699              * Fires when the width of a column changes.
4700              * @param {ColumnModel} this
4701              * @param {Number} columnIndex The column index
4702              * @param {Number} newWidth The new width
4703              */
4704             "widthchange": true,
4705         /**
4706              * @event headerchange
4707              * Fires when the text of a header changes.
4708              * @param {ColumnModel} this
4709              * @param {Number} columnIndex The column index
4710              * @param {Number} newText The new header text
4711              */
4712             "headerchange": true,
4713         /**
4714              * @event hiddenchange
4715              * Fires when a column is hidden or "unhidden".
4716              * @param {ColumnModel} this
4717              * @param {Number} columnIndex The column index
4718              * @param {Boolean} hidden true if hidden, false otherwise
4719              */
4720             "hiddenchange": true,
4721             /**
4722          * @event columnmoved
4723          * Fires when a column is moved.
4724          * @param {ColumnModel} this
4725          * @param {Number} oldIndex
4726          * @param {Number} newIndex
4727          */
4728         "columnmoved" : true,
4729         /**
4730          * @event columlockchange
4731          * Fires when a column's locked state is changed
4732          * @param {ColumnModel} this
4733          * @param {Number} colIndex
4734          * @param {Boolean} locked true if locked
4735          */
4736         "columnlockchange" : true
4737     });
4738     Roo.grid.ColumnModel.superclass.constructor.call(this);
4739 };
4740 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4741     /**
4742      * @cfg {String} header The header text to display in the Grid view.
4743      */
4744     /**
4745      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4746      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4747      * specified, the column's index is used as an index into the Record's data Array.
4748      */
4749     /**
4750      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4751      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4752      */
4753     /**
4754      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4755      * Defaults to the value of the {@link #defaultSortable} property.
4756      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4757      */
4758     /**
4759      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4760      */
4761     /**
4762      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4763      */
4764     /**
4765      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4766      */
4767     /**
4768      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4769      */
4770     /**
4771      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4772      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4773      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4774      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4775      */
4776        /**
4777      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4778      */
4779     /**
4780      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4781      */
4782     /**
4783      * @cfg {String} cursor (Optional)
4784      */
4785     /**
4786      * @cfg {String} tooltip (Optional)
4787      */
4788     /**
4789      * Returns the id of the column at the specified index.
4790      * @param {Number} index The column index
4791      * @return {String} the id
4792      */
4793     getColumnId : function(index){
4794         return this.config[index].id;
4795     },
4796
4797     /**
4798      * Returns the column for a specified id.
4799      * @param {String} id The column id
4800      * @return {Object} the column
4801      */
4802     getColumnById : function(id){
4803         return this.lookup[id];
4804     },
4805
4806     
4807     /**
4808      * Returns the column for a specified dataIndex.
4809      * @param {String} dataIndex The column dataIndex
4810      * @return {Object|Boolean} the column or false if not found
4811      */
4812     getColumnByDataIndex: function(dataIndex){
4813         var index = this.findColumnIndex(dataIndex);
4814         return index > -1 ? this.config[index] : false;
4815     },
4816     
4817     /**
4818      * Returns the index for a specified column id.
4819      * @param {String} id The column id
4820      * @return {Number} the index, or -1 if not found
4821      */
4822     getIndexById : function(id){
4823         for(var i = 0, len = this.config.length; i < len; i++){
4824             if(this.config[i].id == id){
4825                 return i;
4826             }
4827         }
4828         return -1;
4829     },
4830     
4831     /**
4832      * Returns the index for a specified column dataIndex.
4833      * @param {String} dataIndex The column dataIndex
4834      * @return {Number} the index, or -1 if not found
4835      */
4836     
4837     findColumnIndex : function(dataIndex){
4838         for(var i = 0, len = this.config.length; i < len; i++){
4839             if(this.config[i].dataIndex == dataIndex){
4840                 return i;
4841             }
4842         }
4843         return -1;
4844     },
4845     
4846     
4847     moveColumn : function(oldIndex, newIndex){
4848         var c = this.config[oldIndex];
4849         this.config.splice(oldIndex, 1);
4850         this.config.splice(newIndex, 0, c);
4851         this.dataMap = null;
4852         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4853     },
4854
4855     isLocked : function(colIndex){
4856         return this.config[colIndex].locked === true;
4857     },
4858
4859     setLocked : function(colIndex, value, suppressEvent){
4860         if(this.isLocked(colIndex) == value){
4861             return;
4862         }
4863         this.config[colIndex].locked = value;
4864         if(!suppressEvent){
4865             this.fireEvent("columnlockchange", this, colIndex, value);
4866         }
4867     },
4868
4869     getTotalLockedWidth : function(){
4870         var totalWidth = 0;
4871         for(var i = 0; i < this.config.length; i++){
4872             if(this.isLocked(i) && !this.isHidden(i)){
4873                 this.totalWidth += this.getColumnWidth(i);
4874             }
4875         }
4876         return totalWidth;
4877     },
4878
4879     getLockedCount : function(){
4880         for(var i = 0, len = this.config.length; i < len; i++){
4881             if(!this.isLocked(i)){
4882                 return i;
4883             }
4884         }
4885     },
4886
4887     /**
4888      * Returns the number of columns.
4889      * @return {Number}
4890      */
4891     getColumnCount : function(visibleOnly){
4892         if(visibleOnly === true){
4893             var c = 0;
4894             for(var i = 0, len = this.config.length; i < len; i++){
4895                 if(!this.isHidden(i)){
4896                     c++;
4897                 }
4898             }
4899             return c;
4900         }
4901         return this.config.length;
4902     },
4903
4904     /**
4905      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4906      * @param {Function} fn
4907      * @param {Object} scope (optional)
4908      * @return {Array} result
4909      */
4910     getColumnsBy : function(fn, scope){
4911         var r = [];
4912         for(var i = 0, len = this.config.length; i < len; i++){
4913             var c = this.config[i];
4914             if(fn.call(scope||this, c, i) === true){
4915                 r[r.length] = c;
4916             }
4917         }
4918         return r;
4919     },
4920
4921     /**
4922      * Returns true if the specified column is sortable.
4923      * @param {Number} col The column index
4924      * @return {Boolean}
4925      */
4926     isSortable : function(col){
4927         if(typeof this.config[col].sortable == "undefined"){
4928             return this.defaultSortable;
4929         }
4930         return this.config[col].sortable;
4931     },
4932
4933     /**
4934      * Returns the rendering (formatting) function defined for the column.
4935      * @param {Number} col The column index.
4936      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4937      */
4938     getRenderer : function(col){
4939         if(!this.config[col].renderer){
4940             return Roo.grid.ColumnModel.defaultRenderer;
4941         }
4942         return this.config[col].renderer;
4943     },
4944
4945     /**
4946      * Sets the rendering (formatting) function for a column.
4947      * @param {Number} col The column index
4948      * @param {Function} fn The function to use to process the cell's raw data
4949      * to return HTML markup for the grid view. The render function is called with
4950      * the following parameters:<ul>
4951      * <li>Data value.</li>
4952      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4953      * <li>css A CSS style string to apply to the table cell.</li>
4954      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4955      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4956      * <li>Row index</li>
4957      * <li>Column index</li>
4958      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4959      */
4960     setRenderer : function(col, fn){
4961         this.config[col].renderer = fn;
4962     },
4963
4964     /**
4965      * Returns the width for the specified column.
4966      * @param {Number} col The column index
4967      * @return {Number}
4968      */
4969     getColumnWidth : function(col){
4970         return this.config[col].width * 1 || this.defaultWidth;
4971     },
4972
4973     /**
4974      * Sets the width for a column.
4975      * @param {Number} col The column index
4976      * @param {Number} width The new width
4977      */
4978     setColumnWidth : function(col, width, suppressEvent){
4979         this.config[col].width = width;
4980         this.totalWidth = null;
4981         if(!suppressEvent){
4982              this.fireEvent("widthchange", this, col, width);
4983         }
4984     },
4985
4986     /**
4987      * Returns the total width of all columns.
4988      * @param {Boolean} includeHidden True to include hidden column widths
4989      * @return {Number}
4990      */
4991     getTotalWidth : function(includeHidden){
4992         if(!this.totalWidth){
4993             this.totalWidth = 0;
4994             for(var i = 0, len = this.config.length; i < len; i++){
4995                 if(includeHidden || !this.isHidden(i)){
4996                     this.totalWidth += this.getColumnWidth(i);
4997                 }
4998             }
4999         }
5000         return this.totalWidth;
5001     },
5002
5003     /**
5004      * Returns the header for the specified column.
5005      * @param {Number} col The column index
5006      * @return {String}
5007      */
5008     getColumnHeader : function(col){
5009         return this.config[col].header;
5010     },
5011
5012     /**
5013      * Sets the header for a column.
5014      * @param {Number} col The column index
5015      * @param {String} header The new header
5016      */
5017     setColumnHeader : function(col, header){
5018         this.config[col].header = header;
5019         this.fireEvent("headerchange", this, col, header);
5020     },
5021
5022     /**
5023      * Returns the tooltip for the specified column.
5024      * @param {Number} col The column index
5025      * @return {String}
5026      */
5027     getColumnTooltip : function(col){
5028             return this.config[col].tooltip;
5029     },
5030     /**
5031      * Sets the tooltip for a column.
5032      * @param {Number} col The column index
5033      * @param {String} tooltip The new tooltip
5034      */
5035     setColumnTooltip : function(col, tooltip){
5036             this.config[col].tooltip = tooltip;
5037     },
5038
5039     /**
5040      * Returns the dataIndex for the specified column.
5041      * @param {Number} col The column index
5042      * @return {Number}
5043      */
5044     getDataIndex : function(col){
5045         return this.config[col].dataIndex;
5046     },
5047
5048     /**
5049      * Sets the dataIndex for a column.
5050      * @param {Number} col The column index
5051      * @param {Number} dataIndex The new dataIndex
5052      */
5053     setDataIndex : function(col, dataIndex){
5054         this.config[col].dataIndex = dataIndex;
5055     },
5056
5057     
5058     
5059     /**
5060      * Returns true if the cell is editable.
5061      * @param {Number} colIndex The column index
5062      * @param {Number} rowIndex The row index
5063      * @return {Boolean}
5064      */
5065     isCellEditable : function(colIndex, rowIndex){
5066         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5067     },
5068
5069     /**
5070      * Returns the editor defined for the cell/column.
5071      * return false or null to disable editing.
5072      * @param {Number} colIndex The column index
5073      * @param {Number} rowIndex The row index
5074      * @return {Object}
5075      */
5076     getCellEditor : function(colIndex, rowIndex){
5077         return this.config[colIndex].editor;
5078     },
5079
5080     /**
5081      * Sets if a column is editable.
5082      * @param {Number} col The column index
5083      * @param {Boolean} editable True if the column is editable
5084      */
5085     setEditable : function(col, editable){
5086         this.config[col].editable = editable;
5087     },
5088
5089
5090     /**
5091      * Returns true if the column is hidden.
5092      * @param {Number} colIndex The column index
5093      * @return {Boolean}
5094      */
5095     isHidden : function(colIndex){
5096         return this.config[colIndex].hidden;
5097     },
5098
5099
5100     /**
5101      * Returns true if the column width cannot be changed
5102      */
5103     isFixed : function(colIndex){
5104         return this.config[colIndex].fixed;
5105     },
5106
5107     /**
5108      * Returns true if the column can be resized
5109      * @return {Boolean}
5110      */
5111     isResizable : function(colIndex){
5112         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5113     },
5114     /**
5115      * Sets if a column is hidden.
5116      * @param {Number} colIndex The column index
5117      * @param {Boolean} hidden True if the column is hidden
5118      */
5119     setHidden : function(colIndex, hidden){
5120         this.config[colIndex].hidden = hidden;
5121         this.totalWidth = null;
5122         this.fireEvent("hiddenchange", this, colIndex, hidden);
5123     },
5124
5125     /**
5126      * Sets the editor for a column.
5127      * @param {Number} col The column index
5128      * @param {Object} editor The editor object
5129      */
5130     setEditor : function(col, editor){
5131         this.config[col].editor = editor;
5132     }
5133 });
5134
5135 Roo.grid.ColumnModel.defaultRenderer = function(value){
5136         if(typeof value == "string" && value.length < 1){
5137             return "&#160;";
5138         }
5139         return value;
5140 };
5141
5142 // Alias for backwards compatibility
5143 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5144 /*
5145  * Based on:
5146  * Ext JS Library 1.1.1
5147  * Copyright(c) 2006-2007, Ext JS, LLC.
5148  *
5149  * Originally Released Under LGPL - original licence link has changed is not relivant.
5150  *
5151  * Fork - LGPL
5152  * <script type="text/javascript">
5153  */
5154  
5155 /**
5156  * @class Roo.LoadMask
5157  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5158  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5159  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5160  * element's UpdateManager load indicator and will be destroyed after the initial load.
5161  * @constructor
5162  * Create a new LoadMask
5163  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5164  * @param {Object} config The config object
5165  */
5166 Roo.LoadMask = function(el, config){
5167     this.el = Roo.get(el);
5168     Roo.apply(this, config);
5169     if(this.store){
5170         this.store.on('beforeload', this.onBeforeLoad, this);
5171         this.store.on('load', this.onLoad, this);
5172         this.store.on('loadexception', this.onLoadException, this);
5173         this.removeMask = false;
5174     }else{
5175         var um = this.el.getUpdateManager();
5176         um.showLoadIndicator = false; // disable the default indicator
5177         um.on('beforeupdate', this.onBeforeLoad, this);
5178         um.on('update', this.onLoad, this);
5179         um.on('failure', this.onLoad, this);
5180         this.removeMask = true;
5181     }
5182 };
5183
5184 Roo.LoadMask.prototype = {
5185     /**
5186      * @cfg {Boolean} removeMask
5187      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5188      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5189      */
5190     /**
5191      * @cfg {String} msg
5192      * The text to display in a centered loading message box (defaults to 'Loading...')
5193      */
5194     msg : 'Loading...',
5195     /**
5196      * @cfg {String} msgCls
5197      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5198      */
5199     msgCls : 'x-mask-loading',
5200
5201     /**
5202      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5203      * @type Boolean
5204      */
5205     disabled: false,
5206
5207     /**
5208      * Disables the mask to prevent it from being displayed
5209      */
5210     disable : function(){
5211        this.disabled = true;
5212     },
5213
5214     /**
5215      * Enables the mask so that it can be displayed
5216      */
5217     enable : function(){
5218         this.disabled = false;
5219     },
5220     
5221     onLoadException : function()
5222     {
5223         Roo.log(arguments);
5224         
5225         if (typeof(arguments[3]) != 'undefined') {
5226             Roo.MessageBox.alert("Error loading",arguments[3]);
5227         } 
5228         /*
5229         try {
5230             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5231                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5232             }   
5233         } catch(e) {
5234             
5235         }
5236         */
5237     
5238         
5239         
5240         this.el.unmask(this.removeMask);
5241     },
5242     // private
5243     onLoad : function()
5244     {
5245         this.el.unmask(this.removeMask);
5246     },
5247
5248     // private
5249     onBeforeLoad : function(){
5250         if(!this.disabled){
5251             this.el.mask(this.msg, this.msgCls);
5252         }
5253     },
5254
5255     // private
5256     destroy : function(){
5257         if(this.store){
5258             this.store.un('beforeload', this.onBeforeLoad, this);
5259             this.store.un('load', this.onLoad, this);
5260             this.store.un('loadexception', this.onLoadException, this);
5261         }else{
5262             var um = this.el.getUpdateManager();
5263             um.un('beforeupdate', this.onBeforeLoad, this);
5264             um.un('update', this.onLoad, this);
5265             um.un('failure', this.onLoad, this);
5266         }
5267     }
5268 };/*
5269  * - LGPL
5270  *
5271  * table
5272  * 
5273  */
5274
5275 /**
5276  * @class Roo.bootstrap.Table
5277  * @extends Roo.bootstrap.Component
5278  * Bootstrap Table class
5279  * @cfg {String} cls table class
5280  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5281  * @cfg {String} bgcolor Specifies the background color for a table
5282  * @cfg {Number} border Specifies whether the table cells should have borders or not
5283  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5284  * @cfg {Number} cellspacing Specifies the space between cells
5285  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5286  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5287  * @cfg {String} sortable Specifies that the table should be sortable
5288  * @cfg {String} summary Specifies a summary of the content of a table
5289  * @cfg {Number} width Specifies the width of a table
5290  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5291  * 
5292  * @cfg {boolean} striped Should the rows be alternative striped
5293  * @cfg {boolean} bordered Add borders to the table
5294  * @cfg {boolean} hover Add hover highlighting
5295  * @cfg {boolean} condensed Format condensed
5296  * @cfg {boolean} responsive Format condensed
5297  * @cfg {Boolean} loadMask (true|false) default false
5298  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
5299  * @cfg {Boolean} thead (true|false) generate thead, default true
5300  * @cfg {Boolean} RowSelection (true|false) default false
5301  * @cfg {Boolean} CellSelection (true|false) default false
5302  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5303  
5304  * 
5305  * @constructor
5306  * Create a new Table
5307  * @param {Object} config The config object
5308  */
5309
5310 Roo.bootstrap.Table = function(config){
5311     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5312     
5313     if (this.sm) {
5314         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5315         this.sm = this.selModel;
5316         this.sm.xmodule = this.xmodule || false;
5317     }
5318     if (this.cm && typeof(this.cm.config) == 'undefined') {
5319         this.colModel = new Roo.grid.ColumnModel(this.cm);
5320         this.cm = this.colModel;
5321         this.cm.xmodule = this.xmodule || false;
5322     }
5323     if (this.store) {
5324         this.store= Roo.factory(this.store, Roo.data);
5325         this.ds = this.store;
5326         this.ds.xmodule = this.xmodule || false;
5327          
5328     }
5329     if (this.footer && this.store) {
5330         this.footer.dataSource = this.ds;
5331         this.footer = Roo.factory(this.footer);
5332     }
5333     
5334     /** @private */
5335     this.addEvents({
5336         /**
5337          * @event cellclick
5338          * Fires when a cell is clicked
5339          * @param {Roo.bootstrap.Table} this
5340          * @param {Roo.Element} el
5341          * @param {Number} rowIndex
5342          * @param {Number} columnIndex
5343          * @param {Roo.EventObject} e
5344          */
5345         "cellclick" : true,
5346         /**
5347          * @event celldblclick
5348          * Fires when a cell is double clicked
5349          * @param {Roo.bootstrap.Table} this
5350          * @param {Roo.Element} el
5351          * @param {Number} rowIndex
5352          * @param {Number} columnIndex
5353          * @param {Roo.EventObject} e
5354          */
5355         "celldblclick" : true,
5356         /**
5357          * @event rowclick
5358          * Fires when a row is clicked
5359          * @param {Roo.bootstrap.Table} this
5360          * @param {Roo.Element} el
5361          * @param {Number} rowIndex
5362          * @param {Roo.EventObject} e
5363          */
5364         "rowclick" : true,
5365         /**
5366          * @event rowdblclick
5367          * Fires when a row is double clicked
5368          * @param {Roo.bootstrap.Table} this
5369          * @param {Roo.Element} el
5370          * @param {Number} rowIndex
5371          * @param {Roo.EventObject} e
5372          */
5373         "rowdblclick" : true,
5374         /**
5375          * @event mouseover
5376          * Fires when a mouseover occur
5377          * @param {Roo.bootstrap.Table} this
5378          * @param {Roo.Element} el
5379          * @param {Number} rowIndex
5380          * @param {Number} columnIndex
5381          * @param {Roo.EventObject} e
5382          */
5383         "mouseover" : true,
5384         /**
5385          * @event mouseout
5386          * Fires when a mouseout occur
5387          * @param {Roo.bootstrap.Table} this
5388          * @param {Roo.Element} el
5389          * @param {Number} rowIndex
5390          * @param {Number} columnIndex
5391          * @param {Roo.EventObject} e
5392          */
5393         "mouseout" : true,
5394         /**
5395          * @event rowclass
5396          * Fires when a row is rendered, so you can change add a style to it.
5397          * @param {Roo.bootstrap.Table} this
5398          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5399          */
5400         'rowclass' : true,
5401           /**
5402          * @event rowsrendered
5403          * Fires when all the  rows have been rendered
5404          * @param {Roo.bootstrap.Table} this
5405          */
5406         'rowsrendered' : true
5407         
5408     });
5409 };
5410
5411 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5412     
5413     cls: false,
5414     align: false,
5415     bgcolor: false,
5416     border: false,
5417     cellpadding: false,
5418     cellspacing: false,
5419     frame: false,
5420     rules: false,
5421     sortable: false,
5422     summary: false,
5423     width: false,
5424     striped : false,
5425     bordered: false,
5426     hover:  false,
5427     condensed : false,
5428     responsive : false,
5429     sm : false,
5430     cm : false,
5431     store : false,
5432     loadMask : false,
5433     tfoot : true,
5434     thead : true,
5435     RowSelection : false,
5436     CellSelection : false,
5437     layout : false,
5438     
5439     // Roo.Element - the tbody
5440     mainBody: false, 
5441     
5442     getAutoCreate : function(){
5443         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5444         
5445         cfg = {
5446             tag: 'table',
5447             cls : 'table',
5448             cn : []
5449         }
5450             
5451         if (this.striped) {
5452             cfg.cls += ' table-striped';
5453         }
5454         
5455         if (this.hover) {
5456             cfg.cls += ' table-hover';
5457         }
5458         if (this.bordered) {
5459             cfg.cls += ' table-bordered';
5460         }
5461         if (this.condensed) {
5462             cfg.cls += ' table-condensed';
5463         }
5464         if (this.responsive) {
5465             cfg.cls += ' table-responsive';
5466         }
5467         
5468         if (this.cls) {
5469             cfg.cls+=  ' ' +this.cls;
5470         }
5471         
5472         // this lot should be simplifed...
5473         
5474         if (this.align) {
5475             cfg.align=this.align;
5476         }
5477         if (this.bgcolor) {
5478             cfg.bgcolor=this.bgcolor;
5479         }
5480         if (this.border) {
5481             cfg.border=this.border;
5482         }
5483         if (this.cellpadding) {
5484             cfg.cellpadding=this.cellpadding;
5485         }
5486         if (this.cellspacing) {
5487             cfg.cellspacing=this.cellspacing;
5488         }
5489         if (this.frame) {
5490             cfg.frame=this.frame;
5491         }
5492         if (this.rules) {
5493             cfg.rules=this.rules;
5494         }
5495         if (this.sortable) {
5496             cfg.sortable=this.sortable;
5497         }
5498         if (this.summary) {
5499             cfg.summary=this.summary;
5500         }
5501         if (this.width) {
5502             cfg.width=this.width;
5503         }
5504         if (this.layout) {
5505             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5506         }
5507         
5508         if(this.store || this.cm){
5509             if(this.thead){
5510                 cfg.cn.push(this.renderHeader());
5511             }
5512             
5513             cfg.cn.push(this.renderBody());
5514             
5515             if(this.tfoot){
5516                 cfg.cn.push(this.renderFooter());
5517             }
5518             
5519             cfg.cls+=  ' TableGrid';
5520         }
5521         
5522         return { cn : [ cfg ] };
5523     },
5524     
5525     initEvents : function()
5526     {   
5527         if(!this.store || !this.cm){
5528             return;
5529         }
5530         
5531         //Roo.log('initEvents with ds!!!!');
5532         
5533         this.mainBody = this.el.select('tbody', true).first();
5534         
5535         
5536         var _this = this;
5537         
5538         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5539             e.on('click', _this.sort, _this);
5540         });
5541         
5542         this.el.on("click", this.onClick, this);
5543         this.el.on("dblclick", this.onDblClick, this);
5544         
5545         // why is this done????? = it breaks dialogs??
5546         //this.parent().el.setStyle('position', 'relative');
5547         
5548         
5549         if (this.footer) {
5550             this.footer.parentId = this.id;
5551             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5552         }
5553         
5554         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5555         
5556         this.store.on('load', this.onLoad, this);
5557         this.store.on('beforeload', this.onBeforeLoad, this);
5558         this.store.on('update', this.onUpdate, this);
5559         this.store.on('add', this.onAdd, this);
5560         
5561     },
5562     
5563     onMouseover : function(e, el)
5564     {
5565         var cell = Roo.get(el);
5566         
5567         if(!cell){
5568             return;
5569         }
5570         
5571         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5572             cell = cell.findParent('td', false, true);
5573         }
5574         
5575         var row = cell.findParent('tr', false, true);
5576         var cellIndex = cell.dom.cellIndex;
5577         var rowIndex = row.dom.rowIndex - 1; // start from 0
5578         
5579         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5580         
5581     },
5582     
5583     onMouseout : function(e, el)
5584     {
5585         var cell = Roo.get(el);
5586         
5587         if(!cell){
5588             return;
5589         }
5590         
5591         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5592             cell = cell.findParent('td', false, true);
5593         }
5594         
5595         var row = cell.findParent('tr', false, true);
5596         var cellIndex = cell.dom.cellIndex;
5597         var rowIndex = row.dom.rowIndex - 1; // start from 0
5598         
5599         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5600         
5601     },
5602     
5603     onClick : function(e, el)
5604     {
5605         var cell = Roo.get(el);
5606         
5607         if(!cell || (!this.CellSelection && !this.RowSelection)){
5608             return;
5609         }
5610         
5611         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5612             cell = cell.findParent('td', false, true);
5613         }
5614         
5615         if(!cell || typeof(cell) == 'undefined'){
5616             return;
5617         }
5618         
5619         var row = cell.findParent('tr', false, true);
5620         
5621         if(!row || typeof(row) == 'undefined'){
5622             return;
5623         }
5624         
5625         var cellIndex = cell.dom.cellIndex;
5626         var rowIndex = this.getRowIndex(row);
5627         
5628         if(this.CellSelection){
5629             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5630         }
5631         
5632         if(this.RowSelection){
5633             this.fireEvent('rowclick', this, row, rowIndex, e);
5634         }
5635         
5636         
5637     },
5638     
5639     onDblClick : function(e,el)
5640     {
5641         var cell = Roo.get(el);
5642         
5643         if(!cell || (!this.CellSelection && !this.RowSelection)){
5644             return;
5645         }
5646         
5647         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5648             cell = cell.findParent('td', false, true);
5649         }
5650         
5651         if(!cell || typeof(cell) == 'undefined'){
5652             return;
5653         }
5654         
5655         var row = cell.findParent('tr', false, true);
5656         
5657         if(!row || typeof(row) == 'undefined'){
5658             return;
5659         }
5660         
5661         var cellIndex = cell.dom.cellIndex;
5662         var rowIndex = this.getRowIndex(row);
5663         
5664         if(this.CellSelection){
5665             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5666         }
5667         
5668         if(this.RowSelection){
5669             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5670         }
5671     },
5672     
5673     sort : function(e,el)
5674     {
5675         var col = Roo.get(el);
5676         
5677         if(!col.hasClass('sortable')){
5678             return;
5679         }
5680         
5681         var sort = col.attr('sort');
5682         var dir = 'ASC';
5683         
5684         if(col.hasClass('glyphicon-arrow-up')){
5685             dir = 'DESC';
5686         }
5687         
5688         this.store.sortInfo = {field : sort, direction : dir};
5689         
5690         if (this.footer) {
5691             Roo.log("calling footer first");
5692             this.footer.onClick('first');
5693         } else {
5694         
5695             this.store.load({ params : { start : 0 } });
5696         }
5697     },
5698     
5699     renderHeader : function()
5700     {
5701         var header = {
5702             tag: 'thead',
5703             cn : []
5704         };
5705         
5706         var cm = this.cm;
5707         
5708         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5709             
5710             var config = cm.config[i];
5711                     
5712             var c = {
5713                 tag: 'th',
5714                 style : '',
5715                 html: cm.getColumnHeader(i)
5716             };
5717             
5718             if(typeof(config.tooltip) != 'undefined'){
5719                 c.tooltip = config.tooltip;
5720             }
5721             
5722             if(typeof(config.colspan) != 'undefined'){
5723                 c.colspan = config.colspan;
5724             }
5725             
5726             if(typeof(config.hidden) != 'undefined' && config.hidden){
5727                 c.style += ' display:none;';
5728             }
5729             
5730             if(typeof(config.dataIndex) != 'undefined'){
5731                 c.sort = config.dataIndex;
5732             }
5733             
5734             if(typeof(config.sortable) != 'undefined' && config.sortable){
5735                 c.cls = 'sortable';
5736             }
5737             
5738             if(typeof(config.align) != 'undefined' && config.align.length){
5739                 c.style += ' text-align:' + config.align + ';';
5740             }
5741             
5742             if(typeof(config.width) != 'undefined'){
5743                 c.style += ' width:' + config.width + 'px;';
5744             }
5745             
5746             if(typeof(config.cls) != 'undefined'){
5747                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
5748             }
5749             
5750             header.cn.push(c)
5751         }
5752         
5753         return header;
5754     },
5755     
5756     renderBody : function()
5757     {
5758         var body = {
5759             tag: 'tbody',
5760             cn : [
5761                 {
5762                     tag: 'tr',
5763                     cn : [
5764                         {
5765                             tag : 'td',
5766                             colspan :  this.cm.getColumnCount()
5767                         }
5768                     ]
5769                 }
5770             ]
5771         };
5772         
5773         return body;
5774     },
5775     
5776     renderFooter : function()
5777     {
5778         var footer = {
5779             tag: 'tfoot',
5780             cn : [
5781                 {
5782                     tag: 'tr',
5783                     cn : [
5784                         {
5785                             tag : 'td',
5786                             colspan :  this.cm.getColumnCount()
5787                         }
5788                     ]
5789                 }
5790             ]
5791         };
5792         
5793         return footer;
5794     },
5795     
5796     
5797     
5798     onLoad : function()
5799     {
5800         Roo.log('ds onload');
5801         this.clear();
5802         
5803         var _this = this;
5804         var cm = this.cm;
5805         var ds = this.store;
5806         
5807         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5808             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5809             
5810             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5811                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5812             }
5813             
5814             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5815                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5816             }
5817         });
5818         
5819         var tbody =  this.mainBody;
5820               
5821         if(ds.getCount() > 0){
5822             ds.data.each(function(d,rowIndex){
5823                 var row =  this.renderRow(cm, ds, rowIndex);
5824                 
5825                 tbody.createChild(row);
5826                 
5827                 var _this = this;
5828                 
5829                 if(row.cellObjects.length){
5830                     Roo.each(row.cellObjects, function(r){
5831                         _this.renderCellObject(r);
5832                     })
5833                 }
5834                 
5835             }, this);
5836         }
5837         
5838         Roo.each(this.el.select('tbody td', true).elements, function(e){
5839             e.on('mouseover', _this.onMouseover, _this);
5840         });
5841         
5842         Roo.each(this.el.select('tbody td', true).elements, function(e){
5843             e.on('mouseout', _this.onMouseout, _this);
5844         });
5845         this.fireEvent('rowsrendered', this);
5846         //if(this.loadMask){
5847         //    this.maskEl.hide();
5848         //}
5849     },
5850     
5851     
5852     onUpdate : function(ds,record)
5853     {
5854         this.refreshRow(record);
5855     },
5856     
5857     onRemove : function(ds, record, index, isUpdate){
5858         if(isUpdate !== true){
5859             this.fireEvent("beforerowremoved", this, index, record);
5860         }
5861         var bt = this.mainBody.dom;
5862         
5863         var rows = this.el.select('tbody > tr', true).elements;
5864         
5865         if(typeof(rows[index]) != 'undefined'){
5866             bt.removeChild(rows[index].dom);
5867         }
5868         
5869 //        if(bt.rows[index]){
5870 //            bt.removeChild(bt.rows[index]);
5871 //        }
5872         
5873         if(isUpdate !== true){
5874             //this.stripeRows(index);
5875             //this.syncRowHeights(index, index);
5876             //this.layout();
5877             this.fireEvent("rowremoved", this, index, record);
5878         }
5879     },
5880     
5881     onAdd : function(ds, records, rowIndex)
5882     {
5883         //Roo.log('on Add called');
5884         // - note this does not handle multiple adding very well..
5885         var bt = this.mainBody.dom;
5886         for (var i =0 ; i < records.length;i++) {
5887             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
5888             //Roo.log(records[i]);
5889             //Roo.log(this.store.getAt(rowIndex+i));
5890             this.insertRow(this.store, rowIndex + i, false);
5891             return;
5892         }
5893         
5894     },
5895     
5896     
5897     refreshRow : function(record){
5898         var ds = this.store, index;
5899         if(typeof record == 'number'){
5900             index = record;
5901             record = ds.getAt(index);
5902         }else{
5903             index = ds.indexOf(record);
5904         }
5905         this.insertRow(ds, index, true);
5906         this.onRemove(ds, record, index+1, true);
5907         //this.syncRowHeights(index, index);
5908         //this.layout();
5909         this.fireEvent("rowupdated", this, index, record);
5910     },
5911     
5912     insertRow : function(dm, rowIndex, isUpdate){
5913         
5914         if(!isUpdate){
5915             this.fireEvent("beforerowsinserted", this, rowIndex);
5916         }
5917             //var s = this.getScrollState();
5918         var row = this.renderRow(this.cm, this.store, rowIndex);
5919         // insert before rowIndex..
5920         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5921         
5922         var _this = this;
5923                 
5924         if(row.cellObjects.length){
5925             Roo.each(row.cellObjects, function(r){
5926                 _this.renderCellObject(r);
5927             })
5928         }
5929             
5930         if(!isUpdate){
5931             this.fireEvent("rowsinserted", this, rowIndex);
5932             //this.syncRowHeights(firstRow, lastRow);
5933             //this.stripeRows(firstRow);
5934             //this.layout();
5935         }
5936         
5937     },
5938     
5939     
5940     getRowDom : function(rowIndex)
5941     {
5942         var rows = this.el.select('tbody > tr', true).elements;
5943         
5944         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
5945         
5946     },
5947     // returns the object tree for a tr..
5948   
5949     
5950     renderRow : function(cm, ds, rowIndex) 
5951     {
5952         
5953         var d = ds.getAt(rowIndex);
5954         
5955         var row = {
5956             tag : 'tr',
5957             cn : []
5958         };
5959             
5960         var cellObjects = [];
5961         
5962         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5963             var config = cm.config[i];
5964             
5965             var renderer = cm.getRenderer(i);
5966             var value = '';
5967             var id = false;
5968             
5969             if(typeof(renderer) !== 'undefined'){
5970                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5971             }
5972             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5973             // and are rendered into the cells after the row is rendered - using the id for the element.
5974             
5975             if(typeof(value) === 'object'){
5976                 id = Roo.id();
5977                 cellObjects.push({
5978                     container : id,
5979                     cfg : value 
5980                 })
5981             }
5982             
5983             var rowcfg = {
5984                 record: d,
5985                 rowIndex : rowIndex,
5986                 colIndex : i,
5987                 rowClass : ''
5988             }
5989
5990             this.fireEvent('rowclass', this, rowcfg);
5991             
5992             var td = {
5993                 tag: 'td',
5994                 cls : rowcfg.rowClass,
5995                 style: '',
5996                 html: (typeof(value) === 'object') ? '' : value
5997             };
5998             
5999             if (id) {
6000                 td.id = id;
6001             }
6002             
6003             if(typeof(config.colspan) != 'undefined'){
6004                 td.colspan = config.colspan;
6005             }
6006             
6007             if(typeof(config.hidden) != 'undefined' && config.hidden){
6008                 td.style += ' display:none;';
6009             }
6010             
6011             if(typeof(config.align) != 'undefined' && config.align.length){
6012                 td.style += ' text-align:' + config.align + ';';
6013             }
6014             
6015             if(typeof(config.width) != 'undefined'){
6016                 td.style += ' width:' +  config.width + 'px;';
6017             }
6018             
6019             if(typeof(config.cursor) != 'undefined'){
6020                 td.style += ' cursor:' +  config.cursor + ';';
6021             }
6022             
6023             if(typeof(config.cls) != 'undefined'){
6024                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6025             }
6026              
6027             row.cn.push(td);
6028            
6029         }
6030         
6031         row.cellObjects = cellObjects;
6032         
6033         return row;
6034           
6035     },
6036     
6037     
6038     
6039     onBeforeLoad : function()
6040     {
6041         //Roo.log('ds onBeforeLoad');
6042         
6043         //this.clear();
6044         
6045         //if(this.loadMask){
6046         //    this.maskEl.show();
6047         //}
6048     },
6049      /**
6050      * Remove all rows
6051      */
6052     clear : function()
6053     {
6054         this.el.select('tbody', true).first().dom.innerHTML = '';
6055     },
6056     /**
6057      * Show or hide a row.
6058      * @param {Number} rowIndex to show or hide
6059      * @param {Boolean} state hide
6060      */
6061     setRowVisibility : function(rowIndex, state)
6062     {
6063         var bt = this.mainBody.dom;
6064         
6065         var rows = this.el.select('tbody > tr', true).elements;
6066         
6067         if(typeof(rows[rowIndex]) == 'undefined'){
6068             return;
6069         }
6070         rows[rowIndex].dom.style.display = state ? '' : 'none';
6071     },
6072     
6073     
6074     getSelectionModel : function(){
6075         if(!this.selModel){
6076             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6077         }
6078         return this.selModel;
6079     },
6080     /*
6081      * Render the Roo.bootstrap object from renderder
6082      */
6083     renderCellObject : function(r)
6084     {
6085         var _this = this;
6086         
6087         var t = r.cfg.render(r.container);
6088         
6089         if(r.cfg.cn){
6090             Roo.each(r.cfg.cn, function(c){
6091                 var child = {
6092                     container: t.getChildContainer(),
6093                     cfg: c
6094                 }
6095                 _this.renderCellObject(child);
6096             })
6097         }
6098     },
6099     
6100     getRowIndex : function(row)
6101     {
6102         var rowIndex = -1;
6103         
6104         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6105             if(el != row){
6106                 return;
6107             }
6108             
6109             rowIndex = index;
6110         });
6111         
6112         return rowIndex;
6113     }
6114    
6115 });
6116
6117  
6118
6119  /*
6120  * - LGPL
6121  *
6122  * table cell
6123  * 
6124  */
6125
6126 /**
6127  * @class Roo.bootstrap.TableCell
6128  * @extends Roo.bootstrap.Component
6129  * Bootstrap TableCell class
6130  * @cfg {String} html cell contain text
6131  * @cfg {String} cls cell class
6132  * @cfg {String} tag cell tag (td|th) default td
6133  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6134  * @cfg {String} align Aligns the content in a cell
6135  * @cfg {String} axis Categorizes cells
6136  * @cfg {String} bgcolor Specifies the background color of a cell
6137  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6138  * @cfg {Number} colspan Specifies the number of columns a cell should span
6139  * @cfg {String} headers Specifies one or more header cells a cell is related to
6140  * @cfg {Number} height Sets the height of a cell
6141  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6142  * @cfg {Number} rowspan Sets the number of rows a cell should span
6143  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6144  * @cfg {String} valign Vertical aligns the content in a cell
6145  * @cfg {Number} width Specifies the width of a cell
6146  * 
6147  * @constructor
6148  * Create a new TableCell
6149  * @param {Object} config The config object
6150  */
6151
6152 Roo.bootstrap.TableCell = function(config){
6153     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6154 };
6155
6156 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6157     
6158     html: false,
6159     cls: false,
6160     tag: false,
6161     abbr: false,
6162     align: false,
6163     axis: false,
6164     bgcolor: false,
6165     charoff: false,
6166     colspan: false,
6167     headers: false,
6168     height: false,
6169     nowrap: false,
6170     rowspan: false,
6171     scope: false,
6172     valign: false,
6173     width: false,
6174     
6175     
6176     getAutoCreate : function(){
6177         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6178         
6179         cfg = {
6180             tag: 'td'
6181         }
6182         
6183         if(this.tag){
6184             cfg.tag = this.tag;
6185         }
6186         
6187         if (this.html) {
6188             cfg.html=this.html
6189         }
6190         if (this.cls) {
6191             cfg.cls=this.cls
6192         }
6193         if (this.abbr) {
6194             cfg.abbr=this.abbr
6195         }
6196         if (this.align) {
6197             cfg.align=this.align
6198         }
6199         if (this.axis) {
6200             cfg.axis=this.axis
6201         }
6202         if (this.bgcolor) {
6203             cfg.bgcolor=this.bgcolor
6204         }
6205         if (this.charoff) {
6206             cfg.charoff=this.charoff
6207         }
6208         if (this.colspan) {
6209             cfg.colspan=this.colspan
6210         }
6211         if (this.headers) {
6212             cfg.headers=this.headers
6213         }
6214         if (this.height) {
6215             cfg.height=this.height
6216         }
6217         if (this.nowrap) {
6218             cfg.nowrap=this.nowrap
6219         }
6220         if (this.rowspan) {
6221             cfg.rowspan=this.rowspan
6222         }
6223         if (this.scope) {
6224             cfg.scope=this.scope
6225         }
6226         if (this.valign) {
6227             cfg.valign=this.valign
6228         }
6229         if (this.width) {
6230             cfg.width=this.width
6231         }
6232         
6233         
6234         return cfg;
6235     }
6236    
6237 });
6238
6239  
6240
6241  /*
6242  * - LGPL
6243  *
6244  * table row
6245  * 
6246  */
6247
6248 /**
6249  * @class Roo.bootstrap.TableRow
6250  * @extends Roo.bootstrap.Component
6251  * Bootstrap TableRow class
6252  * @cfg {String} cls row class
6253  * @cfg {String} align Aligns the content in a table row
6254  * @cfg {String} bgcolor Specifies a background color for a table row
6255  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6256  * @cfg {String} valign Vertical aligns the content in a table row
6257  * 
6258  * @constructor
6259  * Create a new TableRow
6260  * @param {Object} config The config object
6261  */
6262
6263 Roo.bootstrap.TableRow = function(config){
6264     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6265 };
6266
6267 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6268     
6269     cls: false,
6270     align: false,
6271     bgcolor: false,
6272     charoff: false,
6273     valign: false,
6274     
6275     getAutoCreate : function(){
6276         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6277         
6278         cfg = {
6279             tag: 'tr'
6280         }
6281             
6282         if(this.cls){
6283             cfg.cls = this.cls;
6284         }
6285         if(this.align){
6286             cfg.align = this.align;
6287         }
6288         if(this.bgcolor){
6289             cfg.bgcolor = this.bgcolor;
6290         }
6291         if(this.charoff){
6292             cfg.charoff = this.charoff;
6293         }
6294         if(this.valign){
6295             cfg.valign = this.valign;
6296         }
6297         
6298         return cfg;
6299     }
6300    
6301 });
6302
6303  
6304
6305  /*
6306  * - LGPL
6307  *
6308  * table body
6309  * 
6310  */
6311
6312 /**
6313  * @class Roo.bootstrap.TableBody
6314  * @extends Roo.bootstrap.Component
6315  * Bootstrap TableBody class
6316  * @cfg {String} cls element class
6317  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6318  * @cfg {String} align Aligns the content inside the element
6319  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6320  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6321  * 
6322  * @constructor
6323  * Create a new TableBody
6324  * @param {Object} config The config object
6325  */
6326
6327 Roo.bootstrap.TableBody = function(config){
6328     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6329 };
6330
6331 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6332     
6333     cls: false,
6334     tag: false,
6335     align: false,
6336     charoff: false,
6337     valign: false,
6338     
6339     getAutoCreate : function(){
6340         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6341         
6342         cfg = {
6343             tag: 'tbody'
6344         }
6345             
6346         if (this.cls) {
6347             cfg.cls=this.cls
6348         }
6349         if(this.tag){
6350             cfg.tag = this.tag;
6351         }
6352         
6353         if(this.align){
6354             cfg.align = this.align;
6355         }
6356         if(this.charoff){
6357             cfg.charoff = this.charoff;
6358         }
6359         if(this.valign){
6360             cfg.valign = this.valign;
6361         }
6362         
6363         return cfg;
6364     }
6365     
6366     
6367 //    initEvents : function()
6368 //    {
6369 //        
6370 //        if(!this.store){
6371 //            return;
6372 //        }
6373 //        
6374 //        this.store = Roo.factory(this.store, Roo.data);
6375 //        this.store.on('load', this.onLoad, this);
6376 //        
6377 //        this.store.load();
6378 //        
6379 //    },
6380 //    
6381 //    onLoad: function () 
6382 //    {   
6383 //        this.fireEvent('load', this);
6384 //    }
6385 //    
6386 //   
6387 });
6388
6389  
6390
6391  /*
6392  * Based on:
6393  * Ext JS Library 1.1.1
6394  * Copyright(c) 2006-2007, Ext JS, LLC.
6395  *
6396  * Originally Released Under LGPL - original licence link has changed is not relivant.
6397  *
6398  * Fork - LGPL
6399  * <script type="text/javascript">
6400  */
6401
6402 // as we use this in bootstrap.
6403 Roo.namespace('Roo.form');
6404  /**
6405  * @class Roo.form.Action
6406  * Internal Class used to handle form actions
6407  * @constructor
6408  * @param {Roo.form.BasicForm} el The form element or its id
6409  * @param {Object} config Configuration options
6410  */
6411
6412  
6413  
6414 // define the action interface
6415 Roo.form.Action = function(form, options){
6416     this.form = form;
6417     this.options = options || {};
6418 };
6419 /**
6420  * Client Validation Failed
6421  * @const 
6422  */
6423 Roo.form.Action.CLIENT_INVALID = 'client';
6424 /**
6425  * Server Validation Failed
6426  * @const 
6427  */
6428 Roo.form.Action.SERVER_INVALID = 'server';
6429  /**
6430  * Connect to Server Failed
6431  * @const 
6432  */
6433 Roo.form.Action.CONNECT_FAILURE = 'connect';
6434 /**
6435  * Reading Data from Server Failed
6436  * @const 
6437  */
6438 Roo.form.Action.LOAD_FAILURE = 'load';
6439
6440 Roo.form.Action.prototype = {
6441     type : 'default',
6442     failureType : undefined,
6443     response : undefined,
6444     result : undefined,
6445
6446     // interface method
6447     run : function(options){
6448
6449     },
6450
6451     // interface method
6452     success : function(response){
6453
6454     },
6455
6456     // interface method
6457     handleResponse : function(response){
6458
6459     },
6460
6461     // default connection failure
6462     failure : function(response){
6463         
6464         this.response = response;
6465         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6466         this.form.afterAction(this, false);
6467     },
6468
6469     processResponse : function(response){
6470         this.response = response;
6471         if(!response.responseText){
6472             return true;
6473         }
6474         this.result = this.handleResponse(response);
6475         return this.result;
6476     },
6477
6478     // utility functions used internally
6479     getUrl : function(appendParams){
6480         var url = this.options.url || this.form.url || this.form.el.dom.action;
6481         if(appendParams){
6482             var p = this.getParams();
6483             if(p){
6484                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6485             }
6486         }
6487         return url;
6488     },
6489
6490     getMethod : function(){
6491         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6492     },
6493
6494     getParams : function(){
6495         var bp = this.form.baseParams;
6496         var p = this.options.params;
6497         if(p){
6498             if(typeof p == "object"){
6499                 p = Roo.urlEncode(Roo.applyIf(p, bp));
6500             }else if(typeof p == 'string' && bp){
6501                 p += '&' + Roo.urlEncode(bp);
6502             }
6503         }else if(bp){
6504             p = Roo.urlEncode(bp);
6505         }
6506         return p;
6507     },
6508
6509     createCallback : function(){
6510         return {
6511             success: this.success,
6512             failure: this.failure,
6513             scope: this,
6514             timeout: (this.form.timeout*1000),
6515             upload: this.form.fileUpload ? this.success : undefined
6516         };
6517     }
6518 };
6519
6520 Roo.form.Action.Submit = function(form, options){
6521     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6522 };
6523
6524 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6525     type : 'submit',
6526
6527     haveProgress : false,
6528     uploadComplete : false,
6529     
6530     // uploadProgress indicator.
6531     uploadProgress : function()
6532     {
6533         if (!this.form.progressUrl) {
6534             return;
6535         }
6536         
6537         if (!this.haveProgress) {
6538             Roo.MessageBox.progress("Uploading", "Uploading");
6539         }
6540         if (this.uploadComplete) {
6541            Roo.MessageBox.hide();
6542            return;
6543         }
6544         
6545         this.haveProgress = true;
6546    
6547         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6548         
6549         var c = new Roo.data.Connection();
6550         c.request({
6551             url : this.form.progressUrl,
6552             params: {
6553                 id : uid
6554             },
6555             method: 'GET',
6556             success : function(req){
6557                //console.log(data);
6558                 var rdata = false;
6559                 var edata;
6560                 try  {
6561                    rdata = Roo.decode(req.responseText)
6562                 } catch (e) {
6563                     Roo.log("Invalid data from server..");
6564                     Roo.log(edata);
6565                     return;
6566                 }
6567                 if (!rdata || !rdata.success) {
6568                     Roo.log(rdata);
6569                     Roo.MessageBox.alert(Roo.encode(rdata));
6570                     return;
6571                 }
6572                 var data = rdata.data;
6573                 
6574                 if (this.uploadComplete) {
6575                    Roo.MessageBox.hide();
6576                    return;
6577                 }
6578                    
6579                 if (data){
6580                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6581                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6582                     );
6583                 }
6584                 this.uploadProgress.defer(2000,this);
6585             },
6586        
6587             failure: function(data) {
6588                 Roo.log('progress url failed ');
6589                 Roo.log(data);
6590             },
6591             scope : this
6592         });
6593            
6594     },
6595     
6596     
6597     run : function()
6598     {
6599         // run get Values on the form, so it syncs any secondary forms.
6600         this.form.getValues();
6601         
6602         var o = this.options;
6603         var method = this.getMethod();
6604         var isPost = method == 'POST';
6605         if(o.clientValidation === false || this.form.isValid()){
6606             
6607             if (this.form.progressUrl) {
6608                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6609                     (new Date() * 1) + '' + Math.random());
6610                     
6611             } 
6612             
6613             
6614             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6615                 form:this.form.el.dom,
6616                 url:this.getUrl(!isPost),
6617                 method: method,
6618                 params:isPost ? this.getParams() : null,
6619                 isUpload: this.form.fileUpload
6620             }));
6621             
6622             this.uploadProgress();
6623
6624         }else if (o.clientValidation !== false){ // client validation failed
6625             this.failureType = Roo.form.Action.CLIENT_INVALID;
6626             this.form.afterAction(this, false);
6627         }
6628     },
6629
6630     success : function(response)
6631     {
6632         this.uploadComplete= true;
6633         if (this.haveProgress) {
6634             Roo.MessageBox.hide();
6635         }
6636         
6637         
6638         var result = this.processResponse(response);
6639         if(result === true || result.success){
6640             this.form.afterAction(this, true);
6641             return;
6642         }
6643         if(result.errors){
6644             this.form.markInvalid(result.errors);
6645             this.failureType = Roo.form.Action.SERVER_INVALID;
6646         }
6647         this.form.afterAction(this, false);
6648     },
6649     failure : function(response)
6650     {
6651         this.uploadComplete= true;
6652         if (this.haveProgress) {
6653             Roo.MessageBox.hide();
6654         }
6655         
6656         this.response = response;
6657         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6658         this.form.afterAction(this, false);
6659     },
6660     
6661     handleResponse : function(response){
6662         if(this.form.errorReader){
6663             var rs = this.form.errorReader.read(response);
6664             var errors = [];
6665             if(rs.records){
6666                 for(var i = 0, len = rs.records.length; i < len; i++) {
6667                     var r = rs.records[i];
6668                     errors[i] = r.data;
6669                 }
6670             }
6671             if(errors.length < 1){
6672                 errors = null;
6673             }
6674             return {
6675                 success : rs.success,
6676                 errors : errors
6677             };
6678         }
6679         var ret = false;
6680         try {
6681             ret = Roo.decode(response.responseText);
6682         } catch (e) {
6683             ret = {
6684                 success: false,
6685                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6686                 errors : []
6687             };
6688         }
6689         return ret;
6690         
6691     }
6692 });
6693
6694
6695 Roo.form.Action.Load = function(form, options){
6696     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6697     this.reader = this.form.reader;
6698 };
6699
6700 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6701     type : 'load',
6702
6703     run : function(){
6704         
6705         Roo.Ajax.request(Roo.apply(
6706                 this.createCallback(), {
6707                     method:this.getMethod(),
6708                     url:this.getUrl(false),
6709                     params:this.getParams()
6710         }));
6711     },
6712
6713     success : function(response){
6714         
6715         var result = this.processResponse(response);
6716         if(result === true || !result.success || !result.data){
6717             this.failureType = Roo.form.Action.LOAD_FAILURE;
6718             this.form.afterAction(this, false);
6719             return;
6720         }
6721         this.form.clearInvalid();
6722         this.form.setValues(result.data);
6723         this.form.afterAction(this, true);
6724     },
6725
6726     handleResponse : function(response){
6727         if(this.form.reader){
6728             var rs = this.form.reader.read(response);
6729             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6730             return {
6731                 success : rs.success,
6732                 data : data
6733             };
6734         }
6735         return Roo.decode(response.responseText);
6736     }
6737 });
6738
6739 Roo.form.Action.ACTION_TYPES = {
6740     'load' : Roo.form.Action.Load,
6741     'submit' : Roo.form.Action.Submit
6742 };/*
6743  * - LGPL
6744  *
6745  * form
6746  * 
6747  */
6748
6749 /**
6750  * @class Roo.bootstrap.Form
6751  * @extends Roo.bootstrap.Component
6752  * Bootstrap Form class
6753  * @cfg {String} method  GET | POST (default POST)
6754  * @cfg {String} labelAlign top | left (default top)
6755  * @cfg {String} align left  | right - for navbars
6756  * @cfg {Boolean} loadMask load mask when submit (default true)
6757
6758  * 
6759  * @constructor
6760  * Create a new Form
6761  * @param {Object} config The config object
6762  */
6763
6764
6765 Roo.bootstrap.Form = function(config){
6766     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6767     this.addEvents({
6768         /**
6769          * @event clientvalidation
6770          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6771          * @param {Form} this
6772          * @param {Boolean} valid true if the form has passed client-side validation
6773          */
6774         clientvalidation: true,
6775         /**
6776          * @event beforeaction
6777          * Fires before any action is performed. Return false to cancel the action.
6778          * @param {Form} this
6779          * @param {Action} action The action to be performed
6780          */
6781         beforeaction: true,
6782         /**
6783          * @event actionfailed
6784          * Fires when an action fails.
6785          * @param {Form} this
6786          * @param {Action} action The action that failed
6787          */
6788         actionfailed : true,
6789         /**
6790          * @event actioncomplete
6791          * Fires when an action is completed.
6792          * @param {Form} this
6793          * @param {Action} action The action that completed
6794          */
6795         actioncomplete : true
6796     });
6797     
6798 };
6799
6800 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6801       
6802      /**
6803      * @cfg {String} method
6804      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6805      */
6806     method : 'POST',
6807     /**
6808      * @cfg {String} url
6809      * The URL to use for form actions if one isn't supplied in the action options.
6810      */
6811     /**
6812      * @cfg {Boolean} fileUpload
6813      * Set to true if this form is a file upload.
6814      */
6815      
6816     /**
6817      * @cfg {Object} baseParams
6818      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6819      */
6820       
6821     /**
6822      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6823      */
6824     timeout: 30,
6825     /**
6826      * @cfg {Sting} align (left|right) for navbar forms
6827      */
6828     align : 'left',
6829
6830     // private
6831     activeAction : null,
6832  
6833     /**
6834      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6835      * element by passing it or its id or mask the form itself by passing in true.
6836      * @type Mixed
6837      */
6838     waitMsgTarget : false,
6839     
6840     loadMask : true,
6841     
6842     getAutoCreate : function(){
6843         
6844         var cfg = {
6845             tag: 'form',
6846             method : this.method || 'POST',
6847             id : this.id || Roo.id(),
6848             cls : ''
6849         }
6850         if (this.parent().xtype.match(/^Nav/)) {
6851             cfg.cls = 'navbar-form navbar-' + this.align;
6852             
6853         }
6854         
6855         if (this.labelAlign == 'left' ) {
6856             cfg.cls += ' form-horizontal';
6857         }
6858         
6859         
6860         return cfg;
6861     },
6862     initEvents : function()
6863     {
6864         this.el.on('submit', this.onSubmit, this);
6865         // this was added as random key presses on the form where triggering form submit.
6866         this.el.on('keypress', function(e) {
6867             if (e.getCharCode() != 13) {
6868                 return true;
6869             }
6870             // we might need to allow it for textareas.. and some other items.
6871             // check e.getTarget().
6872             
6873             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6874                 return true;
6875             }
6876         
6877             Roo.log("keypress blocked");
6878             
6879             e.preventDefault();
6880             return false;
6881         });
6882         
6883     },
6884     // private
6885     onSubmit : function(e){
6886         e.stopEvent();
6887     },
6888     
6889      /**
6890      * Returns true if client-side validation on the form is successful.
6891      * @return Boolean
6892      */
6893     isValid : function(){
6894         var items = this.getItems();
6895         var valid = true;
6896         items.each(function(f){
6897            if(!f.validate()){
6898                valid = false;
6899                
6900            }
6901         });
6902         return valid;
6903     },
6904     /**
6905      * Returns true if any fields in this form have changed since their original load.
6906      * @return Boolean
6907      */
6908     isDirty : function(){
6909         var dirty = false;
6910         var items = this.getItems();
6911         items.each(function(f){
6912            if(f.isDirty()){
6913                dirty = true;
6914                return false;
6915            }
6916            return true;
6917         });
6918         return dirty;
6919     },
6920      /**
6921      * Performs a predefined action (submit or load) or custom actions you define on this form.
6922      * @param {String} actionName The name of the action type
6923      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6924      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6925      * accept other config options):
6926      * <pre>
6927 Property          Type             Description
6928 ----------------  ---------------  ----------------------------------------------------------------------------------
6929 url               String           The url for the action (defaults to the form's url)
6930 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6931 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6932 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6933                                    validate the form on the client (defaults to false)
6934      * </pre>
6935      * @return {BasicForm} this
6936      */
6937     doAction : function(action, options){
6938         if(typeof action == 'string'){
6939             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6940         }
6941         if(this.fireEvent('beforeaction', this, action) !== false){
6942             this.beforeAction(action);
6943             action.run.defer(100, action);
6944         }
6945         return this;
6946     },
6947     
6948     // private
6949     beforeAction : function(action){
6950         var o = action.options;
6951         
6952         if(this.loadMask){
6953             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6954         }
6955         // not really supported yet.. ??
6956         
6957         //if(this.waitMsgTarget === true){
6958         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6959         //}else if(this.waitMsgTarget){
6960         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6961         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6962         //}else {
6963         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6964        // }
6965          
6966     },
6967
6968     // private
6969     afterAction : function(action, success){
6970         this.activeAction = null;
6971         var o = action.options;
6972         
6973         //if(this.waitMsgTarget === true){
6974             this.el.unmask();
6975         //}else if(this.waitMsgTarget){
6976         //    this.waitMsgTarget.unmask();
6977         //}else{
6978         //    Roo.MessageBox.updateProgress(1);
6979         //    Roo.MessageBox.hide();
6980        // }
6981         // 
6982         if(success){
6983             if(o.reset){
6984                 this.reset();
6985             }
6986             Roo.callback(o.success, o.scope, [this, action]);
6987             this.fireEvent('actioncomplete', this, action);
6988             
6989         }else{
6990             
6991             // failure condition..
6992             // we have a scenario where updates need confirming.
6993             // eg. if a locking scenario exists..
6994             // we look for { errors : { needs_confirm : true }} in the response.
6995             if (
6996                 (typeof(action.result) != 'undefined')  &&
6997                 (typeof(action.result.errors) != 'undefined')  &&
6998                 (typeof(action.result.errors.needs_confirm) != 'undefined')
6999            ){
7000                 var _t = this;
7001                 Roo.log("not supported yet");
7002                  /*
7003                 
7004                 Roo.MessageBox.confirm(
7005                     "Change requires confirmation",
7006                     action.result.errorMsg,
7007                     function(r) {
7008                         if (r != 'yes') {
7009                             return;
7010                         }
7011                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7012                     }
7013                     
7014                 );
7015                 */
7016                 
7017                 
7018                 return;
7019             }
7020             
7021             Roo.callback(o.failure, o.scope, [this, action]);
7022             // show an error message if no failed handler is set..
7023             if (!this.hasListener('actionfailed')) {
7024                 Roo.log("need to add dialog support");
7025                 /*
7026                 Roo.MessageBox.alert("Error",
7027                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7028                         action.result.errorMsg :
7029                         "Saving Failed, please check your entries or try again"
7030                 );
7031                 */
7032             }
7033             
7034             this.fireEvent('actionfailed', this, action);
7035         }
7036         
7037     },
7038     /**
7039      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7040      * @param {String} id The value to search for
7041      * @return Field
7042      */
7043     findField : function(id){
7044         var items = this.getItems();
7045         var field = items.get(id);
7046         if(!field){
7047              items.each(function(f){
7048                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7049                     field = f;
7050                     return false;
7051                 }
7052                 return true;
7053             });
7054         }
7055         return field || null;
7056     },
7057      /**
7058      * Mark fields in this form invalid in bulk.
7059      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7060      * @return {BasicForm} this
7061      */
7062     markInvalid : function(errors){
7063         if(errors instanceof Array){
7064             for(var i = 0, len = errors.length; i < len; i++){
7065                 var fieldError = errors[i];
7066                 var f = this.findField(fieldError.id);
7067                 if(f){
7068                     f.markInvalid(fieldError.msg);
7069                 }
7070             }
7071         }else{
7072             var field, id;
7073             for(id in errors){
7074                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7075                     field.markInvalid(errors[id]);
7076                 }
7077             }
7078         }
7079         //Roo.each(this.childForms || [], function (f) {
7080         //    f.markInvalid(errors);
7081         //});
7082         
7083         return this;
7084     },
7085
7086     /**
7087      * Set values for fields in this form in bulk.
7088      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7089      * @return {BasicForm} this
7090      */
7091     setValues : function(values){
7092         if(values instanceof Array){ // array of objects
7093             for(var i = 0, len = values.length; i < len; i++){
7094                 var v = values[i];
7095                 var f = this.findField(v.id);
7096                 if(f){
7097                     f.setValue(v.value);
7098                     if(this.trackResetOnLoad){
7099                         f.originalValue = f.getValue();
7100                     }
7101                 }
7102             }
7103         }else{ // object hash
7104             var field, id;
7105             for(id in values){
7106                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7107                     
7108                     if (field.setFromData && 
7109                         field.valueField && 
7110                         field.displayField &&
7111                         // combos' with local stores can 
7112                         // be queried via setValue()
7113                         // to set their value..
7114                         (field.store && !field.store.isLocal)
7115                         ) {
7116                         // it's a combo
7117                         var sd = { };
7118                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7119                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7120                         field.setFromData(sd);
7121                         
7122                     } else {
7123                         field.setValue(values[id]);
7124                     }
7125                     
7126                     
7127                     if(this.trackResetOnLoad){
7128                         field.originalValue = field.getValue();
7129                     }
7130                 }
7131             }
7132         }
7133          
7134         //Roo.each(this.childForms || [], function (f) {
7135         //    f.setValues(values);
7136         //});
7137                 
7138         return this;
7139     },
7140
7141     /**
7142      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7143      * they are returned as an array.
7144      * @param {Boolean} asString
7145      * @return {Object}
7146      */
7147     getValues : function(asString){
7148         //if (this.childForms) {
7149             // copy values from the child forms
7150         //    Roo.each(this.childForms, function (f) {
7151         //        this.setValues(f.getValues());
7152         //    }, this);
7153         //}
7154         
7155         
7156         
7157         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7158         if(asString === true){
7159             return fs;
7160         }
7161         return Roo.urlDecode(fs);
7162     },
7163     
7164     /**
7165      * Returns the fields in this form as an object with key/value pairs. 
7166      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7167      * @return {Object}
7168      */
7169     getFieldValues : function(with_hidden)
7170     {
7171         var items = this.getItems();
7172         var ret = {};
7173         items.each(function(f){
7174             if (!f.getName()) {
7175                 return;
7176             }
7177             var v = f.getValue();
7178             if (f.inputType =='radio') {
7179                 if (typeof(ret[f.getName()]) == 'undefined') {
7180                     ret[f.getName()] = ''; // empty..
7181                 }
7182                 
7183                 if (!f.el.dom.checked) {
7184                     return;
7185                     
7186                 }
7187                 v = f.el.dom.value;
7188                 
7189             }
7190             
7191             // not sure if this supported any more..
7192             if ((typeof(v) == 'object') && f.getRawValue) {
7193                 v = f.getRawValue() ; // dates..
7194             }
7195             // combo boxes where name != hiddenName...
7196             if (f.name != f.getName()) {
7197                 ret[f.name] = f.getRawValue();
7198             }
7199             ret[f.getName()] = v;
7200         });
7201         
7202         return ret;
7203     },
7204
7205     /**
7206      * Clears all invalid messages in this form.
7207      * @return {BasicForm} this
7208      */
7209     clearInvalid : function(){
7210         var items = this.getItems();
7211         
7212         items.each(function(f){
7213            f.clearInvalid();
7214         });
7215         
7216         
7217         
7218         return this;
7219     },
7220
7221     /**
7222      * Resets this form.
7223      * @return {BasicForm} this
7224      */
7225     reset : function(){
7226         var items = this.getItems();
7227         items.each(function(f){
7228             f.reset();
7229         });
7230         
7231         Roo.each(this.childForms || [], function (f) {
7232             f.reset();
7233         });
7234        
7235         
7236         return this;
7237     },
7238     getItems : function()
7239     {
7240         var r=new Roo.util.MixedCollection(false, function(o){
7241             return o.id || (o.id = Roo.id());
7242         });
7243         var iter = function(el) {
7244             if (el.inputEl) {
7245                 r.add(el);
7246             }
7247             if (!el.items) {
7248                 return;
7249             }
7250             Roo.each(el.items,function(e) {
7251                 iter(e);
7252             });
7253             
7254             
7255         };
7256         
7257         iter(this);
7258         return r;
7259         
7260         
7261         
7262         
7263     }
7264     
7265 });
7266
7267  
7268 /*
7269  * Based on:
7270  * Ext JS Library 1.1.1
7271  * Copyright(c) 2006-2007, Ext JS, LLC.
7272  *
7273  * Originally Released Under LGPL - original licence link has changed is not relivant.
7274  *
7275  * Fork - LGPL
7276  * <script type="text/javascript">
7277  */
7278 /**
7279  * @class Roo.form.VTypes
7280  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7281  * @singleton
7282  */
7283 Roo.form.VTypes = function(){
7284     // closure these in so they are only created once.
7285     var alpha = /^[a-zA-Z_]+$/;
7286     var alphanum = /^[a-zA-Z0-9_]+$/;
7287     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7288     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7289
7290     // All these messages and functions are configurable
7291     return {
7292         /**
7293          * The function used to validate email addresses
7294          * @param {String} value The email address
7295          */
7296         'email' : function(v){
7297             return email.test(v);
7298         },
7299         /**
7300          * The error text to display when the email validation function returns false
7301          * @type String
7302          */
7303         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7304         /**
7305          * The keystroke filter mask to be applied on email input
7306          * @type RegExp
7307          */
7308         'emailMask' : /[a-z0-9_\.\-@]/i,
7309
7310         /**
7311          * The function used to validate URLs
7312          * @param {String} value The URL
7313          */
7314         'url' : function(v){
7315             return url.test(v);
7316         },
7317         /**
7318          * The error text to display when the url validation function returns false
7319          * @type String
7320          */
7321         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7322         
7323         /**
7324          * The function used to validate alpha values
7325          * @param {String} value The value
7326          */
7327         'alpha' : function(v){
7328             return alpha.test(v);
7329         },
7330         /**
7331          * The error text to display when the alpha validation function returns false
7332          * @type String
7333          */
7334         'alphaText' : 'This field should only contain letters and _',
7335         /**
7336          * The keystroke filter mask to be applied on alpha input
7337          * @type RegExp
7338          */
7339         'alphaMask' : /[a-z_]/i,
7340
7341         /**
7342          * The function used to validate alphanumeric values
7343          * @param {String} value The value
7344          */
7345         'alphanum' : function(v){
7346             return alphanum.test(v);
7347         },
7348         /**
7349          * The error text to display when the alphanumeric validation function returns false
7350          * @type String
7351          */
7352         'alphanumText' : 'This field should only contain letters, numbers and _',
7353         /**
7354          * The keystroke filter mask to be applied on alphanumeric input
7355          * @type RegExp
7356          */
7357         'alphanumMask' : /[a-z0-9_]/i
7358     };
7359 }();/*
7360  * - LGPL
7361  *
7362  * Input
7363  * 
7364  */
7365
7366 /**
7367  * @class Roo.bootstrap.Input
7368  * @extends Roo.bootstrap.Component
7369  * Bootstrap Input class
7370  * @cfg {Boolean} disabled is it disabled
7371  * @cfg {String} fieldLabel - the label associated
7372  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7373  * @cfg {String} name name of the input
7374  * @cfg {string} fieldLabel - the label associated
7375  * @cfg {string}  inputType - input / file submit ...
7376  * @cfg {string} placeholder - placeholder to put in text.
7377  * @cfg {string}  before - input group add on before
7378  * @cfg {string} after - input group add on after
7379  * @cfg {string} size - (lg|sm) or leave empty..
7380  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7381  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7382  * @cfg {Number} md colspan out of 12 for computer-sized screens
7383  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7384  * @cfg {string} value default value of the input
7385  * @cfg {Number} labelWidth set the width of label (0-12)
7386  * @cfg {String} labelAlign (top|left)
7387  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7388  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7389
7390  * @cfg {String} align (left|center|right) Default left
7391  * 
7392  * 
7393  * 
7394  * @constructor
7395  * Create a new Input
7396  * @param {Object} config The config object
7397  */
7398
7399 Roo.bootstrap.Input = function(config){
7400     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7401    
7402         this.addEvents({
7403             /**
7404              * @event focus
7405              * Fires when this field receives input focus.
7406              * @param {Roo.form.Field} this
7407              */
7408             focus : true,
7409             /**
7410              * @event blur
7411              * Fires when this field loses input focus.
7412              * @param {Roo.form.Field} this
7413              */
7414             blur : true,
7415             /**
7416              * @event specialkey
7417              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7418              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7419              * @param {Roo.form.Field} this
7420              * @param {Roo.EventObject} e The event object
7421              */
7422             specialkey : true,
7423             /**
7424              * @event change
7425              * Fires just before the field blurs if the field value has changed.
7426              * @param {Roo.form.Field} this
7427              * @param {Mixed} newValue The new value
7428              * @param {Mixed} oldValue The original value
7429              */
7430             change : true,
7431             /**
7432              * @event invalid
7433              * Fires after the field has been marked as invalid.
7434              * @param {Roo.form.Field} this
7435              * @param {String} msg The validation message
7436              */
7437             invalid : true,
7438             /**
7439              * @event valid
7440              * Fires after the field has been validated with no errors.
7441              * @param {Roo.form.Field} this
7442              */
7443             valid : true,
7444              /**
7445              * @event keyup
7446              * Fires after the key up
7447              * @param {Roo.form.Field} this
7448              * @param {Roo.EventObject}  e The event Object
7449              */
7450             keyup : true
7451         });
7452 };
7453
7454 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
7455      /**
7456      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7457       automatic validation (defaults to "keyup").
7458      */
7459     validationEvent : "keyup",
7460      /**
7461      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7462      */
7463     validateOnBlur : true,
7464     /**
7465      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7466      */
7467     validationDelay : 250,
7468      /**
7469      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7470      */
7471     focusClass : "x-form-focus",  // not needed???
7472     
7473        
7474     /**
7475      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7476      */
7477     invalidClass : "has-warning",
7478     
7479     /**
7480      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7481      */
7482     validClass : "has-success",
7483     
7484     /**
7485      * @cfg {Boolean} hasFeedback (true|false) default true
7486      */
7487     hasFeedback : true,
7488     
7489     /**
7490      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7491      */
7492     invalidFeedbackClass : "glyphicon-warning-sign",
7493     
7494     /**
7495      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7496      */
7497     validFeedbackClass : "glyphicon-ok",
7498     
7499     /**
7500      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7501      */
7502     selectOnFocus : false,
7503     
7504      /**
7505      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7506      */
7507     maskRe : null,
7508        /**
7509      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7510      */
7511     vtype : null,
7512     
7513       /**
7514      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7515      */
7516     disableKeyFilter : false,
7517     
7518        /**
7519      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7520      */
7521     disabled : false,
7522      /**
7523      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7524      */
7525     allowBlank : true,
7526     /**
7527      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7528      */
7529     blankText : "This field is required",
7530     
7531      /**
7532      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7533      */
7534     minLength : 0,
7535     /**
7536      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7537      */
7538     maxLength : Number.MAX_VALUE,
7539     /**
7540      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7541      */
7542     minLengthText : "The minimum length for this field is {0}",
7543     /**
7544      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7545      */
7546     maxLengthText : "The maximum length for this field is {0}",
7547   
7548     
7549     /**
7550      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7551      * If available, this function will be called only after the basic validators all return true, and will be passed the
7552      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7553      */
7554     validator : null,
7555     /**
7556      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7557      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7558      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7559      */
7560     regex : null,
7561     /**
7562      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7563      */
7564     regexText : "",
7565     
7566     autocomplete: false,
7567     
7568     
7569     fieldLabel : '',
7570     inputType : 'text',
7571     
7572     name : false,
7573     placeholder: false,
7574     before : false,
7575     after : false,
7576     size : false,
7577     hasFocus : false,
7578     preventMark: false,
7579     isFormField : true,
7580     value : '',
7581     labelWidth : 2,
7582     labelAlign : false,
7583     readOnly : false,
7584     align : false,
7585     formatedValue : false,
7586     
7587     parentLabelAlign : function()
7588     {
7589         var parent = this;
7590         while (parent.parent()) {
7591             parent = parent.parent();
7592             if (typeof(parent.labelAlign) !='undefined') {
7593                 return parent.labelAlign;
7594             }
7595         }
7596         return 'left';
7597         
7598     },
7599     
7600     getAutoCreate : function(){
7601         
7602         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7603         
7604         var id = Roo.id();
7605         
7606         var cfg = {};
7607         
7608         if(this.inputType != 'hidden'){
7609             cfg.cls = 'form-group' //input-group
7610         }
7611         
7612         var input =  {
7613             tag: 'input',
7614             id : id,
7615             type : this.inputType,
7616             value : this.value,
7617             cls : 'form-control',
7618             placeholder : this.placeholder || '',
7619             autocomplete : this.autocomplete || 'new-password'
7620         };
7621         
7622         
7623         if(this.align){
7624             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7625         }
7626         
7627         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7628             input.maxLength = this.maxLength;
7629         }
7630         
7631         if (this.disabled) {
7632             input.disabled=true;
7633         }
7634         
7635         if (this.readOnly) {
7636             input.readonly=true;
7637         }
7638         
7639         if (this.name) {
7640             input.name = this.name;
7641         }
7642         if (this.size) {
7643             input.cls += ' input-' + this.size;
7644         }
7645         var settings=this;
7646         ['xs','sm','md','lg'].map(function(size){
7647             if (settings[size]) {
7648                 cfg.cls += ' col-' + size + '-' + settings[size];
7649             }
7650         });
7651         
7652         var inputblock = input;
7653         
7654         var feedback = {
7655             tag: 'span',
7656             cls: 'glyphicon form-control-feedback'
7657         };
7658             
7659         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7660             
7661             inputblock = {
7662                 cls : 'has-feedback',
7663                 cn :  [
7664                     input,
7665                     feedback
7666                 ] 
7667             };  
7668         }
7669         
7670         if (this.before || this.after) {
7671             
7672             inputblock = {
7673                 cls : 'input-group',
7674                 cn :  [] 
7675             };
7676             
7677             if (this.before && typeof(this.before) == 'string') {
7678                 
7679                 inputblock.cn.push({
7680                     tag :'span',
7681                     cls : 'roo-input-before input-group-addon',
7682                     html : this.before
7683                 });
7684             }
7685             if (this.before && typeof(this.before) == 'object') {
7686                 this.before = Roo.factory(this.before);
7687                 Roo.log(this.before);
7688                 inputblock.cn.push({
7689                     tag :'span',
7690                     cls : 'roo-input-before input-group-' +
7691                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7692                 });
7693             }
7694             
7695             inputblock.cn.push(input);
7696             
7697             if (this.after && typeof(this.after) == 'string') {
7698                 inputblock.cn.push({
7699                     tag :'span',
7700                     cls : 'roo-input-after input-group-addon',
7701                     html : this.after
7702                 });
7703             }
7704             if (this.after && typeof(this.after) == 'object') {
7705                 this.after = Roo.factory(this.after);
7706                 Roo.log(this.after);
7707                 inputblock.cn.push({
7708                     tag :'span',
7709                     cls : 'roo-input-after input-group-' +
7710                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7711                 });
7712             }
7713             
7714             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7715                 inputblock.cls += ' has-feedback';
7716                 inputblock.cn.push(feedback);
7717             }
7718         };
7719         
7720         if (align ==='left' && this.fieldLabel.length) {
7721                 Roo.log("left and has label");
7722                 cfg.cn = [
7723                     
7724                     {
7725                         tag: 'label',
7726                         'for' :  id,
7727                         cls : 'control-label col-sm-' + this.labelWidth,
7728                         html : this.fieldLabel
7729                         
7730                     },
7731                     {
7732                         cls : "col-sm-" + (12 - this.labelWidth), 
7733                         cn: [
7734                             inputblock
7735                         ]
7736                     }
7737                     
7738                 ];
7739         } else if ( this.fieldLabel.length) {
7740                 Roo.log(" label");
7741                  cfg.cn = [
7742                    
7743                     {
7744                         tag: 'label',
7745                         //cls : 'input-group-addon',
7746                         html : this.fieldLabel
7747                         
7748                     },
7749                     
7750                     inputblock
7751                     
7752                 ];
7753
7754         } else {
7755             
7756                 Roo.log(" no label && no align");
7757                 cfg.cn = [
7758                     
7759                         inputblock
7760                     
7761                 ];
7762                 
7763                 
7764         };
7765         Roo.log('input-parentType: ' + this.parentType);
7766         
7767         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7768            cfg.cls += ' navbar-form';
7769            Roo.log(cfg);
7770         }
7771         
7772         return cfg;
7773         
7774     },
7775     /**
7776      * return the real input element.
7777      */
7778     inputEl: function ()
7779     {
7780         return this.el.select('input.form-control',true).first();
7781     },
7782     
7783     tooltipEl : function()
7784     {
7785         return this.inputEl();
7786     },
7787     
7788     setDisabled : function(v)
7789     {
7790         var i  = this.inputEl().dom;
7791         if (!v) {
7792             i.removeAttribute('disabled');
7793             return;
7794             
7795         }
7796         i.setAttribute('disabled','true');
7797     },
7798     initEvents : function()
7799     {
7800           
7801         this.inputEl().on("keydown" , this.fireKey,  this);
7802         this.inputEl().on("focus", this.onFocus,  this);
7803         this.inputEl().on("blur", this.onBlur,  this);
7804         
7805         this.inputEl().relayEvent('keyup', this);
7806
7807         // reference to original value for reset
7808         this.originalValue = this.getValue();
7809         //Roo.form.TextField.superclass.initEvents.call(this);
7810         if(this.validationEvent == 'keyup'){
7811             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7812             this.inputEl().on('keyup', this.filterValidation, this);
7813         }
7814         else if(this.validationEvent !== false){
7815             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7816         }
7817         
7818         if(this.selectOnFocus){
7819             this.on("focus", this.preFocus, this);
7820             
7821         }
7822         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7823             this.inputEl().on("keypress", this.filterKeys, this);
7824         }
7825        /* if(this.grow){
7826             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7827             this.el.on("click", this.autoSize,  this);
7828         }
7829         */
7830         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7831             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7832         }
7833         
7834         if (typeof(this.before) == 'object') {
7835             this.before.render(this.el.select('.roo-input-before',true).first());
7836         }
7837         if (typeof(this.after) == 'object') {
7838             this.after.render(this.el.select('.roo-input-after',true).first());
7839         }
7840         
7841         
7842     },
7843     filterValidation : function(e){
7844         if(!e.isNavKeyPress()){
7845             this.validationTask.delay(this.validationDelay);
7846         }
7847     },
7848      /**
7849      * Validates the field value
7850      * @return {Boolean} True if the value is valid, else false
7851      */
7852     validate : function(){
7853         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7854         if(this.disabled || this.validateValue(this.getRawValue())){
7855             this.markValid();
7856             return true;
7857         }
7858         
7859         this.markInvalid();
7860         return false;
7861     },
7862     
7863     
7864     /**
7865      * Validates a value according to the field's validation rules and marks the field as invalid
7866      * if the validation fails
7867      * @param {Mixed} value The value to validate
7868      * @return {Boolean} True if the value is valid, else false
7869      */
7870     validateValue : function(value){
7871         if(value.length < 1)  { // if it's blank
7872             if(this.allowBlank){
7873                 return true;
7874             }
7875             return false;
7876         }
7877         
7878         if(value.length < this.minLength){
7879             return false;
7880         }
7881         if(value.length > this.maxLength){
7882             return false;
7883         }
7884         if(this.vtype){
7885             var vt = Roo.form.VTypes;
7886             if(!vt[this.vtype](value, this)){
7887                 return false;
7888             }
7889         }
7890         if(typeof this.validator == "function"){
7891             var msg = this.validator(value);
7892             if(msg !== true){
7893                 return false;
7894             }
7895         }
7896         
7897         if(this.regex && !this.regex.test(value)){
7898             return false;
7899         }
7900         
7901         return true;
7902     },
7903
7904     
7905     
7906      // private
7907     fireKey : function(e){
7908         //Roo.log('field ' + e.getKey());
7909         if(e.isNavKeyPress()){
7910             this.fireEvent("specialkey", this, e);
7911         }
7912     },
7913     focus : function (selectText){
7914         if(this.rendered){
7915             this.inputEl().focus();
7916             if(selectText === true){
7917                 this.inputEl().dom.select();
7918             }
7919         }
7920         return this;
7921     } ,
7922     
7923     onFocus : function(){
7924         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7925            // this.el.addClass(this.focusClass);
7926         }
7927         if(!this.hasFocus){
7928             this.hasFocus = true;
7929             this.startValue = this.getValue();
7930             this.fireEvent("focus", this);
7931         }
7932     },
7933     
7934     beforeBlur : Roo.emptyFn,
7935
7936     
7937     // private
7938     onBlur : function(){
7939         this.beforeBlur();
7940         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7941             //this.el.removeClass(this.focusClass);
7942         }
7943         this.hasFocus = false;
7944         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7945             this.validate();
7946         }
7947         var v = this.getValue();
7948         if(String(v) !== String(this.startValue)){
7949             this.fireEvent('change', this, v, this.startValue);
7950         }
7951         this.fireEvent("blur", this);
7952     },
7953     
7954     /**
7955      * Resets the current field value to the originally loaded value and clears any validation messages
7956      */
7957     reset : function(){
7958         this.setValue(this.originalValue);
7959         this.validate();
7960     },
7961      /**
7962      * Returns the name of the field
7963      * @return {Mixed} name The name field
7964      */
7965     getName: function(){
7966         return this.name;
7967     },
7968      /**
7969      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
7970      * @return {Mixed} value The field value
7971      */
7972     getValue : function(){
7973         
7974         var v = this.inputEl().getValue();
7975         
7976         return v;
7977     },
7978     /**
7979      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
7980      * @return {Mixed} value The field value
7981      */
7982     getRawValue : function(){
7983         var v = this.inputEl().getValue();
7984         
7985         return v;
7986     },
7987     
7988     /**
7989      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
7990      * @param {Mixed} value The value to set
7991      */
7992     setRawValue : function(v){
7993         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7994     },
7995     
7996     selectText : function(start, end){
7997         var v = this.getRawValue();
7998         if(v.length > 0){
7999             start = start === undefined ? 0 : start;
8000             end = end === undefined ? v.length : end;
8001             var d = this.inputEl().dom;
8002             if(d.setSelectionRange){
8003                 d.setSelectionRange(start, end);
8004             }else if(d.createTextRange){
8005                 var range = d.createTextRange();
8006                 range.moveStart("character", start);
8007                 range.moveEnd("character", v.length-end);
8008                 range.select();
8009             }
8010         }
8011     },
8012     
8013     /**
8014      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8015      * @param {Mixed} value The value to set
8016      */
8017     setValue : function(v){
8018         this.value = v;
8019         if(this.rendered){
8020             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8021             this.validate();
8022         }
8023     },
8024     
8025     /*
8026     processValue : function(value){
8027         if(this.stripCharsRe){
8028             var newValue = value.replace(this.stripCharsRe, '');
8029             if(newValue !== value){
8030                 this.setRawValue(newValue);
8031                 return newValue;
8032             }
8033         }
8034         return value;
8035     },
8036   */
8037     preFocus : function(){
8038         
8039         if(this.selectOnFocus){
8040             this.inputEl().dom.select();
8041         }
8042     },
8043     filterKeys : function(e){
8044         var k = e.getKey();
8045         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8046             return;
8047         }
8048         var c = e.getCharCode(), cc = String.fromCharCode(c);
8049         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8050             return;
8051         }
8052         if(!this.maskRe.test(cc)){
8053             e.stopEvent();
8054         }
8055     },
8056      /**
8057      * Clear any invalid styles/messages for this field
8058      */
8059     clearInvalid : function(){
8060         
8061         if(!this.el || this.preventMark){ // not rendered
8062             return;
8063         }
8064         this.el.removeClass(this.invalidClass);
8065         
8066         this.fireEvent('valid', this);
8067     },
8068     
8069      /**
8070      * Mark this field as valid
8071      */
8072     markValid : function(){
8073         if(!this.el  || this.preventMark){ // not rendered
8074             return;
8075         }
8076         
8077         this.el.removeClass([this.invalidClass, this.validClass]);
8078         
8079         if(this.disabled || this.allowBlank){
8080             return;
8081         }
8082         
8083         this.el.addClass(this.validClass);
8084         
8085         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && this.getValue().length){
8086             
8087             var feedback = this.el.select('.form-control-feedback', true).first();
8088             
8089             if(feedback){
8090                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8091                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8092             }
8093             
8094         }
8095         
8096         this.fireEvent('valid', this);
8097     },
8098     
8099      /**
8100      * Mark this field as invalid
8101      * @param {String} msg The validation message
8102      */
8103     markInvalid : function(msg){
8104         if(!this.el  || this.preventMark){ // not rendered
8105             return;
8106         }
8107         
8108         this.el.removeClass([this.invalidClass, this.validClass]);
8109         
8110         if(this.disabled || this.allowBlank){
8111             return;
8112         }
8113         
8114         this.el.addClass(this.invalidClass);
8115         
8116         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8117             
8118             var feedback = this.el.select('.form-control-feedback', true).first();
8119             
8120             if(feedback){
8121                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8122                 
8123                 if(this.getValue().length){
8124                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8125                 }
8126                 
8127             }
8128             
8129         }
8130         
8131         this.fireEvent('invalid', this, msg);
8132     },
8133     // private
8134     SafariOnKeyDown : function(event)
8135     {
8136         // this is a workaround for a password hang bug on chrome/ webkit.
8137         
8138         var isSelectAll = false;
8139         
8140         if(this.inputEl().dom.selectionEnd > 0){
8141             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8142         }
8143         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8144             event.preventDefault();
8145             this.setValue('');
8146             return;
8147         }
8148         
8149         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8150             
8151             event.preventDefault();
8152             // this is very hacky as keydown always get's upper case.
8153             //
8154             var cc = String.fromCharCode(event.getCharCode());
8155             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8156             
8157         }
8158     },
8159     adjustWidth : function(tag, w){
8160         tag = tag.toLowerCase();
8161         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8162             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8163                 if(tag == 'input'){
8164                     return w + 2;
8165                 }
8166                 if(tag == 'textarea'){
8167                     return w-2;
8168                 }
8169             }else if(Roo.isOpera){
8170                 if(tag == 'input'){
8171                     return w + 2;
8172                 }
8173                 if(tag == 'textarea'){
8174                     return w-2;
8175                 }
8176             }
8177         }
8178         return w;
8179     }
8180     
8181 });
8182
8183  
8184 /*
8185  * - LGPL
8186  *
8187  * Input
8188  * 
8189  */
8190
8191 /**
8192  * @class Roo.bootstrap.TextArea
8193  * @extends Roo.bootstrap.Input
8194  * Bootstrap TextArea class
8195  * @cfg {Number} cols Specifies the visible width of a text area
8196  * @cfg {Number} rows Specifies the visible number of lines in a text area
8197  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8198  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8199  * @cfg {string} html text
8200  * 
8201  * @constructor
8202  * Create a new TextArea
8203  * @param {Object} config The config object
8204  */
8205
8206 Roo.bootstrap.TextArea = function(config){
8207     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8208    
8209 };
8210
8211 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8212      
8213     cols : false,
8214     rows : 5,
8215     readOnly : false,
8216     warp : 'soft',
8217     resize : false,
8218     value: false,
8219     html: false,
8220     
8221     getAutoCreate : function(){
8222         
8223         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8224         
8225         var id = Roo.id();
8226         
8227         var cfg = {};
8228         
8229         var input =  {
8230             tag: 'textarea',
8231             id : id,
8232             warp : this.warp,
8233             rows : this.rows,
8234             value : this.value || '',
8235             html: this.html || '',
8236             cls : 'form-control',
8237             placeholder : this.placeholder || '' 
8238             
8239         };
8240         
8241         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8242             input.maxLength = this.maxLength;
8243         }
8244         
8245         if(this.resize){
8246             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8247         }
8248         
8249         if(this.cols){
8250             input.cols = this.cols;
8251         }
8252         
8253         if (this.readOnly) {
8254             input.readonly = true;
8255         }
8256         
8257         if (this.name) {
8258             input.name = this.name;
8259         }
8260         
8261         if (this.size) {
8262             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8263         }
8264         
8265         var settings=this;
8266         ['xs','sm','md','lg'].map(function(size){
8267             if (settings[size]) {
8268                 cfg.cls += ' col-' + size + '-' + settings[size];
8269             }
8270         });
8271         
8272         var inputblock = input;
8273         
8274         if(this.hasFeedback && !this.allowBlank){
8275             
8276             var feedback = {
8277                 tag: 'span',
8278                 cls: 'glyphicon form-control-feedback'
8279             };
8280
8281             inputblock = {
8282                 cls : 'has-feedback',
8283                 cn :  [
8284                     input,
8285                     feedback
8286                 ] 
8287             };  
8288         }
8289         
8290         
8291         if (this.before || this.after) {
8292             
8293             inputblock = {
8294                 cls : 'input-group',
8295                 cn :  [] 
8296             };
8297             if (this.before) {
8298                 inputblock.cn.push({
8299                     tag :'span',
8300                     cls : 'input-group-addon',
8301                     html : this.before
8302                 });
8303             }
8304             
8305             inputblock.cn.push(input);
8306             
8307             if(this.hasFeedback && !this.allowBlank){
8308                 inputblock.cls += ' has-feedback';
8309                 inputblock.cn.push(feedback);
8310             }
8311             
8312             if (this.after) {
8313                 inputblock.cn.push({
8314                     tag :'span',
8315                     cls : 'input-group-addon',
8316                     html : this.after
8317                 });
8318             }
8319             
8320         }
8321         
8322         if (align ==='left' && this.fieldLabel.length) {
8323                 Roo.log("left and has label");
8324                 cfg.cn = [
8325                     
8326                     {
8327                         tag: 'label',
8328                         'for' :  id,
8329                         cls : 'control-label col-sm-' + this.labelWidth,
8330                         html : this.fieldLabel
8331                         
8332                     },
8333                     {
8334                         cls : "col-sm-" + (12 - this.labelWidth), 
8335                         cn: [
8336                             inputblock
8337                         ]
8338                     }
8339                     
8340                 ];
8341         } else if ( this.fieldLabel.length) {
8342                 Roo.log(" label");
8343                  cfg.cn = [
8344                    
8345                     {
8346                         tag: 'label',
8347                         //cls : 'input-group-addon',
8348                         html : this.fieldLabel
8349                         
8350                     },
8351                     
8352                     inputblock
8353                     
8354                 ];
8355
8356         } else {
8357             
8358                    Roo.log(" no label && no align");
8359                 cfg.cn = [
8360                     
8361                         inputblock
8362                     
8363                 ];
8364                 
8365                 
8366         }
8367         
8368         if (this.disabled) {
8369             input.disabled=true;
8370         }
8371         
8372         return cfg;
8373         
8374     },
8375     /**
8376      * return the real textarea element.
8377      */
8378     inputEl: function ()
8379     {
8380         return this.el.select('textarea.form-control',true).first();
8381     }
8382 });
8383
8384  
8385 /*
8386  * - LGPL
8387  *
8388  * trigger field - base class for combo..
8389  * 
8390  */
8391  
8392 /**
8393  * @class Roo.bootstrap.TriggerField
8394  * @extends Roo.bootstrap.Input
8395  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8396  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8397  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8398  * for which you can provide a custom implementation.  For example:
8399  * <pre><code>
8400 var trigger = new Roo.bootstrap.TriggerField();
8401 trigger.onTriggerClick = myTriggerFn;
8402 trigger.applyTo('my-field');
8403 </code></pre>
8404  *
8405  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8406  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8407  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
8408  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8409  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8410
8411  * @constructor
8412  * Create a new TriggerField.
8413  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8414  * to the base TextField)
8415  */
8416 Roo.bootstrap.TriggerField = function(config){
8417     this.mimicing = false;
8418     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8419 };
8420
8421 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
8422     /**
8423      * @cfg {String} triggerClass A CSS class to apply to the trigger
8424      */
8425      /**
8426      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8427      */
8428     hideTrigger:false,
8429
8430     /**
8431      * @cfg {Boolean} removable (true|false) special filter default false
8432      */
8433     removable : false,
8434     
8435     /** @cfg {Boolean} grow @hide */
8436     /** @cfg {Number} growMin @hide */
8437     /** @cfg {Number} growMax @hide */
8438
8439     /**
8440      * @hide 
8441      * @method
8442      */
8443     autoSize: Roo.emptyFn,
8444     // private
8445     monitorTab : true,
8446     // private
8447     deferHeight : true,
8448
8449     
8450     actionMode : 'wrap',
8451     
8452     caret : false,
8453     
8454     
8455     getAutoCreate : function(){
8456        
8457         var align = this.labelAlign || this.parentLabelAlign();
8458         
8459         var id = Roo.id();
8460         
8461         var cfg = {
8462             cls: 'form-group' //input-group
8463         };
8464         
8465         
8466         var input =  {
8467             tag: 'input',
8468             id : id,
8469             type : this.inputType,
8470             cls : 'form-control',
8471             autocomplete: 'new-password',
8472             placeholder : this.placeholder || '' 
8473             
8474         };
8475         if (this.name) {
8476             input.name = this.name;
8477         }
8478         if (this.size) {
8479             input.cls += ' input-' + this.size;
8480         }
8481         
8482         if (this.disabled) {
8483             input.disabled=true;
8484         }
8485         
8486         var inputblock = input;
8487         
8488         if(this.hasFeedback && !this.allowBlank){
8489             
8490             var feedback = {
8491                 tag: 'span',
8492                 cls: 'glyphicon form-control-feedback'
8493             };
8494             
8495             if(this.removable && !this.editable && !this.tickable){
8496                 inputblock = {
8497                     cls : 'has-feedback',
8498                     cn :  [
8499                         inputblock,
8500                         {
8501                             tag: 'button',
8502                             html : 'x',
8503                             cls : 'roo-combo-removable-btn close'
8504                         },
8505                         feedback
8506                     ] 
8507                 };
8508             } else {
8509                 inputblock = {
8510                     cls : 'has-feedback',
8511                     cn :  [
8512                         inputblock,
8513                         feedback
8514                     ] 
8515                 };
8516             }
8517               
8518         } else {
8519             if(this.removable && !this.editable && !this.tickable){
8520                 inputblock = {
8521                     cls : 'roo-removable',
8522                     cn :  [
8523                         inputblock,
8524                         {
8525                             tag: 'button',
8526                             html : 'x',
8527                             cls : 'roo-combo-removable-btn close'
8528                         }
8529                     ] 
8530                 };
8531             }
8532         }
8533         
8534         if (this.before || this.after) {
8535             
8536             inputblock = {
8537                 cls : 'input-group',
8538                 cn :  [] 
8539             };
8540             if (this.before) {
8541                 inputblock.cn.push({
8542                     tag :'span',
8543                     cls : 'input-group-addon',
8544                     html : this.before
8545                 });
8546             }
8547             
8548             inputblock.cn.push(input);
8549             
8550             if(this.hasFeedback && !this.allowBlank){
8551                 inputblock.cls += ' has-feedback';
8552                 inputblock.cn.push(feedback);
8553             }
8554             
8555             if (this.after) {
8556                 inputblock.cn.push({
8557                     tag :'span',
8558                     cls : 'input-group-addon',
8559                     html : this.after
8560                 });
8561             }
8562             
8563         };
8564         
8565         var box = {
8566             tag: 'div',
8567             cn: [
8568                 {
8569                     tag: 'input',
8570                     type : 'hidden',
8571                     cls: 'form-hidden-field'
8572                 },
8573                 inputblock
8574             ]
8575             
8576         };
8577         
8578         if(this.multiple){
8579             Roo.log('multiple');
8580             
8581             box = {
8582                 tag: 'div',
8583                 cn: [
8584                     {
8585                         tag: 'input',
8586                         type : 'hidden',
8587                         cls: 'form-hidden-field'
8588                     },
8589                     {
8590                         tag: 'ul',
8591                         cls: 'select2-choices',
8592                         cn:[
8593                             {
8594                                 tag: 'li',
8595                                 cls: 'select2-search-field',
8596                                 cn: [
8597
8598                                     inputblock
8599                                 ]
8600                             }
8601                         ]
8602                     }
8603                 ]
8604             }
8605         };
8606         
8607         var combobox = {
8608             cls: 'select2-container input-group',
8609             cn: [
8610                 box
8611 //                {
8612 //                    tag: 'ul',
8613 //                    cls: 'typeahead typeahead-long dropdown-menu',
8614 //                    style: 'display:none'
8615 //                }
8616             ]
8617         };
8618         
8619         if(!this.multiple && this.showToggleBtn){
8620             
8621             var caret = {
8622                         tag: 'span',
8623                         cls: 'caret'
8624              };
8625             if (this.caret != false) {
8626                 caret = {
8627                      tag: 'i',
8628                      cls: 'fa fa-' + this.caret
8629                 };
8630                 
8631             }
8632             
8633             combobox.cn.push({
8634                 tag :'span',
8635                 cls : 'input-group-addon btn dropdown-toggle',
8636                 cn : [
8637                     caret,
8638                     {
8639                         tag: 'span',
8640                         cls: 'combobox-clear',
8641                         cn  : [
8642                             {
8643                                 tag : 'i',
8644                                 cls: 'icon-remove'
8645                             }
8646                         ]
8647                     }
8648                 ]
8649
8650             })
8651         }
8652         
8653         if(this.multiple){
8654             combobox.cls += ' select2-container-multi';
8655         }
8656         
8657         if (align ==='left' && this.fieldLabel.length) {
8658             
8659                 Roo.log("left and has label");
8660                 cfg.cn = [
8661                     
8662                     {
8663                         tag: 'label',
8664                         'for' :  id,
8665                         cls : 'control-label col-sm-' + this.labelWidth,
8666                         html : this.fieldLabel
8667                         
8668                     },
8669                     {
8670                         cls : "col-sm-" + (12 - this.labelWidth), 
8671                         cn: [
8672                             combobox
8673                         ]
8674                     }
8675                     
8676                 ];
8677         } else if ( this.fieldLabel.length) {
8678                 Roo.log(" label");
8679                  cfg.cn = [
8680                    
8681                     {
8682                         tag: 'label',
8683                         //cls : 'input-group-addon',
8684                         html : this.fieldLabel
8685                         
8686                     },
8687                     
8688                     combobox
8689                     
8690                 ];
8691
8692         } else {
8693             
8694                 Roo.log(" no label && no align");
8695                 cfg = combobox
8696                      
8697                 
8698         }
8699          
8700         var settings=this;
8701         ['xs','sm','md','lg'].map(function(size){
8702             if (settings[size]) {
8703                 cfg.cls += ' col-' + size + '-' + settings[size];
8704             }
8705         });
8706         
8707         return cfg;
8708         
8709     },
8710     
8711     
8712     
8713     // private
8714     onResize : function(w, h){
8715 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8716 //        if(typeof w == 'number'){
8717 //            var x = w - this.trigger.getWidth();
8718 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8719 //            this.trigger.setStyle('left', x+'px');
8720 //        }
8721     },
8722
8723     // private
8724     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8725
8726     // private
8727     getResizeEl : function(){
8728         return this.inputEl();
8729     },
8730
8731     // private
8732     getPositionEl : function(){
8733         return this.inputEl();
8734     },
8735
8736     // private
8737     alignErrorIcon : function(){
8738         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8739     },
8740
8741     // private
8742     initEvents : function(){
8743         
8744         this.createList();
8745         
8746         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8747         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8748         if(!this.multiple && this.showToggleBtn){
8749             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8750             if(this.hideTrigger){
8751                 this.trigger.setDisplayed(false);
8752             }
8753             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8754         }
8755         
8756         if(this.multiple){
8757             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8758         }
8759         
8760         if(this.removable && !this.editable && !this.tickable){
8761             var close = this.closeTriggerEl();
8762             
8763             if(close){
8764                 close.setVisibilityMode(Roo.Element.DISPALY).hide();
8765                 close.on('click', this.removeBtnClick, this, close);
8766             }
8767         }
8768         
8769         //this.trigger.addClassOnOver('x-form-trigger-over');
8770         //this.trigger.addClassOnClick('x-form-trigger-click');
8771         
8772         //if(!this.width){
8773         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8774         //}
8775     },
8776     
8777     closeTriggerEl : function()
8778     {
8779         var close = this.el.select('.roo-combo-removable-btn', true).first();
8780         return close ? close : false;
8781     },
8782     
8783     removeBtnClick : function(e, h, el)
8784     {
8785         e.preventDefault();
8786         
8787         this.fireEvent("remove", this);
8788     },
8789     
8790     createList : function()
8791     {
8792         this.list = Roo.get(document.body).createChild({
8793             tag: 'ul',
8794             cls: 'typeahead typeahead-long dropdown-menu',
8795             style: 'display:none'
8796         });
8797         
8798         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8799         
8800     },
8801
8802     // private
8803     initTrigger : function(){
8804        
8805     },
8806
8807     // private
8808     onDestroy : function(){
8809         if(this.trigger){
8810             this.trigger.removeAllListeners();
8811           //  this.trigger.remove();
8812         }
8813         //if(this.wrap){
8814         //    this.wrap.remove();
8815         //}
8816         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8817     },
8818
8819     // private
8820     onFocus : function(){
8821         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8822         /*
8823         if(!this.mimicing){
8824             this.wrap.addClass('x-trigger-wrap-focus');
8825             this.mimicing = true;
8826             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8827             if(this.monitorTab){
8828                 this.el.on("keydown", this.checkTab, this);
8829             }
8830         }
8831         */
8832     },
8833
8834     // private
8835     checkTab : function(e){
8836         if(e.getKey() == e.TAB){
8837             this.triggerBlur();
8838         }
8839     },
8840
8841     // private
8842     onBlur : function(){
8843         // do nothing
8844     },
8845
8846     // private
8847     mimicBlur : function(e, t){
8848         /*
8849         if(!this.wrap.contains(t) && this.validateBlur()){
8850             this.triggerBlur();
8851         }
8852         */
8853     },
8854
8855     // private
8856     triggerBlur : function(){
8857         this.mimicing = false;
8858         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8859         if(this.monitorTab){
8860             this.el.un("keydown", this.checkTab, this);
8861         }
8862         //this.wrap.removeClass('x-trigger-wrap-focus');
8863         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8864     },
8865
8866     // private
8867     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8868     validateBlur : function(e, t){
8869         return true;
8870     },
8871
8872     // private
8873     onDisable : function(){
8874         this.inputEl().dom.disabled = true;
8875         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8876         //if(this.wrap){
8877         //    this.wrap.addClass('x-item-disabled');
8878         //}
8879     },
8880
8881     // private
8882     onEnable : function(){
8883         this.inputEl().dom.disabled = false;
8884         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8885         //if(this.wrap){
8886         //    this.el.removeClass('x-item-disabled');
8887         //}
8888     },
8889
8890     // private
8891     onShow : function(){
8892         var ae = this.getActionEl();
8893         
8894         if(ae){
8895             ae.dom.style.display = '';
8896             ae.dom.style.visibility = 'visible';
8897         }
8898     },
8899
8900     // private
8901     
8902     onHide : function(){
8903         var ae = this.getActionEl();
8904         ae.dom.style.display = 'none';
8905     },
8906
8907     /**
8908      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8909      * by an implementing function.
8910      * @method
8911      * @param {EventObject} e
8912      */
8913     onTriggerClick : Roo.emptyFn
8914 });
8915  /*
8916  * Based on:
8917  * Ext JS Library 1.1.1
8918  * Copyright(c) 2006-2007, Ext JS, LLC.
8919  *
8920  * Originally Released Under LGPL - original licence link has changed is not relivant.
8921  *
8922  * Fork - LGPL
8923  * <script type="text/javascript">
8924  */
8925
8926
8927 /**
8928  * @class Roo.data.SortTypes
8929  * @singleton
8930  * Defines the default sorting (casting?) comparison functions used when sorting data.
8931  */
8932 Roo.data.SortTypes = {
8933     /**
8934      * Default sort that does nothing
8935      * @param {Mixed} s The value being converted
8936      * @return {Mixed} The comparison value
8937      */
8938     none : function(s){
8939         return s;
8940     },
8941     
8942     /**
8943      * The regular expression used to strip tags
8944      * @type {RegExp}
8945      * @property
8946      */
8947     stripTagsRE : /<\/?[^>]+>/gi,
8948     
8949     /**
8950      * Strips all HTML tags to sort on text only
8951      * @param {Mixed} s The value being converted
8952      * @return {String} The comparison value
8953      */
8954     asText : function(s){
8955         return String(s).replace(this.stripTagsRE, "");
8956     },
8957     
8958     /**
8959      * Strips all HTML tags to sort on text only - Case insensitive
8960      * @param {Mixed} s The value being converted
8961      * @return {String} The comparison value
8962      */
8963     asUCText : function(s){
8964         return String(s).toUpperCase().replace(this.stripTagsRE, "");
8965     },
8966     
8967     /**
8968      * Case insensitive string
8969      * @param {Mixed} s The value being converted
8970      * @return {String} The comparison value
8971      */
8972     asUCString : function(s) {
8973         return String(s).toUpperCase();
8974     },
8975     
8976     /**
8977      * Date sorting
8978      * @param {Mixed} s The value being converted
8979      * @return {Number} The comparison value
8980      */
8981     asDate : function(s) {
8982         if(!s){
8983             return 0;
8984         }
8985         if(s instanceof Date){
8986             return s.getTime();
8987         }
8988         return Date.parse(String(s));
8989     },
8990     
8991     /**
8992      * Float sorting
8993      * @param {Mixed} s The value being converted
8994      * @return {Float} The comparison value
8995      */
8996     asFloat : function(s) {
8997         var val = parseFloat(String(s).replace(/,/g, ""));
8998         if(isNaN(val)) val = 0;
8999         return val;
9000     },
9001     
9002     /**
9003      * Integer sorting
9004      * @param {Mixed} s The value being converted
9005      * @return {Number} The comparison value
9006      */
9007     asInt : function(s) {
9008         var val = parseInt(String(s).replace(/,/g, ""));
9009         if(isNaN(val)) val = 0;
9010         return val;
9011     }
9012 };/*
9013  * Based on:
9014  * Ext JS Library 1.1.1
9015  * Copyright(c) 2006-2007, Ext JS, LLC.
9016  *
9017  * Originally Released Under LGPL - original licence link has changed is not relivant.
9018  *
9019  * Fork - LGPL
9020  * <script type="text/javascript">
9021  */
9022
9023 /**
9024 * @class Roo.data.Record
9025  * Instances of this class encapsulate both record <em>definition</em> information, and record
9026  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9027  * to access Records cached in an {@link Roo.data.Store} object.<br>
9028  * <p>
9029  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9030  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9031  * objects.<br>
9032  * <p>
9033  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9034  * @constructor
9035  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9036  * {@link #create}. The parameters are the same.
9037  * @param {Array} data An associative Array of data values keyed by the field name.
9038  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9039  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9040  * not specified an integer id is generated.
9041  */
9042 Roo.data.Record = function(data, id){
9043     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9044     this.data = data;
9045 };
9046
9047 /**
9048  * Generate a constructor for a specific record layout.
9049  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9050  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9051  * Each field definition object may contain the following properties: <ul>
9052  * <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,
9053  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9054  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9055  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9056  * is being used, then this is a string containing the javascript expression to reference the data relative to 
9057  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9058  * to the data item relative to the record element. If the mapping expression is the same as the field name,
9059  * this may be omitted.</p></li>
9060  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9061  * <ul><li>auto (Default, implies no conversion)</li>
9062  * <li>string</li>
9063  * <li>int</li>
9064  * <li>float</li>
9065  * <li>boolean</li>
9066  * <li>date</li></ul></p></li>
9067  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9068  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9069  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9070  * by the Reader into an object that will be stored in the Record. It is passed the
9071  * following parameters:<ul>
9072  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9073  * </ul></p></li>
9074  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9075  * </ul>
9076  * <br>usage:<br><pre><code>
9077 var TopicRecord = Roo.data.Record.create(
9078     {name: 'title', mapping: 'topic_title'},
9079     {name: 'author', mapping: 'username'},
9080     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9081     {name: 'lastPost', mapping: 'post_time', type: 'date'},
9082     {name: 'lastPoster', mapping: 'user2'},
9083     {name: 'excerpt', mapping: 'post_text'}
9084 );
9085
9086 var myNewRecord = new TopicRecord({
9087     title: 'Do my job please',
9088     author: 'noobie',
9089     totalPosts: 1,
9090     lastPost: new Date(),
9091     lastPoster: 'Animal',
9092     excerpt: 'No way dude!'
9093 });
9094 myStore.add(myNewRecord);
9095 </code></pre>
9096  * @method create
9097  * @static
9098  */
9099 Roo.data.Record.create = function(o){
9100     var f = function(){
9101         f.superclass.constructor.apply(this, arguments);
9102     };
9103     Roo.extend(f, Roo.data.Record);
9104     var p = f.prototype;
9105     p.fields = new Roo.util.MixedCollection(false, function(field){
9106         return field.name;
9107     });
9108     for(var i = 0, len = o.length; i < len; i++){
9109         p.fields.add(new Roo.data.Field(o[i]));
9110     }
9111     f.getField = function(name){
9112         return p.fields.get(name);  
9113     };
9114     return f;
9115 };
9116
9117 Roo.data.Record.AUTO_ID = 1000;
9118 Roo.data.Record.EDIT = 'edit';
9119 Roo.data.Record.REJECT = 'reject';
9120 Roo.data.Record.COMMIT = 'commit';
9121
9122 Roo.data.Record.prototype = {
9123     /**
9124      * Readonly flag - true if this record has been modified.
9125      * @type Boolean
9126      */
9127     dirty : false,
9128     editing : false,
9129     error: null,
9130     modified: null,
9131
9132     // private
9133     join : function(store){
9134         this.store = store;
9135     },
9136
9137     /**
9138      * Set the named field to the specified value.
9139      * @param {String} name The name of the field to set.
9140      * @param {Object} value The value to set the field to.
9141      */
9142     set : function(name, value){
9143         if(this.data[name] == value){
9144             return;
9145         }
9146         this.dirty = true;
9147         if(!this.modified){
9148             this.modified = {};
9149         }
9150         if(typeof this.modified[name] == 'undefined'){
9151             this.modified[name] = this.data[name];
9152         }
9153         this.data[name] = value;
9154         if(!this.editing && this.store){
9155             this.store.afterEdit(this);
9156         }       
9157     },
9158
9159     /**
9160      * Get the value of the named field.
9161      * @param {String} name The name of the field to get the value of.
9162      * @return {Object} The value of the field.
9163      */
9164     get : function(name){
9165         return this.data[name]; 
9166     },
9167
9168     // private
9169     beginEdit : function(){
9170         this.editing = true;
9171         this.modified = {}; 
9172     },
9173
9174     // private
9175     cancelEdit : function(){
9176         this.editing = false;
9177         delete this.modified;
9178     },
9179
9180     // private
9181     endEdit : function(){
9182         this.editing = false;
9183         if(this.dirty && this.store){
9184             this.store.afterEdit(this);
9185         }
9186     },
9187
9188     /**
9189      * Usually called by the {@link Roo.data.Store} which owns the Record.
9190      * Rejects all changes made to the Record since either creation, or the last commit operation.
9191      * Modified fields are reverted to their original values.
9192      * <p>
9193      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9194      * of reject operations.
9195      */
9196     reject : function(){
9197         var m = this.modified;
9198         for(var n in m){
9199             if(typeof m[n] != "function"){
9200                 this.data[n] = m[n];
9201             }
9202         }
9203         this.dirty = false;
9204         delete this.modified;
9205         this.editing = false;
9206         if(this.store){
9207             this.store.afterReject(this);
9208         }
9209     },
9210
9211     /**
9212      * Usually called by the {@link Roo.data.Store} which owns the Record.
9213      * Commits all changes made to the Record since either creation, or the last commit operation.
9214      * <p>
9215      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9216      * of commit operations.
9217      */
9218     commit : function(){
9219         this.dirty = false;
9220         delete this.modified;
9221         this.editing = false;
9222         if(this.store){
9223             this.store.afterCommit(this);
9224         }
9225     },
9226
9227     // private
9228     hasError : function(){
9229         return this.error != null;
9230     },
9231
9232     // private
9233     clearError : function(){
9234         this.error = null;
9235     },
9236
9237     /**
9238      * Creates a copy of this record.
9239      * @param {String} id (optional) A new record id if you don't want to use this record's id
9240      * @return {Record}
9241      */
9242     copy : function(newId) {
9243         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9244     }
9245 };/*
9246  * Based on:
9247  * Ext JS Library 1.1.1
9248  * Copyright(c) 2006-2007, Ext JS, LLC.
9249  *
9250  * Originally Released Under LGPL - original licence link has changed is not relivant.
9251  *
9252  * Fork - LGPL
9253  * <script type="text/javascript">
9254  */
9255
9256
9257
9258 /**
9259  * @class Roo.data.Store
9260  * @extends Roo.util.Observable
9261  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9262  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9263  * <p>
9264  * 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
9265  * has no knowledge of the format of the data returned by the Proxy.<br>
9266  * <p>
9267  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9268  * instances from the data object. These records are cached and made available through accessor functions.
9269  * @constructor
9270  * Creates a new Store.
9271  * @param {Object} config A config object containing the objects needed for the Store to access data,
9272  * and read the data into Records.
9273  */
9274 Roo.data.Store = function(config){
9275     this.data = new Roo.util.MixedCollection(false);
9276     this.data.getKey = function(o){
9277         return o.id;
9278     };
9279     this.baseParams = {};
9280     // private
9281     this.paramNames = {
9282         "start" : "start",
9283         "limit" : "limit",
9284         "sort" : "sort",
9285         "dir" : "dir",
9286         "multisort" : "_multisort"
9287     };
9288
9289     if(config && config.data){
9290         this.inlineData = config.data;
9291         delete config.data;
9292     }
9293
9294     Roo.apply(this, config);
9295     
9296     if(this.reader){ // reader passed
9297         this.reader = Roo.factory(this.reader, Roo.data);
9298         this.reader.xmodule = this.xmodule || false;
9299         if(!this.recordType){
9300             this.recordType = this.reader.recordType;
9301         }
9302         if(this.reader.onMetaChange){
9303             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9304         }
9305     }
9306
9307     if(this.recordType){
9308         this.fields = this.recordType.prototype.fields;
9309     }
9310     this.modified = [];
9311
9312     this.addEvents({
9313         /**
9314          * @event datachanged
9315          * Fires when the data cache has changed, and a widget which is using this Store
9316          * as a Record cache should refresh its view.
9317          * @param {Store} this
9318          */
9319         datachanged : true,
9320         /**
9321          * @event metachange
9322          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9323          * @param {Store} this
9324          * @param {Object} meta The JSON metadata
9325          */
9326         metachange : true,
9327         /**
9328          * @event add
9329          * Fires when Records have been added to the Store
9330          * @param {Store} this
9331          * @param {Roo.data.Record[]} records The array of Records added
9332          * @param {Number} index The index at which the record(s) were added
9333          */
9334         add : true,
9335         /**
9336          * @event remove
9337          * Fires when a Record has been removed from the Store
9338          * @param {Store} this
9339          * @param {Roo.data.Record} record The Record that was removed
9340          * @param {Number} index The index at which the record was removed
9341          */
9342         remove : true,
9343         /**
9344          * @event update
9345          * Fires when a Record has been updated
9346          * @param {Store} this
9347          * @param {Roo.data.Record} record The Record that was updated
9348          * @param {String} operation The update operation being performed.  Value may be one of:
9349          * <pre><code>
9350  Roo.data.Record.EDIT
9351  Roo.data.Record.REJECT
9352  Roo.data.Record.COMMIT
9353          * </code></pre>
9354          */
9355         update : true,
9356         /**
9357          * @event clear
9358          * Fires when the data cache has been cleared.
9359          * @param {Store} this
9360          */
9361         clear : true,
9362         /**
9363          * @event beforeload
9364          * Fires before a request is made for a new data object.  If the beforeload handler returns false
9365          * the load action will be canceled.
9366          * @param {Store} this
9367          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9368          */
9369         beforeload : true,
9370         /**
9371          * @event beforeloadadd
9372          * Fires after a new set of Records has been loaded.
9373          * @param {Store} this
9374          * @param {Roo.data.Record[]} records The Records that were loaded
9375          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9376          */
9377         beforeloadadd : true,
9378         /**
9379          * @event load
9380          * Fires after a new set of Records has been loaded, before they are added to the store.
9381          * @param {Store} this
9382          * @param {Roo.data.Record[]} records The Records that were loaded
9383          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9384          * @params {Object} return from reader
9385          */
9386         load : true,
9387         /**
9388          * @event loadexception
9389          * Fires if an exception occurs in the Proxy during loading.
9390          * Called with the signature of the Proxy's "loadexception" event.
9391          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9392          * 
9393          * @param {Proxy} 
9394          * @param {Object} return from JsonData.reader() - success, totalRecords, records
9395          * @param {Object} load options 
9396          * @param {Object} jsonData from your request (normally this contains the Exception)
9397          */
9398         loadexception : true
9399     });
9400     
9401     if(this.proxy){
9402         this.proxy = Roo.factory(this.proxy, Roo.data);
9403         this.proxy.xmodule = this.xmodule || false;
9404         this.relayEvents(this.proxy,  ["loadexception"]);
9405     }
9406     this.sortToggle = {};
9407     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9408
9409     Roo.data.Store.superclass.constructor.call(this);
9410
9411     if(this.inlineData){
9412         this.loadData(this.inlineData);
9413         delete this.inlineData;
9414     }
9415 };
9416
9417 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9418      /**
9419     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
9420     * without a remote query - used by combo/forms at present.
9421     */
9422     
9423     /**
9424     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9425     */
9426     /**
9427     * @cfg {Array} data Inline data to be loaded when the store is initialized.
9428     */
9429     /**
9430     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9431     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9432     */
9433     /**
9434     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9435     * on any HTTP request
9436     */
9437     /**
9438     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9439     */
9440     /**
9441     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9442     */
9443     multiSort: false,
9444     /**
9445     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9446     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9447     */
9448     remoteSort : false,
9449
9450     /**
9451     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9452      * loaded or when a record is removed. (defaults to false).
9453     */
9454     pruneModifiedRecords : false,
9455
9456     // private
9457     lastOptions : null,
9458
9459     /**
9460      * Add Records to the Store and fires the add event.
9461      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9462      */
9463     add : function(records){
9464         records = [].concat(records);
9465         for(var i = 0, len = records.length; i < len; i++){
9466             records[i].join(this);
9467         }
9468         var index = this.data.length;
9469         this.data.addAll(records);
9470         this.fireEvent("add", this, records, index);
9471     },
9472
9473     /**
9474      * Remove a Record from the Store and fires the remove event.
9475      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9476      */
9477     remove : function(record){
9478         var index = this.data.indexOf(record);
9479         this.data.removeAt(index);
9480         if(this.pruneModifiedRecords){
9481             this.modified.remove(record);
9482         }
9483         this.fireEvent("remove", this, record, index);
9484     },
9485
9486     /**
9487      * Remove all Records from the Store and fires the clear event.
9488      */
9489     removeAll : function(){
9490         this.data.clear();
9491         if(this.pruneModifiedRecords){
9492             this.modified = [];
9493         }
9494         this.fireEvent("clear", this);
9495     },
9496
9497     /**
9498      * Inserts Records to the Store at the given index and fires the add event.
9499      * @param {Number} index The start index at which to insert the passed Records.
9500      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9501      */
9502     insert : function(index, records){
9503         records = [].concat(records);
9504         for(var i = 0, len = records.length; i < len; i++){
9505             this.data.insert(index, records[i]);
9506             records[i].join(this);
9507         }
9508         this.fireEvent("add", this, records, index);
9509     },
9510
9511     /**
9512      * Get the index within the cache of the passed Record.
9513      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9514      * @return {Number} The index of the passed Record. Returns -1 if not found.
9515      */
9516     indexOf : function(record){
9517         return this.data.indexOf(record);
9518     },
9519
9520     /**
9521      * Get the index within the cache of the Record with the passed id.
9522      * @param {String} id The id of the Record to find.
9523      * @return {Number} The index of the Record. Returns -1 if not found.
9524      */
9525     indexOfId : function(id){
9526         return this.data.indexOfKey(id);
9527     },
9528
9529     /**
9530      * Get the Record with the specified id.
9531      * @param {String} id The id of the Record to find.
9532      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9533      */
9534     getById : function(id){
9535         return this.data.key(id);
9536     },
9537
9538     /**
9539      * Get the Record at the specified index.
9540      * @param {Number} index The index of the Record to find.
9541      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9542      */
9543     getAt : function(index){
9544         return this.data.itemAt(index);
9545     },
9546
9547     /**
9548      * Returns a range of Records between specified indices.
9549      * @param {Number} startIndex (optional) The starting index (defaults to 0)
9550      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9551      * @return {Roo.data.Record[]} An array of Records
9552      */
9553     getRange : function(start, end){
9554         return this.data.getRange(start, end);
9555     },
9556
9557     // private
9558     storeOptions : function(o){
9559         o = Roo.apply({}, o);
9560         delete o.callback;
9561         delete o.scope;
9562         this.lastOptions = o;
9563     },
9564
9565     /**
9566      * Loads the Record cache from the configured Proxy using the configured Reader.
9567      * <p>
9568      * If using remote paging, then the first load call must specify the <em>start</em>
9569      * and <em>limit</em> properties in the options.params property to establish the initial
9570      * position within the dataset, and the number of Records to cache on each read from the Proxy.
9571      * <p>
9572      * <strong>It is important to note that for remote data sources, loading is asynchronous,
9573      * and this call will return before the new data has been loaded. Perform any post-processing
9574      * in a callback function, or in a "load" event handler.</strong>
9575      * <p>
9576      * @param {Object} options An object containing properties which control loading options:<ul>
9577      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9578      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9579      * passed the following arguments:<ul>
9580      * <li>r : Roo.data.Record[]</li>
9581      * <li>options: Options object from the load call</li>
9582      * <li>success: Boolean success indicator</li></ul></li>
9583      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9584      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9585      * </ul>
9586      */
9587     load : function(options){
9588         options = options || {};
9589         if(this.fireEvent("beforeload", this, options) !== false){
9590             this.storeOptions(options);
9591             var p = Roo.apply(options.params || {}, this.baseParams);
9592             // if meta was not loaded from remote source.. try requesting it.
9593             if (!this.reader.metaFromRemote) {
9594                 p._requestMeta = 1;
9595             }
9596             if(this.sortInfo && this.remoteSort){
9597                 var pn = this.paramNames;
9598                 p[pn["sort"]] = this.sortInfo.field;
9599                 p[pn["dir"]] = this.sortInfo.direction;
9600             }
9601             if (this.multiSort) {
9602                 var pn = this.paramNames;
9603                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9604             }
9605             
9606             this.proxy.load(p, this.reader, this.loadRecords, this, options);
9607         }
9608     },
9609
9610     /**
9611      * Reloads the Record cache from the configured Proxy using the configured Reader and
9612      * the options from the last load operation performed.
9613      * @param {Object} options (optional) An object containing properties which may override the options
9614      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9615      * the most recently used options are reused).
9616      */
9617     reload : function(options){
9618         this.load(Roo.applyIf(options||{}, this.lastOptions));
9619     },
9620
9621     // private
9622     // Called as a callback by the Reader during a load operation.
9623     loadRecords : function(o, options, success){
9624         if(!o || success === false){
9625             if(success !== false){
9626                 this.fireEvent("load", this, [], options, o);
9627             }
9628             if(options.callback){
9629                 options.callback.call(options.scope || this, [], options, false);
9630             }
9631             return;
9632         }
9633         // if data returned failure - throw an exception.
9634         if (o.success === false) {
9635             // show a message if no listener is registered.
9636             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9637                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9638             }
9639             // loadmask wil be hooked into this..
9640             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9641             return;
9642         }
9643         var r = o.records, t = o.totalRecords || r.length;
9644         
9645         this.fireEvent("beforeloadadd", this, r, options, o);
9646         
9647         if(!options || options.add !== true){
9648             if(this.pruneModifiedRecords){
9649                 this.modified = [];
9650             }
9651             for(var i = 0, len = r.length; i < len; i++){
9652                 r[i].join(this);
9653             }
9654             if(this.snapshot){
9655                 this.data = this.snapshot;
9656                 delete this.snapshot;
9657             }
9658             this.data.clear();
9659             this.data.addAll(r);
9660             this.totalLength = t;
9661             this.applySort();
9662             this.fireEvent("datachanged", this);
9663         }else{
9664             this.totalLength = Math.max(t, this.data.length+r.length);
9665             this.add(r);
9666         }
9667         this.fireEvent("load", this, r, options, o);
9668         if(options.callback){
9669             options.callback.call(options.scope || this, r, options, true);
9670         }
9671     },
9672
9673
9674     /**
9675      * Loads data from a passed data block. A Reader which understands the format of the data
9676      * must have been configured in the constructor.
9677      * @param {Object} data The data block from which to read the Records.  The format of the data expected
9678      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9679      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9680      */
9681     loadData : function(o, append){
9682         var r = this.reader.readRecords(o);
9683         this.loadRecords(r, {add: append}, true);
9684     },
9685
9686     /**
9687      * Gets the number of cached records.
9688      * <p>
9689      * <em>If using paging, this may not be the total size of the dataset. If the data object
9690      * used by the Reader contains the dataset size, then the getTotalCount() function returns
9691      * the data set size</em>
9692      */
9693     getCount : function(){
9694         return this.data.length || 0;
9695     },
9696
9697     /**
9698      * Gets the total number of records in the dataset as returned by the server.
9699      * <p>
9700      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9701      * the dataset size</em>
9702      */
9703     getTotalCount : function(){
9704         return this.totalLength || 0;
9705     },
9706
9707     /**
9708      * Returns the sort state of the Store as an object with two properties:
9709      * <pre><code>
9710  field {String} The name of the field by which the Records are sorted
9711  direction {String} The sort order, "ASC" or "DESC"
9712      * </code></pre>
9713      */
9714     getSortState : function(){
9715         return this.sortInfo;
9716     },
9717
9718     // private
9719     applySort : function(){
9720         if(this.sortInfo && !this.remoteSort){
9721             var s = this.sortInfo, f = s.field;
9722             var st = this.fields.get(f).sortType;
9723             var fn = function(r1, r2){
9724                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9725                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9726             };
9727             this.data.sort(s.direction, fn);
9728             if(this.snapshot && this.snapshot != this.data){
9729                 this.snapshot.sort(s.direction, fn);
9730             }
9731         }
9732     },
9733
9734     /**
9735      * Sets the default sort column and order to be used by the next load operation.
9736      * @param {String} fieldName The name of the field to sort by.
9737      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9738      */
9739     setDefaultSort : function(field, dir){
9740         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9741     },
9742
9743     /**
9744      * Sort the Records.
9745      * If remote sorting is used, the sort is performed on the server, and the cache is
9746      * reloaded. If local sorting is used, the cache is sorted internally.
9747      * @param {String} fieldName The name of the field to sort by.
9748      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9749      */
9750     sort : function(fieldName, dir){
9751         var f = this.fields.get(fieldName);
9752         if(!dir){
9753             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9754             
9755             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9756                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9757             }else{
9758                 dir = f.sortDir;
9759             }
9760         }
9761         this.sortToggle[f.name] = dir;
9762         this.sortInfo = {field: f.name, direction: dir};
9763         if(!this.remoteSort){
9764             this.applySort();
9765             this.fireEvent("datachanged", this);
9766         }else{
9767             this.load(this.lastOptions);
9768         }
9769     },
9770
9771     /**
9772      * Calls the specified function for each of the Records in the cache.
9773      * @param {Function} fn The function to call. The Record is passed as the first parameter.
9774      * Returning <em>false</em> aborts and exits the iteration.
9775      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9776      */
9777     each : function(fn, scope){
9778         this.data.each(fn, scope);
9779     },
9780
9781     /**
9782      * Gets all records modified since the last commit.  Modified records are persisted across load operations
9783      * (e.g., during paging).
9784      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9785      */
9786     getModifiedRecords : function(){
9787         return this.modified;
9788     },
9789
9790     // private
9791     createFilterFn : function(property, value, anyMatch){
9792         if(!value.exec){ // not a regex
9793             value = String(value);
9794             if(value.length == 0){
9795                 return false;
9796             }
9797             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9798         }
9799         return function(r){
9800             return value.test(r.data[property]);
9801         };
9802     },
9803
9804     /**
9805      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9806      * @param {String} property A field on your records
9807      * @param {Number} start The record index to start at (defaults to 0)
9808      * @param {Number} end The last record index to include (defaults to length - 1)
9809      * @return {Number} The sum
9810      */
9811     sum : function(property, start, end){
9812         var rs = this.data.items, v = 0;
9813         start = start || 0;
9814         end = (end || end === 0) ? end : rs.length-1;
9815
9816         for(var i = start; i <= end; i++){
9817             v += (rs[i].data[property] || 0);
9818         }
9819         return v;
9820     },
9821
9822     /**
9823      * Filter the records by a specified property.
9824      * @param {String} field A field on your records
9825      * @param {String/RegExp} value Either a string that the field
9826      * should start with or a RegExp to test against the field
9827      * @param {Boolean} anyMatch True to match any part not just the beginning
9828      */
9829     filter : function(property, value, anyMatch){
9830         var fn = this.createFilterFn(property, value, anyMatch);
9831         return fn ? this.filterBy(fn) : this.clearFilter();
9832     },
9833
9834     /**
9835      * Filter by a function. The specified function will be called with each
9836      * record in this data source. If the function returns true the record is included,
9837      * otherwise it is filtered.
9838      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9839      * @param {Object} scope (optional) The scope of the function (defaults to this)
9840      */
9841     filterBy : function(fn, scope){
9842         this.snapshot = this.snapshot || this.data;
9843         this.data = this.queryBy(fn, scope||this);
9844         this.fireEvent("datachanged", this);
9845     },
9846
9847     /**
9848      * Query the records by a specified property.
9849      * @param {String} field A field on your records
9850      * @param {String/RegExp} value Either a string that the field
9851      * should start with or a RegExp to test against the field
9852      * @param {Boolean} anyMatch True to match any part not just the beginning
9853      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9854      */
9855     query : function(property, value, anyMatch){
9856         var fn = this.createFilterFn(property, value, anyMatch);
9857         return fn ? this.queryBy(fn) : this.data.clone();
9858     },
9859
9860     /**
9861      * Query by a function. The specified function will be called with each
9862      * record in this data source. If the function returns true the record is included
9863      * in the results.
9864      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9865      * @param {Object} scope (optional) The scope of the function (defaults to this)
9866       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9867      **/
9868     queryBy : function(fn, scope){
9869         var data = this.snapshot || this.data;
9870         return data.filterBy(fn, scope||this);
9871     },
9872
9873     /**
9874      * Collects unique values for a particular dataIndex from this store.
9875      * @param {String} dataIndex The property to collect
9876      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9877      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9878      * @return {Array} An array of the unique values
9879      **/
9880     collect : function(dataIndex, allowNull, bypassFilter){
9881         var d = (bypassFilter === true && this.snapshot) ?
9882                 this.snapshot.items : this.data.items;
9883         var v, sv, r = [], l = {};
9884         for(var i = 0, len = d.length; i < len; i++){
9885             v = d[i].data[dataIndex];
9886             sv = String(v);
9887             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9888                 l[sv] = true;
9889                 r[r.length] = v;
9890             }
9891         }
9892         return r;
9893     },
9894
9895     /**
9896      * Revert to a view of the Record cache with no filtering applied.
9897      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9898      */
9899     clearFilter : function(suppressEvent){
9900         if(this.snapshot && this.snapshot != this.data){
9901             this.data = this.snapshot;
9902             delete this.snapshot;
9903             if(suppressEvent !== true){
9904                 this.fireEvent("datachanged", this);
9905             }
9906         }
9907     },
9908
9909     // private
9910     afterEdit : function(record){
9911         if(this.modified.indexOf(record) == -1){
9912             this.modified.push(record);
9913         }
9914         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9915     },
9916     
9917     // private
9918     afterReject : function(record){
9919         this.modified.remove(record);
9920         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9921     },
9922
9923     // private
9924     afterCommit : function(record){
9925         this.modified.remove(record);
9926         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9927     },
9928
9929     /**
9930      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9931      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9932      */
9933     commitChanges : function(){
9934         var m = this.modified.slice(0);
9935         this.modified = [];
9936         for(var i = 0, len = m.length; i < len; i++){
9937             m[i].commit();
9938         }
9939     },
9940
9941     /**
9942      * Cancel outstanding changes on all changed records.
9943      */
9944     rejectChanges : function(){
9945         var m = this.modified.slice(0);
9946         this.modified = [];
9947         for(var i = 0, len = m.length; i < len; i++){
9948             m[i].reject();
9949         }
9950     },
9951
9952     onMetaChange : function(meta, rtype, o){
9953         this.recordType = rtype;
9954         this.fields = rtype.prototype.fields;
9955         delete this.snapshot;
9956         this.sortInfo = meta.sortInfo || this.sortInfo;
9957         this.modified = [];
9958         this.fireEvent('metachange', this, this.reader.meta);
9959     },
9960     
9961     moveIndex : function(data, type)
9962     {
9963         var index = this.indexOf(data);
9964         
9965         var newIndex = index + type;
9966         
9967         this.remove(data);
9968         
9969         this.insert(newIndex, data);
9970         
9971     }
9972 });/*
9973  * Based on:
9974  * Ext JS Library 1.1.1
9975  * Copyright(c) 2006-2007, Ext JS, LLC.
9976  *
9977  * Originally Released Under LGPL - original licence link has changed is not relivant.
9978  *
9979  * Fork - LGPL
9980  * <script type="text/javascript">
9981  */
9982
9983 /**
9984  * @class Roo.data.SimpleStore
9985  * @extends Roo.data.Store
9986  * Small helper class to make creating Stores from Array data easier.
9987  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9988  * @cfg {Array} fields An array of field definition objects, or field name strings.
9989  * @cfg {Array} data The multi-dimensional array of data
9990  * @constructor
9991  * @param {Object} config
9992  */
9993 Roo.data.SimpleStore = function(config){
9994     Roo.data.SimpleStore.superclass.constructor.call(this, {
9995         isLocal : true,
9996         reader: new Roo.data.ArrayReader({
9997                 id: config.id
9998             },
9999             Roo.data.Record.create(config.fields)
10000         ),
10001         proxy : new Roo.data.MemoryProxy(config.data)
10002     });
10003     this.load();
10004 };
10005 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10006  * Based on:
10007  * Ext JS Library 1.1.1
10008  * Copyright(c) 2006-2007, Ext JS, LLC.
10009  *
10010  * Originally Released Under LGPL - original licence link has changed is not relivant.
10011  *
10012  * Fork - LGPL
10013  * <script type="text/javascript">
10014  */
10015
10016 /**
10017 /**
10018  * @extends Roo.data.Store
10019  * @class Roo.data.JsonStore
10020  * Small helper class to make creating Stores for JSON data easier. <br/>
10021 <pre><code>
10022 var store = new Roo.data.JsonStore({
10023     url: 'get-images.php',
10024     root: 'images',
10025     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10026 });
10027 </code></pre>
10028  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10029  * JsonReader and HttpProxy (unless inline data is provided).</b>
10030  * @cfg {Array} fields An array of field definition objects, or field name strings.
10031  * @constructor
10032  * @param {Object} config
10033  */
10034 Roo.data.JsonStore = function(c){
10035     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10036         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10037         reader: new Roo.data.JsonReader(c, c.fields)
10038     }));
10039 };
10040 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10041  * Based on:
10042  * Ext JS Library 1.1.1
10043  * Copyright(c) 2006-2007, Ext JS, LLC.
10044  *
10045  * Originally Released Under LGPL - original licence link has changed is not relivant.
10046  *
10047  * Fork - LGPL
10048  * <script type="text/javascript">
10049  */
10050
10051  
10052 Roo.data.Field = function(config){
10053     if(typeof config == "string"){
10054         config = {name: config};
10055     }
10056     Roo.apply(this, config);
10057     
10058     if(!this.type){
10059         this.type = "auto";
10060     }
10061     
10062     var st = Roo.data.SortTypes;
10063     // named sortTypes are supported, here we look them up
10064     if(typeof this.sortType == "string"){
10065         this.sortType = st[this.sortType];
10066     }
10067     
10068     // set default sortType for strings and dates
10069     if(!this.sortType){
10070         switch(this.type){
10071             case "string":
10072                 this.sortType = st.asUCString;
10073                 break;
10074             case "date":
10075                 this.sortType = st.asDate;
10076                 break;
10077             default:
10078                 this.sortType = st.none;
10079         }
10080     }
10081
10082     // define once
10083     var stripRe = /[\$,%]/g;
10084
10085     // prebuilt conversion function for this field, instead of
10086     // switching every time we're reading a value
10087     if(!this.convert){
10088         var cv, dateFormat = this.dateFormat;
10089         switch(this.type){
10090             case "":
10091             case "auto":
10092             case undefined:
10093                 cv = function(v){ return v; };
10094                 break;
10095             case "string":
10096                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10097                 break;
10098             case "int":
10099                 cv = function(v){
10100                     return v !== undefined && v !== null && v !== '' ?
10101                            parseInt(String(v).replace(stripRe, ""), 10) : '';
10102                     };
10103                 break;
10104             case "float":
10105                 cv = function(v){
10106                     return v !== undefined && v !== null && v !== '' ?
10107                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
10108                     };
10109                 break;
10110             case "bool":
10111             case "boolean":
10112                 cv = function(v){ return v === true || v === "true" || v == 1; };
10113                 break;
10114             case "date":
10115                 cv = function(v){
10116                     if(!v){
10117                         return '';
10118                     }
10119                     if(v instanceof Date){
10120                         return v;
10121                     }
10122                     if(dateFormat){
10123                         if(dateFormat == "timestamp"){
10124                             return new Date(v*1000);
10125                         }
10126                         return Date.parseDate(v, dateFormat);
10127                     }
10128                     var parsed = Date.parse(v);
10129                     return parsed ? new Date(parsed) : null;
10130                 };
10131              break;
10132             
10133         }
10134         this.convert = cv;
10135     }
10136 };
10137
10138 Roo.data.Field.prototype = {
10139     dateFormat: null,
10140     defaultValue: "",
10141     mapping: null,
10142     sortType : null,
10143     sortDir : "ASC"
10144 };/*
10145  * Based on:
10146  * Ext JS Library 1.1.1
10147  * Copyright(c) 2006-2007, Ext JS, LLC.
10148  *
10149  * Originally Released Under LGPL - original licence link has changed is not relivant.
10150  *
10151  * Fork - LGPL
10152  * <script type="text/javascript">
10153  */
10154  
10155 // Base class for reading structured data from a data source.  This class is intended to be
10156 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10157
10158 /**
10159  * @class Roo.data.DataReader
10160  * Base class for reading structured data from a data source.  This class is intended to be
10161  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10162  */
10163
10164 Roo.data.DataReader = function(meta, recordType){
10165     
10166     this.meta = meta;
10167     
10168     this.recordType = recordType instanceof Array ? 
10169         Roo.data.Record.create(recordType) : recordType;
10170 };
10171
10172 Roo.data.DataReader.prototype = {
10173      /**
10174      * Create an empty record
10175      * @param {Object} data (optional) - overlay some values
10176      * @return {Roo.data.Record} record created.
10177      */
10178     newRow :  function(d) {
10179         var da =  {};
10180         this.recordType.prototype.fields.each(function(c) {
10181             switch( c.type) {
10182                 case 'int' : da[c.name] = 0; break;
10183                 case 'date' : da[c.name] = new Date(); break;
10184                 case 'float' : da[c.name] = 0.0; break;
10185                 case 'boolean' : da[c.name] = false; break;
10186                 default : da[c.name] = ""; break;
10187             }
10188             
10189         });
10190         return new this.recordType(Roo.apply(da, d));
10191     }
10192     
10193 };/*
10194  * Based on:
10195  * Ext JS Library 1.1.1
10196  * Copyright(c) 2006-2007, Ext JS, LLC.
10197  *
10198  * Originally Released Under LGPL - original licence link has changed is not relivant.
10199  *
10200  * Fork - LGPL
10201  * <script type="text/javascript">
10202  */
10203
10204 /**
10205  * @class Roo.data.DataProxy
10206  * @extends Roo.data.Observable
10207  * This class is an abstract base class for implementations which provide retrieval of
10208  * unformatted data objects.<br>
10209  * <p>
10210  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10211  * (of the appropriate type which knows how to parse the data object) to provide a block of
10212  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10213  * <p>
10214  * Custom implementations must implement the load method as described in
10215  * {@link Roo.data.HttpProxy#load}.
10216  */
10217 Roo.data.DataProxy = function(){
10218     this.addEvents({
10219         /**
10220          * @event beforeload
10221          * Fires before a network request is made to retrieve a data object.
10222          * @param {Object} This DataProxy object.
10223          * @param {Object} params The params parameter to the load function.
10224          */
10225         beforeload : true,
10226         /**
10227          * @event load
10228          * Fires before the load method's callback is called.
10229          * @param {Object} This DataProxy object.
10230          * @param {Object} o The data object.
10231          * @param {Object} arg The callback argument object passed to the load function.
10232          */
10233         load : true,
10234         /**
10235          * @event loadexception
10236          * Fires if an Exception occurs during data retrieval.
10237          * @param {Object} This DataProxy object.
10238          * @param {Object} o The data object.
10239          * @param {Object} arg The callback argument object passed to the load function.
10240          * @param {Object} e The Exception.
10241          */
10242         loadexception : true
10243     });
10244     Roo.data.DataProxy.superclass.constructor.call(this);
10245 };
10246
10247 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10248
10249     /**
10250      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10251      */
10252 /*
10253  * Based on:
10254  * Ext JS Library 1.1.1
10255  * Copyright(c) 2006-2007, Ext JS, LLC.
10256  *
10257  * Originally Released Under LGPL - original licence link has changed is not relivant.
10258  *
10259  * Fork - LGPL
10260  * <script type="text/javascript">
10261  */
10262 /**
10263  * @class Roo.data.MemoryProxy
10264  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10265  * to the Reader when its load method is called.
10266  * @constructor
10267  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10268  */
10269 Roo.data.MemoryProxy = function(data){
10270     if (data.data) {
10271         data = data.data;
10272     }
10273     Roo.data.MemoryProxy.superclass.constructor.call(this);
10274     this.data = data;
10275 };
10276
10277 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10278     /**
10279      * Load data from the requested source (in this case an in-memory
10280      * data object passed to the constructor), read the data object into
10281      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10282      * process that block using the passed callback.
10283      * @param {Object} params This parameter is not used by the MemoryProxy class.
10284      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10285      * object into a block of Roo.data.Records.
10286      * @param {Function} callback The function into which to pass the block of Roo.data.records.
10287      * The function must be passed <ul>
10288      * <li>The Record block object</li>
10289      * <li>The "arg" argument from the load function</li>
10290      * <li>A boolean success indicator</li>
10291      * </ul>
10292      * @param {Object} scope The scope in which to call the callback
10293      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10294      */
10295     load : function(params, reader, callback, scope, arg){
10296         params = params || {};
10297         var result;
10298         try {
10299             result = reader.readRecords(this.data);
10300         }catch(e){
10301             this.fireEvent("loadexception", this, arg, null, e);
10302             callback.call(scope, null, arg, false);
10303             return;
10304         }
10305         callback.call(scope, result, arg, true);
10306     },
10307     
10308     // private
10309     update : function(params, records){
10310         
10311     }
10312 });/*
10313  * Based on:
10314  * Ext JS Library 1.1.1
10315  * Copyright(c) 2006-2007, Ext JS, LLC.
10316  *
10317  * Originally Released Under LGPL - original licence link has changed is not relivant.
10318  *
10319  * Fork - LGPL
10320  * <script type="text/javascript">
10321  */
10322 /**
10323  * @class Roo.data.HttpProxy
10324  * @extends Roo.data.DataProxy
10325  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10326  * configured to reference a certain URL.<br><br>
10327  * <p>
10328  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10329  * from which the running page was served.<br><br>
10330  * <p>
10331  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10332  * <p>
10333  * Be aware that to enable the browser to parse an XML document, the server must set
10334  * the Content-Type header in the HTTP response to "text/xml".
10335  * @constructor
10336  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10337  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
10338  * will be used to make the request.
10339  */
10340 Roo.data.HttpProxy = function(conn){
10341     Roo.data.HttpProxy.superclass.constructor.call(this);
10342     // is conn a conn config or a real conn?
10343     this.conn = conn;
10344     this.useAjax = !conn || !conn.events;
10345   
10346 };
10347
10348 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10349     // thse are take from connection...
10350     
10351     /**
10352      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10353      */
10354     /**
10355      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10356      * extra parameters to each request made by this object. (defaults to undefined)
10357      */
10358     /**
10359      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10360      *  to each request made by this object. (defaults to undefined)
10361      */
10362     /**
10363      * @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)
10364      */
10365     /**
10366      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10367      */
10368      /**
10369      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10370      * @type Boolean
10371      */
10372   
10373
10374     /**
10375      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10376      * @type Boolean
10377      */
10378     /**
10379      * Return the {@link Roo.data.Connection} object being used by this Proxy.
10380      * @return {Connection} The Connection object. This object may be used to subscribe to events on
10381      * a finer-grained basis than the DataProxy events.
10382      */
10383     getConnection : function(){
10384         return this.useAjax ? Roo.Ajax : this.conn;
10385     },
10386
10387     /**
10388      * Load data from the configured {@link Roo.data.Connection}, read the data object into
10389      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10390      * process that block using the passed callback.
10391      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10392      * for the request to the remote server.
10393      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10394      * object into a block of Roo.data.Records.
10395      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10396      * The function must be passed <ul>
10397      * <li>The Record block object</li>
10398      * <li>The "arg" argument from the load function</li>
10399      * <li>A boolean success indicator</li>
10400      * </ul>
10401      * @param {Object} scope The scope in which to call the callback
10402      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10403      */
10404     load : function(params, reader, callback, scope, arg){
10405         if(this.fireEvent("beforeload", this, params) !== false){
10406             var  o = {
10407                 params : params || {},
10408                 request: {
10409                     callback : callback,
10410                     scope : scope,
10411                     arg : arg
10412                 },
10413                 reader: reader,
10414                 callback : this.loadResponse,
10415                 scope: this
10416             };
10417             if(this.useAjax){
10418                 Roo.applyIf(o, this.conn);
10419                 if(this.activeRequest){
10420                     Roo.Ajax.abort(this.activeRequest);
10421                 }
10422                 this.activeRequest = Roo.Ajax.request(o);
10423             }else{
10424                 this.conn.request(o);
10425             }
10426         }else{
10427             callback.call(scope||this, null, arg, false);
10428         }
10429     },
10430
10431     // private
10432     loadResponse : function(o, success, response){
10433         delete this.activeRequest;
10434         if(!success){
10435             this.fireEvent("loadexception", this, o, response);
10436             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10437             return;
10438         }
10439         var result;
10440         try {
10441             result = o.reader.read(response);
10442         }catch(e){
10443             this.fireEvent("loadexception", this, o, response, e);
10444             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10445             return;
10446         }
10447         
10448         this.fireEvent("load", this, o, o.request.arg);
10449         o.request.callback.call(o.request.scope, result, o.request.arg, true);
10450     },
10451
10452     // private
10453     update : function(dataSet){
10454
10455     },
10456
10457     // private
10458     updateResponse : function(dataSet){
10459
10460     }
10461 });/*
10462  * Based on:
10463  * Ext JS Library 1.1.1
10464  * Copyright(c) 2006-2007, Ext JS, LLC.
10465  *
10466  * Originally Released Under LGPL - original licence link has changed is not relivant.
10467  *
10468  * Fork - LGPL
10469  * <script type="text/javascript">
10470  */
10471
10472 /**
10473  * @class Roo.data.ScriptTagProxy
10474  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10475  * other than the originating domain of the running page.<br><br>
10476  * <p>
10477  * <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
10478  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10479  * <p>
10480  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10481  * source code that is used as the source inside a &lt;script> tag.<br><br>
10482  * <p>
10483  * In order for the browser to process the returned data, the server must wrap the data object
10484  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10485  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10486  * depending on whether the callback name was passed:
10487  * <p>
10488  * <pre><code>
10489 boolean scriptTag = false;
10490 String cb = request.getParameter("callback");
10491 if (cb != null) {
10492     scriptTag = true;
10493     response.setContentType("text/javascript");
10494 } else {
10495     response.setContentType("application/x-json");
10496 }
10497 Writer out = response.getWriter();
10498 if (scriptTag) {
10499     out.write(cb + "(");
10500 }
10501 out.print(dataBlock.toJsonString());
10502 if (scriptTag) {
10503     out.write(");");
10504 }
10505 </pre></code>
10506  *
10507  * @constructor
10508  * @param {Object} config A configuration object.
10509  */
10510 Roo.data.ScriptTagProxy = function(config){
10511     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10512     Roo.apply(this, config);
10513     this.head = document.getElementsByTagName("head")[0];
10514 };
10515
10516 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10517
10518 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10519     /**
10520      * @cfg {String} url The URL from which to request the data object.
10521      */
10522     /**
10523      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10524      */
10525     timeout : 30000,
10526     /**
10527      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10528      * the server the name of the callback function set up by the load call to process the returned data object.
10529      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10530      * javascript output which calls this named function passing the data object as its only parameter.
10531      */
10532     callbackParam : "callback",
10533     /**
10534      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10535      * name to the request.
10536      */
10537     nocache : true,
10538
10539     /**
10540      * Load data from the configured URL, read the data object into
10541      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10542      * process that block using the passed callback.
10543      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10544      * for the request to the remote server.
10545      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10546      * object into a block of Roo.data.Records.
10547      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10548      * The function must be passed <ul>
10549      * <li>The Record block object</li>
10550      * <li>The "arg" argument from the load function</li>
10551      * <li>A boolean success indicator</li>
10552      * </ul>
10553      * @param {Object} scope The scope in which to call the callback
10554      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10555      */
10556     load : function(params, reader, callback, scope, arg){
10557         if(this.fireEvent("beforeload", this, params) !== false){
10558
10559             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10560
10561             var url = this.url;
10562             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10563             if(this.nocache){
10564                 url += "&_dc=" + (new Date().getTime());
10565             }
10566             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10567             var trans = {
10568                 id : transId,
10569                 cb : "stcCallback"+transId,
10570                 scriptId : "stcScript"+transId,
10571                 params : params,
10572                 arg : arg,
10573                 url : url,
10574                 callback : callback,
10575                 scope : scope,
10576                 reader : reader
10577             };
10578             var conn = this;
10579
10580             window[trans.cb] = function(o){
10581                 conn.handleResponse(o, trans);
10582             };
10583
10584             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10585
10586             if(this.autoAbort !== false){
10587                 this.abort();
10588             }
10589
10590             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10591
10592             var script = document.createElement("script");
10593             script.setAttribute("src", url);
10594             script.setAttribute("type", "text/javascript");
10595             script.setAttribute("id", trans.scriptId);
10596             this.head.appendChild(script);
10597
10598             this.trans = trans;
10599         }else{
10600             callback.call(scope||this, null, arg, false);
10601         }
10602     },
10603
10604     // private
10605     isLoading : function(){
10606         return this.trans ? true : false;
10607     },
10608
10609     /**
10610      * Abort the current server request.
10611      */
10612     abort : function(){
10613         if(this.isLoading()){
10614             this.destroyTrans(this.trans);
10615         }
10616     },
10617
10618     // private
10619     destroyTrans : function(trans, isLoaded){
10620         this.head.removeChild(document.getElementById(trans.scriptId));
10621         clearTimeout(trans.timeoutId);
10622         if(isLoaded){
10623             window[trans.cb] = undefined;
10624             try{
10625                 delete window[trans.cb];
10626             }catch(e){}
10627         }else{
10628             // if hasn't been loaded, wait for load to remove it to prevent script error
10629             window[trans.cb] = function(){
10630                 window[trans.cb] = undefined;
10631                 try{
10632                     delete window[trans.cb];
10633                 }catch(e){}
10634             };
10635         }
10636     },
10637
10638     // private
10639     handleResponse : function(o, trans){
10640         this.trans = false;
10641         this.destroyTrans(trans, true);
10642         var result;
10643         try {
10644             result = trans.reader.readRecords(o);
10645         }catch(e){
10646             this.fireEvent("loadexception", this, o, trans.arg, e);
10647             trans.callback.call(trans.scope||window, null, trans.arg, false);
10648             return;
10649         }
10650         this.fireEvent("load", this, o, trans.arg);
10651         trans.callback.call(trans.scope||window, result, trans.arg, true);
10652     },
10653
10654     // private
10655     handleFailure : function(trans){
10656         this.trans = false;
10657         this.destroyTrans(trans, false);
10658         this.fireEvent("loadexception", this, null, trans.arg);
10659         trans.callback.call(trans.scope||window, null, trans.arg, false);
10660     }
10661 });/*
10662  * Based on:
10663  * Ext JS Library 1.1.1
10664  * Copyright(c) 2006-2007, Ext JS, LLC.
10665  *
10666  * Originally Released Under LGPL - original licence link has changed is not relivant.
10667  *
10668  * Fork - LGPL
10669  * <script type="text/javascript">
10670  */
10671
10672 /**
10673  * @class Roo.data.JsonReader
10674  * @extends Roo.data.DataReader
10675  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10676  * based on mappings in a provided Roo.data.Record constructor.
10677  * 
10678  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10679  * in the reply previously. 
10680  * 
10681  * <p>
10682  * Example code:
10683  * <pre><code>
10684 var RecordDef = Roo.data.Record.create([
10685     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
10686     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
10687 ]);
10688 var myReader = new Roo.data.JsonReader({
10689     totalProperty: "results",    // The property which contains the total dataset size (optional)
10690     root: "rows",                // The property which contains an Array of row objects
10691     id: "id"                     // The property within each row object that provides an ID for the record (optional)
10692 }, RecordDef);
10693 </code></pre>
10694  * <p>
10695  * This would consume a JSON file like this:
10696  * <pre><code>
10697 { 'results': 2, 'rows': [
10698     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10699     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10700 }
10701 </code></pre>
10702  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10703  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10704  * paged from the remote server.
10705  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10706  * @cfg {String} root name of the property which contains the Array of row objects.
10707  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10708  * @cfg {Array} fields Array of field definition objects
10709  * @constructor
10710  * Create a new JsonReader
10711  * @param {Object} meta Metadata configuration options
10712  * @param {Object} recordType Either an Array of field definition objects,
10713  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10714  */
10715 Roo.data.JsonReader = function(meta, recordType){
10716     
10717     meta = meta || {};
10718     // set some defaults:
10719     Roo.applyIf(meta, {
10720         totalProperty: 'total',
10721         successProperty : 'success',
10722         root : 'data',
10723         id : 'id'
10724     });
10725     
10726     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10727 };
10728 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10729     
10730     /**
10731      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
10732      * Used by Store query builder to append _requestMeta to params.
10733      * 
10734      */
10735     metaFromRemote : false,
10736     /**
10737      * This method is only used by a DataProxy which has retrieved data from a remote server.
10738      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10739      * @return {Object} data A data block which is used by an Roo.data.Store object as
10740      * a cache of Roo.data.Records.
10741      */
10742     read : function(response){
10743         var json = response.responseText;
10744        
10745         var o = /* eval:var:o */ eval("("+json+")");
10746         if(!o) {
10747             throw {message: "JsonReader.read: Json object not found"};
10748         }
10749         
10750         if(o.metaData){
10751             
10752             delete this.ef;
10753             this.metaFromRemote = true;
10754             this.meta = o.metaData;
10755             this.recordType = Roo.data.Record.create(o.metaData.fields);
10756             this.onMetaChange(this.meta, this.recordType, o);
10757         }
10758         return this.readRecords(o);
10759     },
10760
10761     // private function a store will implement
10762     onMetaChange : function(meta, recordType, o){
10763
10764     },
10765
10766     /**
10767          * @ignore
10768          */
10769     simpleAccess: function(obj, subsc) {
10770         return obj[subsc];
10771     },
10772
10773         /**
10774          * @ignore
10775          */
10776     getJsonAccessor: function(){
10777         var re = /[\[\.]/;
10778         return function(expr) {
10779             try {
10780                 return(re.test(expr))
10781                     ? new Function("obj", "return obj." + expr)
10782                     : function(obj){
10783                         return obj[expr];
10784                     };
10785             } catch(e){}
10786             return Roo.emptyFn;
10787         };
10788     }(),
10789
10790     /**
10791      * Create a data block containing Roo.data.Records from an XML document.
10792      * @param {Object} o An object which contains an Array of row objects in the property specified
10793      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10794      * which contains the total size of the dataset.
10795      * @return {Object} data A data block which is used by an Roo.data.Store object as
10796      * a cache of Roo.data.Records.
10797      */
10798     readRecords : function(o){
10799         /**
10800          * After any data loads, the raw JSON data is available for further custom processing.
10801          * @type Object
10802          */
10803         this.o = o;
10804         var s = this.meta, Record = this.recordType,
10805             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
10806
10807 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10808         if (!this.ef) {
10809             if(s.totalProperty) {
10810                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10811                 }
10812                 if(s.successProperty) {
10813                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10814                 }
10815                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10816                 if (s.id) {
10817                         var g = this.getJsonAccessor(s.id);
10818                         this.getId = function(rec) {
10819                                 var r = g(rec);  
10820                                 return (r === undefined || r === "") ? null : r;
10821                         };
10822                 } else {
10823                         this.getId = function(){return null;};
10824                 }
10825             this.ef = [];
10826             for(var jj = 0; jj < fl; jj++){
10827                 f = fi[jj];
10828                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10829                 this.ef[jj] = this.getJsonAccessor(map);
10830             }
10831         }
10832
10833         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10834         if(s.totalProperty){
10835             var vt = parseInt(this.getTotal(o), 10);
10836             if(!isNaN(vt)){
10837                 totalRecords = vt;
10838             }
10839         }
10840         if(s.successProperty){
10841             var vs = this.getSuccess(o);
10842             if(vs === false || vs === 'false'){
10843                 success = false;
10844             }
10845         }
10846         var records = [];
10847         for(var i = 0; i < c; i++){
10848                 var n = root[i];
10849             var values = {};
10850             var id = this.getId(n);
10851             for(var j = 0; j < fl; j++){
10852                 f = fi[j];
10853             var v = this.ef[j](n);
10854             if (!f.convert) {
10855                 Roo.log('missing convert for ' + f.name);
10856                 Roo.log(f);
10857                 continue;
10858             }
10859             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10860             }
10861             var record = new Record(values, id);
10862             record.json = n;
10863             records[i] = record;
10864         }
10865         return {
10866             raw : o,
10867             success : success,
10868             records : records,
10869             totalRecords : totalRecords
10870         };
10871     }
10872 });/*
10873  * Based on:
10874  * Ext JS Library 1.1.1
10875  * Copyright(c) 2006-2007, Ext JS, LLC.
10876  *
10877  * Originally Released Under LGPL - original licence link has changed is not relivant.
10878  *
10879  * Fork - LGPL
10880  * <script type="text/javascript">
10881  */
10882
10883 /**
10884  * @class Roo.data.ArrayReader
10885  * @extends Roo.data.DataReader
10886  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10887  * Each element of that Array represents a row of data fields. The
10888  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10889  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10890  * <p>
10891  * Example code:.
10892  * <pre><code>
10893 var RecordDef = Roo.data.Record.create([
10894     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10895     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10896 ]);
10897 var myReader = new Roo.data.ArrayReader({
10898     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10899 }, RecordDef);
10900 </code></pre>
10901  * <p>
10902  * This would consume an Array like this:
10903  * <pre><code>
10904 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10905   </code></pre>
10906  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10907  * @constructor
10908  * Create a new JsonReader
10909  * @param {Object} meta Metadata configuration options.
10910  * @param {Object} recordType Either an Array of field definition objects
10911  * as specified to {@link Roo.data.Record#create},
10912  * or an {@link Roo.data.Record} object
10913  * created using {@link Roo.data.Record#create}.
10914  */
10915 Roo.data.ArrayReader = function(meta, recordType){
10916     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10917 };
10918
10919 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10920     /**
10921      * Create a data block containing Roo.data.Records from an XML document.
10922      * @param {Object} o An Array of row objects which represents the dataset.
10923      * @return {Object} data A data block which is used by an Roo.data.Store object as
10924      * a cache of Roo.data.Records.
10925      */
10926     readRecords : function(o){
10927         var sid = this.meta ? this.meta.id : null;
10928         var recordType = this.recordType, fields = recordType.prototype.fields;
10929         var records = [];
10930         var root = o;
10931             for(var i = 0; i < root.length; i++){
10932                     var n = root[i];
10933                 var values = {};
10934                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10935                 for(var j = 0, jlen = fields.length; j < jlen; j++){
10936                 var f = fields.items[j];
10937                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10938                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10939                 v = f.convert(v);
10940                 values[f.name] = v;
10941             }
10942                 var record = new recordType(values, id);
10943                 record.json = n;
10944                 records[records.length] = record;
10945             }
10946             return {
10947                 records : records,
10948                 totalRecords : records.length
10949             };
10950     }
10951 });/*
10952  * - LGPL
10953  * * 
10954  */
10955
10956 /**
10957  * @class Roo.bootstrap.ComboBox
10958  * @extends Roo.bootstrap.TriggerField
10959  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10960  * @cfg {Boolean} append (true|false) default false
10961  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10962  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10963  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10964  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
10965  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10966  * @constructor
10967  * Create a new ComboBox.
10968  * @param {Object} config Configuration options
10969  */
10970 Roo.bootstrap.ComboBox = function(config){
10971     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10972     this.addEvents({
10973         /**
10974          * @event expand
10975          * Fires when the dropdown list is expanded
10976              * @param {Roo.bootstrap.ComboBox} combo This combo box
10977              */
10978         'expand' : true,
10979         /**
10980          * @event collapse
10981          * Fires when the dropdown list is collapsed
10982              * @param {Roo.bootstrap.ComboBox} combo This combo box
10983              */
10984         'collapse' : true,
10985         /**
10986          * @event beforeselect
10987          * Fires before a list item is selected. Return false to cancel the selection.
10988              * @param {Roo.bootstrap.ComboBox} combo This combo box
10989              * @param {Roo.data.Record} record The data record returned from the underlying store
10990              * @param {Number} index The index of the selected item in the dropdown list
10991              */
10992         'beforeselect' : true,
10993         /**
10994          * @event select
10995          * Fires when a list item is selected
10996              * @param {Roo.bootstrap.ComboBox} combo This combo box
10997              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10998              * @param {Number} index The index of the selected item in the dropdown list
10999              */
11000         'select' : true,
11001         /**
11002          * @event beforequery
11003          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11004          * The event object passed has these properties:
11005              * @param {Roo.bootstrap.ComboBox} combo This combo box
11006              * @param {String} query The query
11007              * @param {Boolean} forceAll true to force "all" query
11008              * @param {Boolean} cancel true to cancel the query
11009              * @param {Object} e The query event object
11010              */
11011         'beforequery': true,
11012          /**
11013          * @event add
11014          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11015              * @param {Roo.bootstrap.ComboBox} combo This combo box
11016              */
11017         'add' : true,
11018         /**
11019          * @event edit
11020          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11021              * @param {Roo.bootstrap.ComboBox} combo This combo box
11022              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11023              */
11024         'edit' : true,
11025         /**
11026          * @event remove
11027          * Fires when the remove value from the combobox array
11028              * @param {Roo.bootstrap.ComboBox} combo This combo box
11029              */
11030         'remove' : true,
11031         /**
11032          * @event specialfilter
11033          * Fires when specialfilter
11034             * @param {Roo.bootstrap.ComboBox} combo This combo box
11035             */
11036         'specialfilter' : true
11037         
11038     });
11039     
11040     this.item = [];
11041     this.tickItems = [];
11042     
11043     this.selectedIndex = -1;
11044     if(this.mode == 'local'){
11045         if(config.queryDelay === undefined){
11046             this.queryDelay = 10;
11047         }
11048         if(config.minChars === undefined){
11049             this.minChars = 0;
11050         }
11051     }
11052 };
11053
11054 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11055      
11056     /**
11057      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11058      * rendering into an Roo.Editor, defaults to false)
11059      */
11060     /**
11061      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11062      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11063      */
11064     /**
11065      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11066      */
11067     /**
11068      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11069      * the dropdown list (defaults to undefined, with no header element)
11070      */
11071
11072      /**
11073      * @cfg {String/Roo.Template} tpl The template to use to render the output
11074      */
11075      
11076      /**
11077      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11078      */
11079     listWidth: undefined,
11080     /**
11081      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11082      * mode = 'remote' or 'text' if mode = 'local')
11083      */
11084     displayField: undefined,
11085     
11086     /**
11087      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11088      * mode = 'remote' or 'value' if mode = 'local'). 
11089      * Note: use of a valueField requires the user make a selection
11090      * in order for a value to be mapped.
11091      */
11092     valueField: undefined,
11093     
11094     
11095     /**
11096      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11097      * field's data value (defaults to the underlying DOM element's name)
11098      */
11099     hiddenName: undefined,
11100     /**
11101      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11102      */
11103     listClass: '',
11104     /**
11105      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11106      */
11107     selectedClass: 'active',
11108     
11109     /**
11110      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11111      */
11112     shadow:'sides',
11113     /**
11114      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11115      * anchor positions (defaults to 'tl-bl')
11116      */
11117     listAlign: 'tl-bl?',
11118     /**
11119      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11120      */
11121     maxHeight: 300,
11122     /**
11123      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
11124      * query specified by the allQuery config option (defaults to 'query')
11125      */
11126     triggerAction: 'query',
11127     /**
11128      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11129      * (defaults to 4, does not apply if editable = false)
11130      */
11131     minChars : 4,
11132     /**
11133      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11134      * delay (typeAheadDelay) if it matches a known value (defaults to false)
11135      */
11136     typeAhead: false,
11137     /**
11138      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11139      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11140      */
11141     queryDelay: 500,
11142     /**
11143      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11144      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
11145      */
11146     pageSize: 0,
11147     /**
11148      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
11149      * when editable = true (defaults to false)
11150      */
11151     selectOnFocus:false,
11152     /**
11153      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11154      */
11155     queryParam: 'query',
11156     /**
11157      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
11158      * when mode = 'remote' (defaults to 'Loading...')
11159      */
11160     loadingText: 'Loading...',
11161     /**
11162      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11163      */
11164     resizable: false,
11165     /**
11166      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11167      */
11168     handleHeight : 8,
11169     /**
11170      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11171      * traditional select (defaults to true)
11172      */
11173     editable: true,
11174     /**
11175      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11176      */
11177     allQuery: '',
11178     /**
11179      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11180      */
11181     mode: 'remote',
11182     /**
11183      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11184      * listWidth has a higher value)
11185      */
11186     minListWidth : 70,
11187     /**
11188      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11189      * allow the user to set arbitrary text into the field (defaults to false)
11190      */
11191     forceSelection:false,
11192     /**
11193      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11194      * if typeAhead = true (defaults to 250)
11195      */
11196     typeAheadDelay : 250,
11197     /**
11198      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11199      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11200      */
11201     valueNotFoundText : undefined,
11202     /**
11203      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11204      */
11205     blockFocus : false,
11206     
11207     /**
11208      * @cfg {Boolean} disableClear Disable showing of clear button.
11209      */
11210     disableClear : false,
11211     /**
11212      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
11213      */
11214     alwaysQuery : false,
11215     
11216     /**
11217      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
11218      */
11219     multiple : false,
11220     
11221     /**
11222      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11223      */
11224     invalidClass : "has-warning",
11225     
11226     /**
11227      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11228      */
11229     validClass : "has-success",
11230     
11231     /**
11232      * @cfg {Boolean} specialFilter (true|false) special filter default false
11233      */
11234     specialFilter : false,
11235     
11236     //private
11237     addicon : false,
11238     editicon: false,
11239     
11240     page: 0,
11241     hasQuery: false,
11242     append: false,
11243     loadNext: false,
11244     autoFocus : true,
11245     tickable : false,
11246     btnPosition : 'right',
11247     triggerList : true,
11248     showToggleBtn : true,
11249     // element that contains real text value.. (when hidden is used..)
11250     
11251     getAutoCreate : function()
11252     {
11253         var cfg = false;
11254         
11255         /*
11256          *  Normal ComboBox
11257          */
11258         if(!this.tickable){
11259             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11260             return cfg;
11261         }
11262         
11263         /*
11264          *  ComboBox with tickable selections
11265          */
11266              
11267         var align = this.labelAlign || this.parentLabelAlign();
11268         
11269         cfg = {
11270             cls : 'form-group roo-combobox-tickable' //input-group
11271         };
11272         
11273         var buttons = {
11274             tag : 'div',
11275             cls : 'tickable-buttons',
11276             cn : [
11277                 {
11278                     tag : 'button',
11279                     type : 'button',
11280                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11281                     html : 'Edit'
11282                 },
11283                 {
11284                     tag : 'button',
11285                     type : 'button',
11286                     name : 'ok',
11287                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11288                     html : 'Done'
11289                 },
11290                 {
11291                     tag : 'button',
11292                     type : 'button',
11293                     name : 'cancel',
11294                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11295                     html : 'Cancel'
11296                 }
11297             ]
11298         };
11299         
11300         if(this.editable){
11301             buttons.cn.unshift({
11302                 tag: 'input',
11303                 cls: 'select2-search-field-input'
11304             });
11305         }
11306         
11307         var _this = this;
11308         
11309         Roo.each(buttons.cn, function(c){
11310             if (_this.size) {
11311                 c.cls += ' btn-' + _this.size;
11312             }
11313
11314             if (_this.disabled) {
11315                 c.disabled = true;
11316             }
11317         });
11318         
11319         var box = {
11320             tag: 'div',
11321             cn: [
11322                 {
11323                     tag: 'input',
11324                     type : 'hidden',
11325                     cls: 'form-hidden-field'
11326                 },
11327                 {
11328                     tag: 'ul',
11329                     cls: 'select2-choices',
11330                     cn:[
11331                         {
11332                             tag: 'li',
11333                             cls: 'select2-search-field',
11334                             cn: [
11335
11336                                 buttons
11337                             ]
11338                         }
11339                     ]
11340                 }
11341             ]
11342         }
11343         
11344         var combobox = {
11345             cls: 'select2-container input-group select2-container-multi',
11346             cn: [
11347                 box
11348 //                {
11349 //                    tag: 'ul',
11350 //                    cls: 'typeahead typeahead-long dropdown-menu',
11351 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
11352 //                }
11353             ]
11354         };
11355         
11356         if(this.hasFeedback && !this.allowBlank){
11357             
11358             var feedback = {
11359                 tag: 'span',
11360                 cls: 'glyphicon form-control-feedback'
11361             };
11362
11363             combobox.cn.push(feedback);
11364         }
11365         
11366         if (align ==='left' && this.fieldLabel.length) {
11367             
11368                 Roo.log("left and has label");
11369                 cfg.cn = [
11370                     
11371                     {
11372                         tag: 'label',
11373                         'for' :  id,
11374                         cls : 'control-label col-sm-' + this.labelWidth,
11375                         html : this.fieldLabel
11376                         
11377                     },
11378                     {
11379                         cls : "col-sm-" + (12 - this.labelWidth), 
11380                         cn: [
11381                             combobox
11382                         ]
11383                     }
11384                     
11385                 ];
11386         } else if ( this.fieldLabel.length) {
11387                 Roo.log(" label");
11388                  cfg.cn = [
11389                    
11390                     {
11391                         tag: 'label',
11392                         //cls : 'input-group-addon',
11393                         html : this.fieldLabel
11394                         
11395                     },
11396                     
11397                     combobox
11398                     
11399                 ];
11400
11401         } else {
11402             
11403                 Roo.log(" no label && no align");
11404                 cfg = combobox
11405                      
11406                 
11407         }
11408          
11409         var settings=this;
11410         ['xs','sm','md','lg'].map(function(size){
11411             if (settings[size]) {
11412                 cfg.cls += ' col-' + size + '-' + settings[size];
11413             }
11414         });
11415         
11416         return cfg;
11417         
11418     },
11419     
11420     // private
11421     initEvents: function()
11422     {
11423         
11424         if (!this.store) {
11425             throw "can not find store for combo";
11426         }
11427         this.store = Roo.factory(this.store, Roo.data);
11428         
11429         if(this.tickable){
11430             this.initTickableEvents();
11431             return;
11432         }
11433         
11434         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11435         
11436         if(this.hiddenName){
11437             
11438             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11439             
11440             this.hiddenField.dom.value =
11441                 this.hiddenValue !== undefined ? this.hiddenValue :
11442                 this.value !== undefined ? this.value : '';
11443
11444             // prevent input submission
11445             this.el.dom.removeAttribute('name');
11446             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11447              
11448              
11449         }
11450         //if(Roo.isGecko){
11451         //    this.el.dom.setAttribute('autocomplete', 'off');
11452         //}
11453         
11454         var cls = 'x-combo-list';
11455         
11456         //this.list = new Roo.Layer({
11457         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11458         //});
11459         
11460         var _this = this;
11461         
11462         (function(){
11463             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11464             _this.list.setWidth(lw);
11465         }).defer(100);
11466         
11467         this.list.on('mouseover', this.onViewOver, this);
11468         this.list.on('mousemove', this.onViewMove, this);
11469         
11470         this.list.on('scroll', this.onViewScroll, this);
11471         
11472         /*
11473         this.list.swallowEvent('mousewheel');
11474         this.assetHeight = 0;
11475
11476         if(this.title){
11477             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
11478             this.assetHeight += this.header.getHeight();
11479         }
11480
11481         this.innerList = this.list.createChild({cls:cls+'-inner'});
11482         this.innerList.on('mouseover', this.onViewOver, this);
11483         this.innerList.on('mousemove', this.onViewMove, this);
11484         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11485         
11486         if(this.allowBlank && !this.pageSize && !this.disableClear){
11487             this.footer = this.list.createChild({cls:cls+'-ft'});
11488             this.pageTb = new Roo.Toolbar(this.footer);
11489            
11490         }
11491         if(this.pageSize){
11492             this.footer = this.list.createChild({cls:cls+'-ft'});
11493             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
11494                     {pageSize: this.pageSize});
11495             
11496         }
11497         
11498         if (this.pageTb && this.allowBlank && !this.disableClear) {
11499             var _this = this;
11500             this.pageTb.add(new Roo.Toolbar.Fill(), {
11501                 cls: 'x-btn-icon x-btn-clear',
11502                 text: '&#160;',
11503                 handler: function()
11504                 {
11505                     _this.collapse();
11506                     _this.clearValue();
11507                     _this.onSelect(false, -1);
11508                 }
11509             });
11510         }
11511         if (this.footer) {
11512             this.assetHeight += this.footer.getHeight();
11513         }
11514         */
11515             
11516         if(!this.tpl){
11517             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11518         }
11519
11520         this.view = new Roo.View(this.list, this.tpl, {
11521             singleSelect:true, store: this.store, selectedClass: this.selectedClass
11522         });
11523         //this.view.wrapEl.setDisplayed(false);
11524         this.view.on('click', this.onViewClick, this);
11525         
11526         
11527         
11528         this.store.on('beforeload', this.onBeforeLoad, this);
11529         this.store.on('load', this.onLoad, this);
11530         this.store.on('loadexception', this.onLoadException, this);
11531         /*
11532         if(this.resizable){
11533             this.resizer = new Roo.Resizable(this.list,  {
11534                pinned:true, handles:'se'
11535             });
11536             this.resizer.on('resize', function(r, w, h){
11537                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11538                 this.listWidth = w;
11539                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11540                 this.restrictHeight();
11541             }, this);
11542             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11543         }
11544         */
11545         if(!this.editable){
11546             this.editable = true;
11547             this.setEditable(false);
11548         }
11549         
11550         /*
11551         
11552         if (typeof(this.events.add.listeners) != 'undefined') {
11553             
11554             this.addicon = this.wrap.createChild(
11555                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
11556        
11557             this.addicon.on('click', function(e) {
11558                 this.fireEvent('add', this);
11559             }, this);
11560         }
11561         if (typeof(this.events.edit.listeners) != 'undefined') {
11562             
11563             this.editicon = this.wrap.createChild(
11564                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
11565             if (this.addicon) {
11566                 this.editicon.setStyle('margin-left', '40px');
11567             }
11568             this.editicon.on('click', function(e) {
11569                 
11570                 // we fire even  if inothing is selected..
11571                 this.fireEvent('edit', this, this.lastData );
11572                 
11573             }, this);
11574         }
11575         */
11576         
11577         this.keyNav = new Roo.KeyNav(this.inputEl(), {
11578             "up" : function(e){
11579                 this.inKeyMode = true;
11580                 this.selectPrev();
11581             },
11582
11583             "down" : function(e){
11584                 if(!this.isExpanded()){
11585                     this.onTriggerClick();
11586                 }else{
11587                     this.inKeyMode = true;
11588                     this.selectNext();
11589                 }
11590             },
11591
11592             "enter" : function(e){
11593 //                this.onViewClick();
11594                 //return true;
11595                 this.collapse();
11596                 
11597                 if(this.fireEvent("specialkey", this, e)){
11598                     this.onViewClick(false);
11599                 }
11600                 
11601                 return true;
11602             },
11603
11604             "esc" : function(e){
11605                 this.collapse();
11606             },
11607
11608             "tab" : function(e){
11609                 this.collapse();
11610                 
11611                 if(this.fireEvent("specialkey", this, e)){
11612                     this.onViewClick(false);
11613                 }
11614                 
11615                 return true;
11616             },
11617
11618             scope : this,
11619
11620             doRelay : function(foo, bar, hname){
11621                 if(hname == 'down' || this.scope.isExpanded()){
11622                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11623                 }
11624                 return true;
11625             },
11626
11627             forceKeyDown: true
11628         });
11629         
11630         
11631         this.queryDelay = Math.max(this.queryDelay || 10,
11632                 this.mode == 'local' ? 10 : 250);
11633         
11634         
11635         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11636         
11637         if(this.typeAhead){
11638             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11639         }
11640         if(this.editable !== false){
11641             this.inputEl().on("keyup", this.onKeyUp, this);
11642         }
11643         if(this.forceSelection){
11644             this.inputEl().on('blur', this.doForce, this);
11645         }
11646         
11647         if(this.multiple){
11648             this.choices = this.el.select('ul.select2-choices', true).first();
11649             this.searchField = this.el.select('ul li.select2-search-field', true).first();
11650         }
11651     },
11652     
11653     initTickableEvents: function()
11654     {   
11655         this.createList();
11656         
11657         if(this.hiddenName){
11658             
11659             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11660             
11661             this.hiddenField.dom.value =
11662                 this.hiddenValue !== undefined ? this.hiddenValue :
11663                 this.value !== undefined ? this.value : '';
11664
11665             // prevent input submission
11666             this.el.dom.removeAttribute('name');
11667             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11668              
11669              
11670         }
11671         
11672 //        this.list = this.el.select('ul.dropdown-menu',true).first();
11673         
11674         this.choices = this.el.select('ul.select2-choices', true).first();
11675         this.searchField = this.el.select('ul li.select2-search-field', true).first();
11676         if(this.triggerList){
11677             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
11678         }
11679          
11680         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
11681         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
11682         
11683         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
11684         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
11685         
11686         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
11687         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
11688         
11689         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
11690         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
11691         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
11692         
11693         this.okBtn.hide();
11694         this.cancelBtn.hide();
11695         
11696         var _this = this;
11697         
11698         (function(){
11699             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11700             _this.list.setWidth(lw);
11701         }).defer(100);
11702         
11703         this.list.on('mouseover', this.onViewOver, this);
11704         this.list.on('mousemove', this.onViewMove, this);
11705         
11706         this.list.on('scroll', this.onViewScroll, this);
11707         
11708         if(!this.tpl){
11709             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>';
11710         }
11711
11712         this.view = new Roo.View(this.list, this.tpl, {
11713             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
11714         });
11715         
11716         //this.view.wrapEl.setDisplayed(false);
11717         this.view.on('click', this.onViewClick, this);
11718         
11719         
11720         
11721         this.store.on('beforeload', this.onBeforeLoad, this);
11722         this.store.on('load', this.onLoad, this);
11723         this.store.on('loadexception', this.onLoadException, this);
11724         
11725         if(this.editable){
11726             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
11727                 "up" : function(e){
11728                     this.inKeyMode = true;
11729                     this.selectPrev();
11730                 },
11731
11732                 "down" : function(e){
11733                     this.inKeyMode = true;
11734                     this.selectNext();
11735                 },
11736
11737                 "enter" : function(e){
11738                     if(this.fireEvent("specialkey", this, e)){
11739                         this.onViewClick(false);
11740                     }
11741                     
11742                     return true;
11743                 },
11744
11745                 "esc" : function(e){
11746                     this.onTickableFooterButtonClick(e, false, false);
11747                 },
11748
11749                 "tab" : function(e){
11750                     this.fireEvent("specialkey", this, e);
11751                     
11752                     this.onTickableFooterButtonClick(e, false, false);
11753                     
11754                     return true;
11755                 },
11756
11757                 scope : this,
11758
11759                 doRelay : function(e, fn, key){
11760                     if(this.scope.isExpanded()){
11761                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11762                     }
11763                     return true;
11764                 },
11765
11766                 forceKeyDown: true
11767             });
11768         }
11769         
11770         this.queryDelay = Math.max(this.queryDelay || 10,
11771                 this.mode == 'local' ? 10 : 250);
11772         
11773         
11774         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11775         
11776         if(this.typeAhead){
11777             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11778         }
11779         
11780         if(this.editable !== false){
11781             this.tickableInputEl().on("keyup", this.onKeyUp, this);
11782         }
11783         
11784     },
11785
11786     onDestroy : function(){
11787         if(this.view){
11788             this.view.setStore(null);
11789             this.view.el.removeAllListeners();
11790             this.view.el.remove();
11791             this.view.purgeListeners();
11792         }
11793         if(this.list){
11794             this.list.dom.innerHTML  = '';
11795         }
11796         
11797         if(this.store){
11798             this.store.un('beforeload', this.onBeforeLoad, this);
11799             this.store.un('load', this.onLoad, this);
11800             this.store.un('loadexception', this.onLoadException, this);
11801         }
11802         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11803     },
11804
11805     // private
11806     fireKey : function(e){
11807         if(e.isNavKeyPress() && !this.list.isVisible()){
11808             this.fireEvent("specialkey", this, e);
11809         }
11810     },
11811
11812     // private
11813     onResize: function(w, h){
11814 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11815 //        
11816 //        if(typeof w != 'number'){
11817 //            // we do not handle it!?!?
11818 //            return;
11819 //        }
11820 //        var tw = this.trigger.getWidth();
11821 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11822 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11823 //        var x = w - tw;
11824 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11825 //            
11826 //        //this.trigger.setStyle('left', x+'px');
11827 //        
11828 //        if(this.list && this.listWidth === undefined){
11829 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11830 //            this.list.setWidth(lw);
11831 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11832 //        }
11833         
11834     
11835         
11836     },
11837
11838     /**
11839      * Allow or prevent the user from directly editing the field text.  If false is passed,
11840      * the user will only be able to select from the items defined in the dropdown list.  This method
11841      * is the runtime equivalent of setting the 'editable' config option at config time.
11842      * @param {Boolean} value True to allow the user to directly edit the field text
11843      */
11844     setEditable : function(value){
11845         if(value == this.editable){
11846             return;
11847         }
11848         this.editable = value;
11849         if(!value){
11850             this.inputEl().dom.setAttribute('readOnly', true);
11851             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11852             this.inputEl().addClass('x-combo-noedit');
11853         }else{
11854             this.inputEl().dom.setAttribute('readOnly', false);
11855             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11856             this.inputEl().removeClass('x-combo-noedit');
11857         }
11858     },
11859
11860     // private
11861     
11862     onBeforeLoad : function(combo,opts){
11863         if(!this.hasFocus){
11864             return;
11865         }
11866          if (!opts.add) {
11867             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11868          }
11869         this.restrictHeight();
11870         this.selectedIndex = -1;
11871     },
11872
11873     // private
11874     onLoad : function(){
11875         
11876         this.hasQuery = false;
11877         
11878         if(!this.hasFocus){
11879             return;
11880         }
11881         
11882         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11883             this.loading.hide();
11884         }
11885              
11886         if(this.store.getCount() > 0){
11887             this.expand();
11888             this.restrictHeight();
11889             if(this.lastQuery == this.allQuery){
11890                 if(this.editable && !this.tickable){
11891                     this.inputEl().dom.select();
11892                 }
11893                 
11894                 if(
11895                     !this.selectByValue(this.value, true) &&
11896                     this.autoFocus && 
11897                     (
11898                         !this.store.lastOptions ||
11899                         typeof(this.store.lastOptions.add) == 'undefined' || 
11900                         this.store.lastOptions.add != true
11901                     )
11902                 ){
11903                     this.select(0, true);
11904                 }
11905             }else{
11906                 if(this.autoFocus){
11907                     this.selectNext();
11908                 }
11909                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11910                     this.taTask.delay(this.typeAheadDelay);
11911                 }
11912             }
11913         }else{
11914             this.onEmptyResults();
11915         }
11916         
11917         //this.el.focus();
11918     },
11919     // private
11920     onLoadException : function()
11921     {
11922         this.hasQuery = false;
11923         
11924         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11925             this.loading.hide();
11926         }
11927         
11928         if(this.tickable && this.editable){
11929             return;
11930         }
11931         
11932         this.collapse();
11933         
11934         Roo.log(this.store.reader.jsonData);
11935         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11936             // fixme
11937             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11938         }
11939         
11940         
11941     },
11942     // private
11943     onTypeAhead : function(){
11944         if(this.store.getCount() > 0){
11945             var r = this.store.getAt(0);
11946             var newValue = r.data[this.displayField];
11947             var len = newValue.length;
11948             var selStart = this.getRawValue().length;
11949             
11950             if(selStart != len){
11951                 this.setRawValue(newValue);
11952                 this.selectText(selStart, newValue.length);
11953             }
11954         }
11955     },
11956
11957     // private
11958     onSelect : function(record, index){
11959         
11960         if(this.fireEvent('beforeselect', this, record, index) !== false){
11961         
11962             this.setFromData(index > -1 ? record.data : false);
11963             
11964             this.collapse();
11965             this.fireEvent('select', this, record, index);
11966         }
11967     },
11968
11969     /**
11970      * Returns the currently selected field value or empty string if no value is set.
11971      * @return {String} value The selected value
11972      */
11973     getValue : function(){
11974         
11975         if(this.multiple){
11976             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11977         }
11978         
11979         if(this.valueField){
11980             return typeof this.value != 'undefined' ? this.value : '';
11981         }else{
11982             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11983         }
11984     },
11985
11986     /**
11987      * Clears any text/value currently set in the field
11988      */
11989     clearValue : function(){
11990         if(this.hiddenField){
11991             this.hiddenField.dom.value = '';
11992         }
11993         this.value = '';
11994         this.setRawValue('');
11995         this.lastSelectionText = '';
11996         this.lastData = false;
11997         
11998     },
11999
12000     /**
12001      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
12002      * will be displayed in the field.  If the value does not match the data value of an existing item,
12003      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12004      * Otherwise the field will be blank (although the value will still be set).
12005      * @param {String} value The value to match
12006      */
12007     setValue : function(v){
12008         if(this.multiple){
12009             this.syncValue();
12010             return;
12011         }
12012         
12013         var text = v;
12014         if(this.valueField){
12015             var r = this.findRecord(this.valueField, v);
12016             if(r){
12017                 text = r.data[this.displayField];
12018             }else if(this.valueNotFoundText !== undefined){
12019                 text = this.valueNotFoundText;
12020             }
12021         }
12022         this.lastSelectionText = text;
12023         if(this.hiddenField){
12024             this.hiddenField.dom.value = v;
12025         }
12026         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12027         this.value = v;
12028         
12029         var close = this.closeTriggerEl();
12030         
12031         if(close){
12032             (v.length || v * 1 > 0) ? close.show() : close.hide();
12033         }
12034     },
12035     /**
12036      * @property {Object} the last set data for the element
12037      */
12038     
12039     lastData : false,
12040     /**
12041      * Sets the value of the field based on a object which is related to the record format for the store.
12042      * @param {Object} value the value to set as. or false on reset?
12043      */
12044     setFromData : function(o){
12045         
12046         if(this.multiple){
12047             this.addItem(o);
12048             return;
12049         }
12050             
12051         var dv = ''; // display value
12052         var vv = ''; // value value..
12053         this.lastData = o;
12054         if (this.displayField) {
12055             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12056         } else {
12057             // this is an error condition!!!
12058             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12059         }
12060         
12061         if(this.valueField){
12062             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12063         }
12064         
12065         var close = this.closeTriggerEl();
12066         
12067         if(close){
12068             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12069         }
12070         
12071         if(this.hiddenField){
12072             this.hiddenField.dom.value = vv;
12073             
12074             this.lastSelectionText = dv;
12075             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12076             this.value = vv;
12077             return;
12078         }
12079         // no hidden field.. - we store the value in 'value', but still display
12080         // display field!!!!
12081         this.lastSelectionText = dv;
12082         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12083         this.value = vv;
12084         
12085         
12086         
12087     },
12088     // private
12089     reset : function(){
12090         // overridden so that last data is reset..
12091         
12092         if(this.multiple){
12093             this.clearItem();
12094             return;
12095         }
12096         
12097         this.setValue(this.originalValue);
12098         this.clearInvalid();
12099         this.lastData = false;
12100         if (this.view) {
12101             this.view.clearSelections();
12102         }
12103     },
12104     // private
12105     findRecord : function(prop, value){
12106         var record;
12107         if(this.store.getCount() > 0){
12108             this.store.each(function(r){
12109                 if(r.data[prop] == value){
12110                     record = r;
12111                     return false;
12112                 }
12113                 return true;
12114             });
12115         }
12116         return record;
12117     },
12118     
12119     getName: function()
12120     {
12121         // returns hidden if it's set..
12122         if (!this.rendered) {return ''};
12123         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
12124         
12125     },
12126     // private
12127     onViewMove : function(e, t){
12128         this.inKeyMode = false;
12129     },
12130
12131     // private
12132     onViewOver : function(e, t){
12133         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12134             return;
12135         }
12136         var item = this.view.findItemFromChild(t);
12137         
12138         if(item){
12139             var index = this.view.indexOf(item);
12140             this.select(index, false);
12141         }
12142     },
12143
12144     // private
12145     onViewClick : function(view, doFocus, el, e)
12146     {
12147         var index = this.view.getSelectedIndexes()[0];
12148         
12149         var r = this.store.getAt(index);
12150         
12151         if(this.tickable){
12152             
12153             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12154                 return;
12155             }
12156             
12157             var rm = false;
12158             var _this = this;
12159             
12160             Roo.each(this.tickItems, function(v,k){
12161                 
12162                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12163                     _this.tickItems.splice(k, 1);
12164                     
12165                     if(typeof(e) == 'undefined' && view == false){
12166                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12167                     }
12168                     
12169                     rm = true;
12170                     return;
12171                 }
12172             });
12173             
12174             if(rm){
12175                 return;
12176             }
12177             
12178             this.tickItems.push(r.data);
12179             
12180             if(typeof(e) == 'undefined' && view == false){
12181                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12182             }
12183                     
12184             return;
12185         }
12186         
12187         if(r){
12188             this.onSelect(r, index);
12189         }
12190         if(doFocus !== false && !this.blockFocus){
12191             this.inputEl().focus();
12192         }
12193     },
12194
12195     // private
12196     restrictHeight : function(){
12197         //this.innerList.dom.style.height = '';
12198         //var inner = this.innerList.dom;
12199         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12200         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12201         //this.list.beginUpdate();
12202         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12203         this.list.alignTo(this.inputEl(), this.listAlign);
12204         this.list.alignTo(this.inputEl(), this.listAlign);
12205         //this.list.endUpdate();
12206     },
12207
12208     // private
12209     onEmptyResults : function(){
12210         
12211         if(this.tickable && this.editable){
12212             this.restrictHeight();
12213             return;
12214         }
12215         
12216         this.collapse();
12217     },
12218
12219     /**
12220      * Returns true if the dropdown list is expanded, else false.
12221      */
12222     isExpanded : function(){
12223         return this.list.isVisible();
12224     },
12225
12226     /**
12227      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12228      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12229      * @param {String} value The data value of the item to select
12230      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12231      * selected item if it is not currently in view (defaults to true)
12232      * @return {Boolean} True if the value matched an item in the list, else false
12233      */
12234     selectByValue : function(v, scrollIntoView){
12235         if(v !== undefined && v !== null){
12236             var r = this.findRecord(this.valueField || this.displayField, v);
12237             if(r){
12238                 this.select(this.store.indexOf(r), scrollIntoView);
12239                 return true;
12240             }
12241         }
12242         return false;
12243     },
12244
12245     /**
12246      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12247      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12248      * @param {Number} index The zero-based index of the list item to select
12249      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12250      * selected item if it is not currently in view (defaults to true)
12251      */
12252     select : function(index, scrollIntoView){
12253         this.selectedIndex = index;
12254         this.view.select(index);
12255         if(scrollIntoView !== false){
12256             var el = this.view.getNode(index);
12257             /*
12258              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12259              */
12260             if(el){
12261                 this.list.scrollChildIntoView(el, false);
12262             }
12263         }
12264     },
12265
12266     // private
12267     selectNext : function(){
12268         var ct = this.store.getCount();
12269         if(ct > 0){
12270             if(this.selectedIndex == -1){
12271                 this.select(0);
12272             }else if(this.selectedIndex < ct-1){
12273                 this.select(this.selectedIndex+1);
12274             }
12275         }
12276     },
12277
12278     // private
12279     selectPrev : function(){
12280         var ct = this.store.getCount();
12281         if(ct > 0){
12282             if(this.selectedIndex == -1){
12283                 this.select(0);
12284             }else if(this.selectedIndex != 0){
12285                 this.select(this.selectedIndex-1);
12286             }
12287         }
12288     },
12289
12290     // private
12291     onKeyUp : function(e){
12292         if(this.editable !== false && !e.isSpecialKey()){
12293             this.lastKey = e.getKey();
12294             this.dqTask.delay(this.queryDelay);
12295         }
12296     },
12297
12298     // private
12299     validateBlur : function(){
12300         return !this.list || !this.list.isVisible();   
12301     },
12302
12303     // private
12304     initQuery : function(){
12305         
12306         var v = this.getRawValue();
12307         
12308         if(this.tickable && this.editable){
12309             v = this.tickableInputEl().getValue();
12310         }
12311         
12312         this.doQuery(v);
12313     },
12314
12315     // private
12316     doForce : function(){
12317         if(this.inputEl().dom.value.length > 0){
12318             this.inputEl().dom.value =
12319                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12320              
12321         }
12322     },
12323
12324     /**
12325      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
12326      * query allowing the query action to be canceled if needed.
12327      * @param {String} query The SQL query to execute
12328      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12329      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
12330      * saved in the current store (defaults to false)
12331      */
12332     doQuery : function(q, forceAll){
12333         
12334         if(q === undefined || q === null){
12335             q = '';
12336         }
12337         var qe = {
12338             query: q,
12339             forceAll: forceAll,
12340             combo: this,
12341             cancel:false
12342         };
12343         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12344             return false;
12345         }
12346         q = qe.query;
12347         
12348         forceAll = qe.forceAll;
12349         if(forceAll === true || (q.length >= this.minChars)){
12350             
12351             this.hasQuery = true;
12352             
12353             if(this.lastQuery != q || this.alwaysQuery){
12354                 this.lastQuery = q;
12355                 if(this.mode == 'local'){
12356                     this.selectedIndex = -1;
12357                     if(forceAll){
12358                         this.store.clearFilter();
12359                     }else{
12360                         
12361                         if(this.specialFilter){
12362                             this.fireEvent('specialfilter', this);
12363                             this.onLoad();
12364                             return;
12365                         }
12366                         
12367                         this.store.filter(this.displayField, q);
12368                     }
12369                     
12370                     this.store.fireEvent("datachanged", this.store);
12371                     
12372                     this.onLoad();
12373                     
12374                     
12375                 }else{
12376                     
12377                     this.store.baseParams[this.queryParam] = q;
12378                     
12379                     var options = {params : this.getParams(q)};
12380                     
12381                     if(this.loadNext){
12382                         options.add = true;
12383                         options.params.start = this.page * this.pageSize;
12384                     }
12385                     
12386                     this.store.load(options);
12387                     
12388                     /*
12389                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
12390                      *  we should expand the list on onLoad
12391                      *  so command out it
12392                      */
12393 //                    this.expand();
12394                 }
12395             }else{
12396                 this.selectedIndex = -1;
12397                 this.onLoad();   
12398             }
12399         }
12400         
12401         this.loadNext = false;
12402     },
12403     
12404     // private
12405     getParams : function(q){
12406         var p = {};
12407         //p[this.queryParam] = q;
12408         
12409         if(this.pageSize){
12410             p.start = 0;
12411             p.limit = this.pageSize;
12412         }
12413         return p;
12414     },
12415
12416     /**
12417      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12418      */
12419     collapse : function(){
12420         if(!this.isExpanded()){
12421             return;
12422         }
12423         
12424         this.list.hide();
12425         
12426         if(this.tickable){
12427             this.hasFocus = false;
12428             this.okBtn.hide();
12429             this.cancelBtn.hide();
12430             this.trigger.show();
12431             
12432             if(this.editable){
12433                 this.tickableInputEl().dom.value = '';
12434                 this.tickableInputEl().blur();
12435             }
12436             
12437         }
12438         
12439         Roo.get(document).un('mousedown', this.collapseIf, this);
12440         Roo.get(document).un('mousewheel', this.collapseIf, this);
12441         if (!this.editable) {
12442             Roo.get(document).un('keydown', this.listKeyPress, this);
12443         }
12444         this.fireEvent('collapse', this);
12445     },
12446
12447     // private
12448     collapseIf : function(e){
12449         var in_combo  = e.within(this.el);
12450         var in_list =  e.within(this.list);
12451         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12452         
12453         if (in_combo || in_list || is_list) {
12454             //e.stopPropagation();
12455             return;
12456         }
12457         
12458         if(this.tickable){
12459             this.onTickableFooterButtonClick(e, false, false);
12460         }
12461
12462         this.collapse();
12463         
12464     },
12465
12466     /**
12467      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
12468      */
12469     expand : function(){
12470        
12471         if(this.isExpanded() || !this.hasFocus){
12472             return;
12473         }
12474         
12475         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
12476         this.list.setWidth(lw);
12477         
12478         
12479          Roo.log('expand');
12480         
12481         this.list.show();
12482         
12483         this.restrictHeight();
12484         
12485         if(this.tickable){
12486             
12487             this.tickItems = Roo.apply([], this.item);
12488             
12489             this.okBtn.show();
12490             this.cancelBtn.show();
12491             this.trigger.hide();
12492             
12493             if(this.editable){
12494                 this.tickableInputEl().focus();
12495             }
12496             
12497         }
12498         
12499         Roo.get(document).on('mousedown', this.collapseIf, this);
12500         Roo.get(document).on('mousewheel', this.collapseIf, this);
12501         if (!this.editable) {
12502             Roo.get(document).on('keydown', this.listKeyPress, this);
12503         }
12504         
12505         this.fireEvent('expand', this);
12506     },
12507
12508     // private
12509     // Implements the default empty TriggerField.onTriggerClick function
12510     onTriggerClick : function(e)
12511     {
12512         Roo.log('trigger click');
12513         
12514         if(this.disabled || !this.triggerList){
12515             return;
12516         }
12517         
12518         this.page = 0;
12519         this.loadNext = false;
12520         
12521         if(this.isExpanded()){
12522             this.collapse();
12523             if (!this.blockFocus) {
12524                 this.inputEl().focus();
12525             }
12526             
12527         }else {
12528             this.hasFocus = true;
12529             if(this.triggerAction == 'all') {
12530                 this.doQuery(this.allQuery, true);
12531             } else {
12532                 this.doQuery(this.getRawValue());
12533             }
12534             if (!this.blockFocus) {
12535                 this.inputEl().focus();
12536             }
12537         }
12538     },
12539     
12540     onTickableTriggerClick : function(e)
12541     {
12542         if(this.disabled){
12543             return;
12544         }
12545         
12546         this.page = 0;
12547         this.loadNext = false;
12548         this.hasFocus = true;
12549         
12550         if(this.triggerAction == 'all') {
12551             this.doQuery(this.allQuery, true);
12552         } else {
12553             this.doQuery(this.getRawValue());
12554         }
12555     },
12556     
12557     onSearchFieldClick : function(e)
12558     {
12559         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
12560             this.onTickableFooterButtonClick(e, false, false);
12561             return;
12562         }
12563         
12564         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
12565             return;
12566         }
12567         
12568         this.page = 0;
12569         this.loadNext = false;
12570         this.hasFocus = true;
12571         
12572         if(this.triggerAction == 'all') {
12573             this.doQuery(this.allQuery, true);
12574         } else {
12575             this.doQuery(this.getRawValue());
12576         }
12577     },
12578     
12579     listKeyPress : function(e)
12580     {
12581         //Roo.log('listkeypress');
12582         // scroll to first matching element based on key pres..
12583         if (e.isSpecialKey()) {
12584             return false;
12585         }
12586         var k = String.fromCharCode(e.getKey()).toUpperCase();
12587         //Roo.log(k);
12588         var match  = false;
12589         var csel = this.view.getSelectedNodes();
12590         var cselitem = false;
12591         if (csel.length) {
12592             var ix = this.view.indexOf(csel[0]);
12593             cselitem  = this.store.getAt(ix);
12594             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
12595                 cselitem = false;
12596             }
12597             
12598         }
12599         
12600         this.store.each(function(v) { 
12601             if (cselitem) {
12602                 // start at existing selection.
12603                 if (cselitem.id == v.id) {
12604                     cselitem = false;
12605                 }
12606                 return true;
12607             }
12608                 
12609             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
12610                 match = this.store.indexOf(v);
12611                 return false;
12612             }
12613             return true;
12614         }, this);
12615         
12616         if (match === false) {
12617             return true; // no more action?
12618         }
12619         // scroll to?
12620         this.view.select(match);
12621         var sn = Roo.get(this.view.getSelectedNodes()[0])
12622         sn.scrollIntoView(sn.dom.parentNode, false);
12623     },
12624     
12625     onViewScroll : function(e, t){
12626         
12627         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){
12628             return;
12629         }
12630         
12631         this.hasQuery = true;
12632         
12633         this.loading = this.list.select('.loading', true).first();
12634         
12635         if(this.loading === null){
12636             this.list.createChild({
12637                 tag: 'div',
12638                 cls: 'loading select2-more-results select2-active',
12639                 html: 'Loading more results...'
12640             })
12641             
12642             this.loading = this.list.select('.loading', true).first();
12643             
12644             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
12645             
12646             this.loading.hide();
12647         }
12648         
12649         this.loading.show();
12650         
12651         var _combo = this;
12652         
12653         this.page++;
12654         this.loadNext = true;
12655         
12656         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
12657         
12658         return;
12659     },
12660     
12661     addItem : function(o)
12662     {   
12663         var dv = ''; // display value
12664         
12665         if (this.displayField) {
12666             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12667         } else {
12668             // this is an error condition!!!
12669             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12670         }
12671         
12672         if(!dv.length){
12673             return;
12674         }
12675         
12676         var choice = this.choices.createChild({
12677             tag: 'li',
12678             cls: 'select2-search-choice',
12679             cn: [
12680                 {
12681                     tag: 'div',
12682                     html: dv
12683                 },
12684                 {
12685                     tag: 'a',
12686                     href: '#',
12687                     cls: 'select2-search-choice-close',
12688                     tabindex: '-1'
12689                 }
12690             ]
12691             
12692         }, this.searchField);
12693         
12694         var close = choice.select('a.select2-search-choice-close', true).first()
12695         
12696         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
12697         
12698         this.item.push(o);
12699         
12700         this.lastData = o;
12701         
12702         this.syncValue();
12703         
12704         this.inputEl().dom.value = '';
12705         
12706         this.validate();
12707     },
12708     
12709     onRemoveItem : function(e, _self, o)
12710     {
12711         e.preventDefault();
12712         
12713         this.lastItem = Roo.apply([], this.item);
12714         
12715         var index = this.item.indexOf(o.data) * 1;
12716         
12717         if( index < 0){
12718             Roo.log('not this item?!');
12719             return;
12720         }
12721         
12722         this.item.splice(index, 1);
12723         o.item.remove();
12724         
12725         this.syncValue();
12726         
12727         this.fireEvent('remove', this, e);
12728         
12729         this.validate();
12730         
12731     },
12732     
12733     syncValue : function()
12734     {
12735         if(!this.item.length){
12736             this.clearValue();
12737             return;
12738         }
12739             
12740         var value = [];
12741         var _this = this;
12742         Roo.each(this.item, function(i){
12743             if(_this.valueField){
12744                 value.push(i[_this.valueField]);
12745                 return;
12746             }
12747
12748             value.push(i);
12749         });
12750
12751         this.value = value.join(',');
12752
12753         if(this.hiddenField){
12754             this.hiddenField.dom.value = this.value;
12755         }
12756         
12757         this.store.fireEvent("datachanged", this.store);
12758     },
12759     
12760     clearItem : function()
12761     {
12762         if(!this.multiple){
12763             return;
12764         }
12765         
12766         this.item = [];
12767         
12768         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
12769            c.remove();
12770         });
12771         
12772         this.syncValue();
12773         
12774         this.validate();
12775     },
12776     
12777     inputEl: function ()
12778     {
12779         if(this.tickable){
12780             return this.searchField;
12781         }
12782         return this.el.select('input.form-control',true).first();
12783     },
12784     
12785     
12786     onTickableFooterButtonClick : function(e, btn, el)
12787     {
12788         e.preventDefault();
12789         
12790         this.lastItem = Roo.apply([], this.item);
12791         
12792         if(btn && btn.name == 'cancel'){
12793             this.tickItems = Roo.apply([], this.item);
12794             this.collapse();
12795             return;
12796         }
12797         
12798         this.clearItem();
12799         
12800         var _this = this;
12801         
12802         Roo.each(this.tickItems, function(o){
12803             _this.addItem(o);
12804         });
12805         
12806         this.collapse();
12807         
12808     },
12809     
12810     validate : function()
12811     {
12812         var v = this.getRawValue();
12813         
12814         if(this.multiple){
12815             v = this.getValue();
12816         }
12817         
12818         if(this.disabled || this.allowBlank || v.length){
12819             this.markValid();
12820             return true;
12821         }
12822         
12823         this.markInvalid();
12824         return false;
12825     },
12826     
12827     tickableInputEl : function()
12828     {
12829         if(!this.tickable || !this.editable){
12830             return this.inputEl();
12831         }
12832         
12833         return this.inputEl().select('.select2-search-field-input', true).first();
12834     }
12835     
12836     
12837
12838     /** 
12839     * @cfg {Boolean} grow 
12840     * @hide 
12841     */
12842     /** 
12843     * @cfg {Number} growMin 
12844     * @hide 
12845     */
12846     /** 
12847     * @cfg {Number} growMax 
12848     * @hide 
12849     */
12850     /**
12851      * @hide
12852      * @method autoSize
12853      */
12854 });
12855 /*
12856  * Based on:
12857  * Ext JS Library 1.1.1
12858  * Copyright(c) 2006-2007, Ext JS, LLC.
12859  *
12860  * Originally Released Under LGPL - original licence link has changed is not relivant.
12861  *
12862  * Fork - LGPL
12863  * <script type="text/javascript">
12864  */
12865
12866 /**
12867  * @class Roo.View
12868  * @extends Roo.util.Observable
12869  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
12870  * This class also supports single and multi selection modes. <br>
12871  * Create a data model bound view:
12872  <pre><code>
12873  var store = new Roo.data.Store(...);
12874
12875  var view = new Roo.View({
12876     el : "my-element",
12877     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
12878  
12879     singleSelect: true,
12880     selectedClass: "ydataview-selected",
12881     store: store
12882  });
12883
12884  // listen for node click?
12885  view.on("click", function(vw, index, node, e){
12886  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
12887  });
12888
12889  // load XML data
12890  dataModel.load("foobar.xml");
12891  </code></pre>
12892  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
12893  * <br><br>
12894  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
12895  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
12896  * 
12897  * Note: old style constructor is still suported (container, template, config)
12898  * 
12899  * @constructor
12900  * Create a new View
12901  * @param {Object} config The config object
12902  * 
12903  */
12904 Roo.View = function(config, depreciated_tpl, depreciated_config){
12905     
12906     this.parent = false;
12907     
12908     if (typeof(depreciated_tpl) == 'undefined') {
12909         // new way.. - universal constructor.
12910         Roo.apply(this, config);
12911         this.el  = Roo.get(this.el);
12912     } else {
12913         // old format..
12914         this.el  = Roo.get(config);
12915         this.tpl = depreciated_tpl;
12916         Roo.apply(this, depreciated_config);
12917     }
12918     this.wrapEl  = this.el.wrap().wrap();
12919     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
12920     
12921     
12922     if(typeof(this.tpl) == "string"){
12923         this.tpl = new Roo.Template(this.tpl);
12924     } else {
12925         // support xtype ctors..
12926         this.tpl = new Roo.factory(this.tpl, Roo);
12927     }
12928     
12929     
12930     this.tpl.compile();
12931     
12932     /** @private */
12933     this.addEvents({
12934         /**
12935          * @event beforeclick
12936          * Fires before a click is processed. Returns false to cancel the default action.
12937          * @param {Roo.View} this
12938          * @param {Number} index The index of the target node
12939          * @param {HTMLElement} node The target node
12940          * @param {Roo.EventObject} e The raw event object
12941          */
12942             "beforeclick" : true,
12943         /**
12944          * @event click
12945          * Fires when a template node is clicked.
12946          * @param {Roo.View} this
12947          * @param {Number} index The index of the target node
12948          * @param {HTMLElement} node The target node
12949          * @param {Roo.EventObject} e The raw event object
12950          */
12951             "click" : true,
12952         /**
12953          * @event dblclick
12954          * Fires when a template node is double clicked.
12955          * @param {Roo.View} this
12956          * @param {Number} index The index of the target node
12957          * @param {HTMLElement} node The target node
12958          * @param {Roo.EventObject} e The raw event object
12959          */
12960             "dblclick" : true,
12961         /**
12962          * @event contextmenu
12963          * Fires when a template node is right clicked.
12964          * @param {Roo.View} this
12965          * @param {Number} index The index of the target node
12966          * @param {HTMLElement} node The target node
12967          * @param {Roo.EventObject} e The raw event object
12968          */
12969             "contextmenu" : true,
12970         /**
12971          * @event selectionchange
12972          * Fires when the selected nodes change.
12973          * @param {Roo.View} this
12974          * @param {Array} selections Array of the selected nodes
12975          */
12976             "selectionchange" : true,
12977     
12978         /**
12979          * @event beforeselect
12980          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
12981          * @param {Roo.View} this
12982          * @param {HTMLElement} node The node to be selected
12983          * @param {Array} selections Array of currently selected nodes
12984          */
12985             "beforeselect" : true,
12986         /**
12987          * @event preparedata
12988          * Fires on every row to render, to allow you to change the data.
12989          * @param {Roo.View} this
12990          * @param {Object} data to be rendered (change this)
12991          */
12992           "preparedata" : true
12993           
12994           
12995         });
12996
12997
12998
12999     this.el.on({
13000         "click": this.onClick,
13001         "dblclick": this.onDblClick,
13002         "contextmenu": this.onContextMenu,
13003         scope:this
13004     });
13005
13006     this.selections = [];
13007     this.nodes = [];
13008     this.cmp = new Roo.CompositeElementLite([]);
13009     if(this.store){
13010         this.store = Roo.factory(this.store, Roo.data);
13011         this.setStore(this.store, true);
13012     }
13013     
13014     if ( this.footer && this.footer.xtype) {
13015            
13016          var fctr = this.wrapEl.appendChild(document.createElement("div"));
13017         
13018         this.footer.dataSource = this.store
13019         this.footer.container = fctr;
13020         this.footer = Roo.factory(this.footer, Roo);
13021         fctr.insertFirst(this.el);
13022         
13023         // this is a bit insane - as the paging toolbar seems to detach the el..
13024 //        dom.parentNode.parentNode.parentNode
13025          // they get detached?
13026     }
13027     
13028     
13029     Roo.View.superclass.constructor.call(this);
13030     
13031     
13032 };
13033
13034 Roo.extend(Roo.View, Roo.util.Observable, {
13035     
13036      /**
13037      * @cfg {Roo.data.Store} store Data store to load data from.
13038      */
13039     store : false,
13040     
13041     /**
13042      * @cfg {String|Roo.Element} el The container element.
13043      */
13044     el : '',
13045     
13046     /**
13047      * @cfg {String|Roo.Template} tpl The template used by this View 
13048      */
13049     tpl : false,
13050     /**
13051      * @cfg {String} dataName the named area of the template to use as the data area
13052      *                          Works with domtemplates roo-name="name"
13053      */
13054     dataName: false,
13055     /**
13056      * @cfg {String} selectedClass The css class to add to selected nodes
13057      */
13058     selectedClass : "x-view-selected",
13059      /**
13060      * @cfg {String} emptyText The empty text to show when nothing is loaded.
13061      */
13062     emptyText : "",
13063     
13064     /**
13065      * @cfg {String} text to display on mask (default Loading)
13066      */
13067     mask : false,
13068     /**
13069      * @cfg {Boolean} multiSelect Allow multiple selection
13070      */
13071     multiSelect : false,
13072     /**
13073      * @cfg {Boolean} singleSelect Allow single selection
13074      */
13075     singleSelect:  false,
13076     
13077     /**
13078      * @cfg {Boolean} toggleSelect - selecting 
13079      */
13080     toggleSelect : false,
13081     
13082     /**
13083      * @cfg {Boolean} tickable - selecting 
13084      */
13085     tickable : false,
13086     
13087     /**
13088      * Returns the element this view is bound to.
13089      * @return {Roo.Element}
13090      */
13091     getEl : function(){
13092         return this.wrapEl;
13093     },
13094     
13095     
13096
13097     /**
13098      * Refreshes the view. - called by datachanged on the store. - do not call directly.
13099      */
13100     refresh : function(){
13101         //Roo.log('refresh');
13102         var t = this.tpl;
13103         
13104         // if we are using something like 'domtemplate', then
13105         // the what gets used is:
13106         // t.applySubtemplate(NAME, data, wrapping data..)
13107         // the outer template then get' applied with
13108         //     the store 'extra data'
13109         // and the body get's added to the
13110         //      roo-name="data" node?
13111         //      <span class='roo-tpl-{name}'></span> ?????
13112         
13113         
13114         
13115         this.clearSelections();
13116         this.el.update("");
13117         var html = [];
13118         var records = this.store.getRange();
13119         if(records.length < 1) {
13120             
13121             // is this valid??  = should it render a template??
13122             
13123             this.el.update(this.emptyText);
13124             return;
13125         }
13126         var el = this.el;
13127         if (this.dataName) {
13128             this.el.update(t.apply(this.store.meta)); //????
13129             el = this.el.child('.roo-tpl-' + this.dataName);
13130         }
13131         
13132         for(var i = 0, len = records.length; i < len; i++){
13133             var data = this.prepareData(records[i].data, i, records[i]);
13134             this.fireEvent("preparedata", this, data, i, records[i]);
13135             
13136             var d = Roo.apply({}, data);
13137             
13138             if(this.tickable){
13139                 Roo.apply(d, {'roo-id' : Roo.id()});
13140                 
13141                 var _this = this;
13142             
13143                 Roo.each(this.parent.item, function(item){
13144                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
13145                         return;
13146                     }
13147                     Roo.apply(d, {'roo-data-checked' : 'checked'});
13148                 });
13149             }
13150             
13151             html[html.length] = Roo.util.Format.trim(
13152                 this.dataName ?
13153                     t.applySubtemplate(this.dataName, d, this.store.meta) :
13154                     t.apply(d)
13155             );
13156         }
13157         
13158         
13159         
13160         el.update(html.join(""));
13161         this.nodes = el.dom.childNodes;
13162         this.updateIndexes(0);
13163     },
13164     
13165
13166     /**
13167      * Function to override to reformat the data that is sent to
13168      * the template for each node.
13169      * DEPRICATED - use the preparedata event handler.
13170      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
13171      * a JSON object for an UpdateManager bound view).
13172      */
13173     prepareData : function(data, index, record)
13174     {
13175         this.fireEvent("preparedata", this, data, index, record);
13176         return data;
13177     },
13178
13179     onUpdate : function(ds, record){
13180         // Roo.log('on update');   
13181         this.clearSelections();
13182         var index = this.store.indexOf(record);
13183         var n = this.nodes[index];
13184         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
13185         n.parentNode.removeChild(n);
13186         this.updateIndexes(index, index);
13187     },
13188
13189     
13190     
13191 // --------- FIXME     
13192     onAdd : function(ds, records, index)
13193     {
13194         //Roo.log(['on Add', ds, records, index] );        
13195         this.clearSelections();
13196         if(this.nodes.length == 0){
13197             this.refresh();
13198             return;
13199         }
13200         var n = this.nodes[index];
13201         for(var i = 0, len = records.length; i < len; i++){
13202             var d = this.prepareData(records[i].data, i, records[i]);
13203             if(n){
13204                 this.tpl.insertBefore(n, d);
13205             }else{
13206                 
13207                 this.tpl.append(this.el, d);
13208             }
13209         }
13210         this.updateIndexes(index);
13211     },
13212
13213     onRemove : function(ds, record, index){
13214        // Roo.log('onRemove');
13215         this.clearSelections();
13216         var el = this.dataName  ?
13217             this.el.child('.roo-tpl-' + this.dataName) :
13218             this.el; 
13219         
13220         el.dom.removeChild(this.nodes[index]);
13221         this.updateIndexes(index);
13222     },
13223
13224     /**
13225      * Refresh an individual node.
13226      * @param {Number} index
13227      */
13228     refreshNode : function(index){
13229         this.onUpdate(this.store, this.store.getAt(index));
13230     },
13231
13232     updateIndexes : function(startIndex, endIndex){
13233         var ns = this.nodes;
13234         startIndex = startIndex || 0;
13235         endIndex = endIndex || ns.length - 1;
13236         for(var i = startIndex; i <= endIndex; i++){
13237             ns[i].nodeIndex = i;
13238         }
13239     },
13240
13241     /**
13242      * Changes the data store this view uses and refresh the view.
13243      * @param {Store} store
13244      */
13245     setStore : function(store, initial){
13246         if(!initial && this.store){
13247             this.store.un("datachanged", this.refresh);
13248             this.store.un("add", this.onAdd);
13249             this.store.un("remove", this.onRemove);
13250             this.store.un("update", this.onUpdate);
13251             this.store.un("clear", this.refresh);
13252             this.store.un("beforeload", this.onBeforeLoad);
13253             this.store.un("load", this.onLoad);
13254             this.store.un("loadexception", this.onLoad);
13255         }
13256         if(store){
13257           
13258             store.on("datachanged", this.refresh, this);
13259             store.on("add", this.onAdd, this);
13260             store.on("remove", this.onRemove, this);
13261             store.on("update", this.onUpdate, this);
13262             store.on("clear", this.refresh, this);
13263             store.on("beforeload", this.onBeforeLoad, this);
13264             store.on("load", this.onLoad, this);
13265             store.on("loadexception", this.onLoad, this);
13266         }
13267         
13268         if(store){
13269             this.refresh();
13270         }
13271     },
13272     /**
13273      * onbeforeLoad - masks the loading area.
13274      *
13275      */
13276     onBeforeLoad : function(store,opts)
13277     {
13278          //Roo.log('onBeforeLoad');   
13279         if (!opts.add) {
13280             this.el.update("");
13281         }
13282         this.el.mask(this.mask ? this.mask : "Loading" ); 
13283     },
13284     onLoad : function ()
13285     {
13286         this.el.unmask();
13287     },
13288     
13289
13290     /**
13291      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
13292      * @param {HTMLElement} node
13293      * @return {HTMLElement} The template node
13294      */
13295     findItemFromChild : function(node){
13296         var el = this.dataName  ?
13297             this.el.child('.roo-tpl-' + this.dataName,true) :
13298             this.el.dom; 
13299         
13300         if(!node || node.parentNode == el){
13301                     return node;
13302             }
13303             var p = node.parentNode;
13304             while(p && p != el){
13305             if(p.parentNode == el){
13306                 return p;
13307             }
13308             p = p.parentNode;
13309         }
13310             return null;
13311     },
13312
13313     /** @ignore */
13314     onClick : function(e){
13315         var item = this.findItemFromChild(e.getTarget());
13316         if(item){
13317             var index = this.indexOf(item);
13318             if(this.onItemClick(item, index, e) !== false){
13319                 this.fireEvent("click", this, index, item, e);
13320             }
13321         }else{
13322             this.clearSelections();
13323         }
13324     },
13325
13326     /** @ignore */
13327     onContextMenu : function(e){
13328         var item = this.findItemFromChild(e.getTarget());
13329         if(item){
13330             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
13331         }
13332     },
13333
13334     /** @ignore */
13335     onDblClick : function(e){
13336         var item = this.findItemFromChild(e.getTarget());
13337         if(item){
13338             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
13339         }
13340     },
13341
13342     onItemClick : function(item, index, e)
13343     {
13344         if(this.fireEvent("beforeclick", this, index, item, e) === false){
13345             return false;
13346         }
13347         if (this.toggleSelect) {
13348             var m = this.isSelected(item) ? 'unselect' : 'select';
13349             //Roo.log(m);
13350             var _t = this;
13351             _t[m](item, true, false);
13352             return true;
13353         }
13354         if(this.multiSelect || this.singleSelect){
13355             if(this.multiSelect && e.shiftKey && this.lastSelection){
13356                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
13357             }else{
13358                 this.select(item, this.multiSelect && e.ctrlKey);
13359                 this.lastSelection = item;
13360             }
13361             
13362             if(!this.tickable){
13363                 e.preventDefault();
13364             }
13365             
13366         }
13367         return true;
13368     },
13369
13370     /**
13371      * Get the number of selected nodes.
13372      * @return {Number}
13373      */
13374     getSelectionCount : function(){
13375         return this.selections.length;
13376     },
13377
13378     /**
13379      * Get the currently selected nodes.
13380      * @return {Array} An array of HTMLElements
13381      */
13382     getSelectedNodes : function(){
13383         return this.selections;
13384     },
13385
13386     /**
13387      * Get the indexes of the selected nodes.
13388      * @return {Array}
13389      */
13390     getSelectedIndexes : function(){
13391         var indexes = [], s = this.selections;
13392         for(var i = 0, len = s.length; i < len; i++){
13393             indexes.push(s[i].nodeIndex);
13394         }
13395         return indexes;
13396     },
13397
13398     /**
13399      * Clear all selections
13400      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
13401      */
13402     clearSelections : function(suppressEvent){
13403         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
13404             this.cmp.elements = this.selections;
13405             this.cmp.removeClass(this.selectedClass);
13406             this.selections = [];
13407             if(!suppressEvent){
13408                 this.fireEvent("selectionchange", this, this.selections);
13409             }
13410         }
13411     },
13412
13413     /**
13414      * Returns true if the passed node is selected
13415      * @param {HTMLElement/Number} node The node or node index
13416      * @return {Boolean}
13417      */
13418     isSelected : function(node){
13419         var s = this.selections;
13420         if(s.length < 1){
13421             return false;
13422         }
13423         node = this.getNode(node);
13424         return s.indexOf(node) !== -1;
13425     },
13426
13427     /**
13428      * Selects nodes.
13429      * @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
13430      * @param {Boolean} keepExisting (optional) true to keep existing selections
13431      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
13432      */
13433     select : function(nodeInfo, keepExisting, suppressEvent){
13434         if(nodeInfo instanceof Array){
13435             if(!keepExisting){
13436                 this.clearSelections(true);
13437             }
13438             for(var i = 0, len = nodeInfo.length; i < len; i++){
13439                 this.select(nodeInfo[i], true, true);
13440             }
13441             return;
13442         } 
13443         var node = this.getNode(nodeInfo);
13444         if(!node || this.isSelected(node)){
13445             return; // already selected.
13446         }
13447         if(!keepExisting){
13448             this.clearSelections(true);
13449         }
13450         
13451         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
13452             Roo.fly(node).addClass(this.selectedClass);
13453             this.selections.push(node);
13454             if(!suppressEvent){
13455                 this.fireEvent("selectionchange", this, this.selections);
13456             }
13457         }
13458         
13459         
13460     },
13461       /**
13462      * Unselects nodes.
13463      * @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
13464      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
13465      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
13466      */
13467     unselect : function(nodeInfo, keepExisting, suppressEvent)
13468     {
13469         if(nodeInfo instanceof Array){
13470             Roo.each(this.selections, function(s) {
13471                 this.unselect(s, nodeInfo);
13472             }, this);
13473             return;
13474         }
13475         var node = this.getNode(nodeInfo);
13476         if(!node || !this.isSelected(node)){
13477             //Roo.log("not selected");
13478             return; // not selected.
13479         }
13480         // fireevent???
13481         var ns = [];
13482         Roo.each(this.selections, function(s) {
13483             if (s == node ) {
13484                 Roo.fly(node).removeClass(this.selectedClass);
13485
13486                 return;
13487             }
13488             ns.push(s);
13489         },this);
13490         
13491         this.selections= ns;
13492         this.fireEvent("selectionchange", this, this.selections);
13493     },
13494
13495     /**
13496      * Gets a template node.
13497      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
13498      * @return {HTMLElement} The node or null if it wasn't found
13499      */
13500     getNode : function(nodeInfo){
13501         if(typeof nodeInfo == "string"){
13502             return document.getElementById(nodeInfo);
13503         }else if(typeof nodeInfo == "number"){
13504             return this.nodes[nodeInfo];
13505         }
13506         return nodeInfo;
13507     },
13508
13509     /**
13510      * Gets a range template nodes.
13511      * @param {Number} startIndex
13512      * @param {Number} endIndex
13513      * @return {Array} An array of nodes
13514      */
13515     getNodes : function(start, end){
13516         var ns = this.nodes;
13517         start = start || 0;
13518         end = typeof end == "undefined" ? ns.length - 1 : end;
13519         var nodes = [];
13520         if(start <= end){
13521             for(var i = start; i <= end; i++){
13522                 nodes.push(ns[i]);
13523             }
13524         } else{
13525             for(var i = start; i >= end; i--){
13526                 nodes.push(ns[i]);
13527             }
13528         }
13529         return nodes;
13530     },
13531
13532     /**
13533      * Finds the index of the passed node
13534      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
13535      * @return {Number} The index of the node or -1
13536      */
13537     indexOf : function(node){
13538         node = this.getNode(node);
13539         if(typeof node.nodeIndex == "number"){
13540             return node.nodeIndex;
13541         }
13542         var ns = this.nodes;
13543         for(var i = 0, len = ns.length; i < len; i++){
13544             if(ns[i] == node){
13545                 return i;
13546             }
13547         }
13548         return -1;
13549     }
13550 });
13551 /*
13552  * - LGPL
13553  *
13554  * based on jquery fullcalendar
13555  * 
13556  */
13557
13558 Roo.bootstrap = Roo.bootstrap || {};
13559 /**
13560  * @class Roo.bootstrap.Calendar
13561  * @extends Roo.bootstrap.Component
13562  * Bootstrap Calendar class
13563  * @cfg {Boolean} loadMask (true|false) default false
13564  * @cfg {Object} header generate the user specific header of the calendar, default false
13565
13566  * @constructor
13567  * Create a new Container
13568  * @param {Object} config The config object
13569  */
13570
13571
13572
13573 Roo.bootstrap.Calendar = function(config){
13574     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
13575      this.addEvents({
13576         /**
13577              * @event select
13578              * Fires when a date is selected
13579              * @param {DatePicker} this
13580              * @param {Date} date The selected date
13581              */
13582         'select': true,
13583         /**
13584              * @event monthchange
13585              * Fires when the displayed month changes 
13586              * @param {DatePicker} this
13587              * @param {Date} date The selected month
13588              */
13589         'monthchange': true,
13590         /**
13591              * @event evententer
13592              * Fires when mouse over an event
13593              * @param {Calendar} this
13594              * @param {event} Event
13595              */
13596         'evententer': true,
13597         /**
13598              * @event eventleave
13599              * Fires when the mouse leaves an
13600              * @param {Calendar} this
13601              * @param {event}
13602              */
13603         'eventleave': true,
13604         /**
13605              * @event eventclick
13606              * Fires when the mouse click an
13607              * @param {Calendar} this
13608              * @param {event}
13609              */
13610         'eventclick': true
13611         
13612     });
13613
13614 };
13615
13616 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
13617     
13618      /**
13619      * @cfg {Number} startDay
13620      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
13621      */
13622     startDay : 0,
13623     
13624     loadMask : false,
13625     
13626     header : false,
13627       
13628     getAutoCreate : function(){
13629         
13630         
13631         var fc_button = function(name, corner, style, content ) {
13632             return Roo.apply({},{
13633                 tag : 'span',
13634                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
13635                          (corner.length ?
13636                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
13637                             ''
13638                         ),
13639                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
13640                 unselectable: 'on'
13641             });
13642         };
13643         
13644         var header = {};
13645         
13646         if(!this.header){
13647             header = {
13648                 tag : 'table',
13649                 cls : 'fc-header',
13650                 style : 'width:100%',
13651                 cn : [
13652                     {
13653                         tag: 'tr',
13654                         cn : [
13655                             {
13656                                 tag : 'td',
13657                                 cls : 'fc-header-left',
13658                                 cn : [
13659                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
13660                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
13661                                     { tag: 'span', cls: 'fc-header-space' },
13662                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
13663
13664
13665                                 ]
13666                             },
13667
13668                             {
13669                                 tag : 'td',
13670                                 cls : 'fc-header-center',
13671                                 cn : [
13672                                     {
13673                                         tag: 'span',
13674                                         cls: 'fc-header-title',
13675                                         cn : {
13676                                             tag: 'H2',
13677                                             html : 'month / year'
13678                                         }
13679                                     }
13680
13681                                 ]
13682                             },
13683                             {
13684                                 tag : 'td',
13685                                 cls : 'fc-header-right',
13686                                 cn : [
13687                               /*      fc_button('month', 'left', '', 'month' ),
13688                                     fc_button('week', '', '', 'week' ),
13689                                     fc_button('day', 'right', '', 'day' )
13690                                 */    
13691
13692                                 ]
13693                             }
13694
13695                         ]
13696                     }
13697                 ]
13698             };
13699         }
13700         
13701         header = this.header;
13702         
13703        
13704         var cal_heads = function() {
13705             var ret = [];
13706             // fixme - handle this.
13707             
13708             for (var i =0; i < Date.dayNames.length; i++) {
13709                 var d = Date.dayNames[i];
13710                 ret.push({
13711                     tag: 'th',
13712                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
13713                     html : d.substring(0,3)
13714                 });
13715                 
13716             }
13717             ret[0].cls += ' fc-first';
13718             ret[6].cls += ' fc-last';
13719             return ret;
13720         };
13721         var cal_cell = function(n) {
13722             return  {
13723                 tag: 'td',
13724                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
13725                 cn : [
13726                     {
13727                         cn : [
13728                             {
13729                                 cls: 'fc-day-number',
13730                                 html: 'D'
13731                             },
13732                             {
13733                                 cls: 'fc-day-content',
13734                              
13735                                 cn : [
13736                                      {
13737                                         style: 'position: relative;' // height: 17px;
13738                                     }
13739                                 ]
13740                             }
13741                             
13742                             
13743                         ]
13744                     }
13745                 ]
13746                 
13747             }
13748         };
13749         var cal_rows = function() {
13750             
13751             var ret = [];
13752             for (var r = 0; r < 6; r++) {
13753                 var row= {
13754                     tag : 'tr',
13755                     cls : 'fc-week',
13756                     cn : []
13757                 };
13758                 
13759                 for (var i =0; i < Date.dayNames.length; i++) {
13760                     var d = Date.dayNames[i];
13761                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
13762
13763                 }
13764                 row.cn[0].cls+=' fc-first';
13765                 row.cn[0].cn[0].style = 'min-height:90px';
13766                 row.cn[6].cls+=' fc-last';
13767                 ret.push(row);
13768                 
13769             }
13770             ret[0].cls += ' fc-first';
13771             ret[4].cls += ' fc-prev-last';
13772             ret[5].cls += ' fc-last';
13773             return ret;
13774             
13775         };
13776         
13777         var cal_table = {
13778             tag: 'table',
13779             cls: 'fc-border-separate',
13780             style : 'width:100%',
13781             cellspacing  : 0,
13782             cn : [
13783                 { 
13784                     tag: 'thead',
13785                     cn : [
13786                         { 
13787                             tag: 'tr',
13788                             cls : 'fc-first fc-last',
13789                             cn : cal_heads()
13790                         }
13791                     ]
13792                 },
13793                 { 
13794                     tag: 'tbody',
13795                     cn : cal_rows()
13796                 }
13797                   
13798             ]
13799         };
13800          
13801          var cfg = {
13802             cls : 'fc fc-ltr',
13803             cn : [
13804                 header,
13805                 {
13806                     cls : 'fc-content',
13807                     style : "position: relative;",
13808                     cn : [
13809                         {
13810                             cls : 'fc-view fc-view-month fc-grid',
13811                             style : 'position: relative',
13812                             unselectable : 'on',
13813                             cn : [
13814                                 {
13815                                     cls : 'fc-event-container',
13816                                     style : 'position:absolute;z-index:8;top:0;left:0;'
13817                                 },
13818                                 cal_table
13819                             ]
13820                         }
13821                     ]
13822     
13823                 }
13824            ] 
13825             
13826         };
13827         
13828          
13829         
13830         return cfg;
13831     },
13832     
13833     
13834     initEvents : function()
13835     {
13836         if(!this.store){
13837             throw "can not find store for calendar";
13838         }
13839         
13840         var mark = {
13841             tag: "div",
13842             cls:"x-dlg-mask",
13843             style: "text-align:center",
13844             cn: [
13845                 {
13846                     tag: "div",
13847                     style: "background-color:white;width:50%;margin:250 auto",
13848                     cn: [
13849                         {
13850                             tag: "img",
13851                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
13852                         },
13853                         {
13854                             tag: "span",
13855                             html: "Loading"
13856                         }
13857                         
13858                     ]
13859                 }
13860             ]
13861         }
13862         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
13863         
13864         var size = this.el.select('.fc-content', true).first().getSize();
13865         this.maskEl.setSize(size.width, size.height);
13866         this.maskEl.enableDisplayMode("block");
13867         if(!this.loadMask){
13868             this.maskEl.hide();
13869         }
13870         
13871         this.store = Roo.factory(this.store, Roo.data);
13872         this.store.on('load', this.onLoad, this);
13873         this.store.on('beforeload', this.onBeforeLoad, this);
13874         
13875         this.resize();
13876         
13877         this.cells = this.el.select('.fc-day',true);
13878         //Roo.log(this.cells);
13879         this.textNodes = this.el.query('.fc-day-number');
13880         this.cells.addClassOnOver('fc-state-hover');
13881         
13882         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
13883         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
13884         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
13885         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
13886         
13887         this.on('monthchange', this.onMonthChange, this);
13888         
13889         this.update(new Date().clearTime());
13890     },
13891     
13892     resize : function() {
13893         var sz  = this.el.getSize();
13894         
13895         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
13896         this.el.select('.fc-day-content div',true).setHeight(34);
13897     },
13898     
13899     
13900     // private
13901     showPrevMonth : function(e){
13902         this.update(this.activeDate.add("mo", -1));
13903     },
13904     showToday : function(e){
13905         this.update(new Date().clearTime());
13906     },
13907     // private
13908     showNextMonth : function(e){
13909         this.update(this.activeDate.add("mo", 1));
13910     },
13911
13912     // private
13913     showPrevYear : function(){
13914         this.update(this.activeDate.add("y", -1));
13915     },
13916
13917     // private
13918     showNextYear : function(){
13919         this.update(this.activeDate.add("y", 1));
13920     },
13921
13922     
13923    // private
13924     update : function(date)
13925     {
13926         var vd = this.activeDate;
13927         this.activeDate = date;
13928 //        if(vd && this.el){
13929 //            var t = date.getTime();
13930 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
13931 //                Roo.log('using add remove');
13932 //                
13933 //                this.fireEvent('monthchange', this, date);
13934 //                
13935 //                this.cells.removeClass("fc-state-highlight");
13936 //                this.cells.each(function(c){
13937 //                   if(c.dateValue == t){
13938 //                       c.addClass("fc-state-highlight");
13939 //                       setTimeout(function(){
13940 //                            try{c.dom.firstChild.focus();}catch(e){}
13941 //                       }, 50);
13942 //                       return false;
13943 //                   }
13944 //                   return true;
13945 //                });
13946 //                return;
13947 //            }
13948 //        }
13949         
13950         var days = date.getDaysInMonth();
13951         
13952         var firstOfMonth = date.getFirstDateOfMonth();
13953         var startingPos = firstOfMonth.getDay()-this.startDay;
13954         
13955         if(startingPos < this.startDay){
13956             startingPos += 7;
13957         }
13958         
13959         var pm = date.add(Date.MONTH, -1);
13960         var prevStart = pm.getDaysInMonth()-startingPos;
13961 //        
13962         this.cells = this.el.select('.fc-day',true);
13963         this.textNodes = this.el.query('.fc-day-number');
13964         this.cells.addClassOnOver('fc-state-hover');
13965         
13966         var cells = this.cells.elements;
13967         var textEls = this.textNodes;
13968         
13969         Roo.each(cells, function(cell){
13970             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
13971         });
13972         
13973         days += startingPos;
13974
13975         // convert everything to numbers so it's fast
13976         var day = 86400000;
13977         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
13978         //Roo.log(d);
13979         //Roo.log(pm);
13980         //Roo.log(prevStart);
13981         
13982         var today = new Date().clearTime().getTime();
13983         var sel = date.clearTime().getTime();
13984         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
13985         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
13986         var ddMatch = this.disabledDatesRE;
13987         var ddText = this.disabledDatesText;
13988         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
13989         var ddaysText = this.disabledDaysText;
13990         var format = this.format;
13991         
13992         var setCellClass = function(cal, cell){
13993             cell.row = 0;
13994             cell.events = [];
13995             cell.more = [];
13996             //Roo.log('set Cell Class');
13997             cell.title = "";
13998             var t = d.getTime();
13999             
14000             //Roo.log(d);
14001             
14002             cell.dateValue = t;
14003             if(t == today){
14004                 cell.className += " fc-today";
14005                 cell.className += " fc-state-highlight";
14006                 cell.title = cal.todayText;
14007             }
14008             if(t == sel){
14009                 // disable highlight in other month..
14010                 //cell.className += " fc-state-highlight";
14011                 
14012             }
14013             // disabling
14014             if(t < min) {
14015                 cell.className = " fc-state-disabled";
14016                 cell.title = cal.minText;
14017                 return;
14018             }
14019             if(t > max) {
14020                 cell.className = " fc-state-disabled";
14021                 cell.title = cal.maxText;
14022                 return;
14023             }
14024             if(ddays){
14025                 if(ddays.indexOf(d.getDay()) != -1){
14026                     cell.title = ddaysText;
14027                     cell.className = " fc-state-disabled";
14028                 }
14029             }
14030             if(ddMatch && format){
14031                 var fvalue = d.dateFormat(format);
14032                 if(ddMatch.test(fvalue)){
14033                     cell.title = ddText.replace("%0", fvalue);
14034                     cell.className = " fc-state-disabled";
14035                 }
14036             }
14037             
14038             if (!cell.initialClassName) {
14039                 cell.initialClassName = cell.dom.className;
14040             }
14041             
14042             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
14043         };
14044
14045         var i = 0;
14046         
14047         for(; i < startingPos; i++) {
14048             textEls[i].innerHTML = (++prevStart);
14049             d.setDate(d.getDate()+1);
14050             
14051             cells[i].className = "fc-past fc-other-month";
14052             setCellClass(this, cells[i]);
14053         }
14054         
14055         var intDay = 0;
14056         
14057         for(; i < days; i++){
14058             intDay = i - startingPos + 1;
14059             textEls[i].innerHTML = (intDay);
14060             d.setDate(d.getDate()+1);
14061             
14062             cells[i].className = ''; // "x-date-active";
14063             setCellClass(this, cells[i]);
14064         }
14065         var extraDays = 0;
14066         
14067         for(; i < 42; i++) {
14068             textEls[i].innerHTML = (++extraDays);
14069             d.setDate(d.getDate()+1);
14070             
14071             cells[i].className = "fc-future fc-other-month";
14072             setCellClass(this, cells[i]);
14073         }
14074         
14075         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
14076         
14077         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
14078         
14079         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
14080         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
14081         
14082         if(totalRows != 6){
14083             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
14084             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
14085         }
14086         
14087         this.fireEvent('monthchange', this, date);
14088         
14089         
14090         /*
14091         if(!this.internalRender){
14092             var main = this.el.dom.firstChild;
14093             var w = main.offsetWidth;
14094             this.el.setWidth(w + this.el.getBorderWidth("lr"));
14095             Roo.fly(main).setWidth(w);
14096             this.internalRender = true;
14097             // opera does not respect the auto grow header center column
14098             // then, after it gets a width opera refuses to recalculate
14099             // without a second pass
14100             if(Roo.isOpera && !this.secondPass){
14101                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
14102                 this.secondPass = true;
14103                 this.update.defer(10, this, [date]);
14104             }
14105         }
14106         */
14107         
14108     },
14109     
14110     findCell : function(dt) {
14111         dt = dt.clearTime().getTime();
14112         var ret = false;
14113         this.cells.each(function(c){
14114             //Roo.log("check " +c.dateValue + '?=' + dt);
14115             if(c.dateValue == dt){
14116                 ret = c;
14117                 return false;
14118             }
14119             return true;
14120         });
14121         
14122         return ret;
14123     },
14124     
14125     findCells : function(ev) {
14126         var s = ev.start.clone().clearTime().getTime();
14127        // Roo.log(s);
14128         var e= ev.end.clone().clearTime().getTime();
14129        // Roo.log(e);
14130         var ret = [];
14131         this.cells.each(function(c){
14132              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
14133             
14134             if(c.dateValue > e){
14135                 return ;
14136             }
14137             if(c.dateValue < s){
14138                 return ;
14139             }
14140             ret.push(c);
14141         });
14142         
14143         return ret;    
14144     },
14145     
14146 //    findBestRow: function(cells)
14147 //    {
14148 //        var ret = 0;
14149 //        
14150 //        for (var i =0 ; i < cells.length;i++) {
14151 //            ret  = Math.max(cells[i].rows || 0,ret);
14152 //        }
14153 //        return ret;
14154 //        
14155 //    },
14156     
14157     
14158     addItem : function(ev)
14159     {
14160         // look for vertical location slot in
14161         var cells = this.findCells(ev);
14162         
14163 //        ev.row = this.findBestRow(cells);
14164         
14165         // work out the location.
14166         
14167         var crow = false;
14168         var rows = [];
14169         for(var i =0; i < cells.length; i++) {
14170             
14171             cells[i].row = cells[0].row;
14172             
14173             if(i == 0){
14174                 cells[i].row = cells[i].row + 1;
14175             }
14176             
14177             if (!crow) {
14178                 crow = {
14179                     start : cells[i],
14180                     end :  cells[i]
14181                 };
14182                 continue;
14183             }
14184             if (crow.start.getY() == cells[i].getY()) {
14185                 // on same row.
14186                 crow.end = cells[i];
14187                 continue;
14188             }
14189             // different row.
14190             rows.push(crow);
14191             crow = {
14192                 start: cells[i],
14193                 end : cells[i]
14194             };
14195             
14196         }
14197         
14198         rows.push(crow);
14199         ev.els = [];
14200         ev.rows = rows;
14201         ev.cells = cells;
14202         
14203         cells[0].events.push(ev);
14204         
14205         this.calevents.push(ev);
14206     },
14207     
14208     clearEvents: function() {
14209         
14210         if(!this.calevents){
14211             return;
14212         }
14213         
14214         Roo.each(this.cells.elements, function(c){
14215             c.row = 0;
14216             c.events = [];
14217             c.more = [];
14218         });
14219         
14220         Roo.each(this.calevents, function(e) {
14221             Roo.each(e.els, function(el) {
14222                 el.un('mouseenter' ,this.onEventEnter, this);
14223                 el.un('mouseleave' ,this.onEventLeave, this);
14224                 el.remove();
14225             },this);
14226         },this);
14227         
14228         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
14229             e.remove();
14230         });
14231         
14232     },
14233     
14234     renderEvents: function()
14235     {   
14236         var _this = this;
14237         
14238         this.cells.each(function(c) {
14239             
14240             if(c.row < 5){
14241                 return;
14242             }
14243             
14244             var ev = c.events;
14245             
14246             var r = 4;
14247             if(c.row != c.events.length){
14248                 r = 4 - (4 - (c.row - c.events.length));
14249             }
14250             
14251             c.events = ev.slice(0, r);
14252             c.more = ev.slice(r);
14253             
14254             if(c.more.length && c.more.length == 1){
14255                 c.events.push(c.more.pop());
14256             }
14257             
14258             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
14259             
14260         });
14261             
14262         this.cells.each(function(c) {
14263             
14264             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
14265             
14266             
14267             for (var e = 0; e < c.events.length; e++){
14268                 var ev = c.events[e];
14269                 var rows = ev.rows;
14270                 
14271                 for(var i = 0; i < rows.length; i++) {
14272                 
14273                     // how many rows should it span..
14274
14275                     var  cfg = {
14276                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
14277                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
14278
14279                         unselectable : "on",
14280                         cn : [
14281                             {
14282                                 cls: 'fc-event-inner',
14283                                 cn : [
14284     //                                {
14285     //                                  tag:'span',
14286     //                                  cls: 'fc-event-time',
14287     //                                  html : cells.length > 1 ? '' : ev.time
14288     //                                },
14289                                     {
14290                                       tag:'span',
14291                                       cls: 'fc-event-title',
14292                                       html : String.format('{0}', ev.title)
14293                                     }
14294
14295
14296                                 ]
14297                             },
14298                             {
14299                                 cls: 'ui-resizable-handle ui-resizable-e',
14300                                 html : '&nbsp;&nbsp;&nbsp'
14301                             }
14302
14303                         ]
14304                     };
14305
14306                     if (i == 0) {
14307                         cfg.cls += ' fc-event-start';
14308                     }
14309                     if ((i+1) == rows.length) {
14310                         cfg.cls += ' fc-event-end';
14311                     }
14312
14313                     var ctr = _this.el.select('.fc-event-container',true).first();
14314                     var cg = ctr.createChild(cfg);
14315
14316                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
14317                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
14318
14319                     var r = (c.more.length) ? 1 : 0;
14320                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
14321                     cg.setWidth(ebox.right - sbox.x -2);
14322
14323                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
14324                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
14325                     cg.on('click', _this.onEventClick, _this, ev);
14326
14327                     ev.els.push(cg);
14328                     
14329                 }
14330                 
14331             }
14332             
14333             
14334             if(c.more.length){
14335                 var  cfg = {
14336                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
14337                     style : 'position: absolute',
14338                     unselectable : "on",
14339                     cn : [
14340                         {
14341                             cls: 'fc-event-inner',
14342                             cn : [
14343                                 {
14344                                   tag:'span',
14345                                   cls: 'fc-event-title',
14346                                   html : 'More'
14347                                 }
14348
14349
14350                             ]
14351                         },
14352                         {
14353                             cls: 'ui-resizable-handle ui-resizable-e',
14354                             html : '&nbsp;&nbsp;&nbsp'
14355                         }
14356
14357                     ]
14358                 };
14359
14360                 var ctr = _this.el.select('.fc-event-container',true).first();
14361                 var cg = ctr.createChild(cfg);
14362
14363                 var sbox = c.select('.fc-day-content',true).first().getBox();
14364                 var ebox = c.select('.fc-day-content',true).first().getBox();
14365                 //Roo.log(cg);
14366                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
14367                 cg.setWidth(ebox.right - sbox.x -2);
14368
14369                 cg.on('click', _this.onMoreEventClick, _this, c.more);
14370                 
14371             }
14372             
14373         });
14374         
14375         
14376         
14377     },
14378     
14379     onEventEnter: function (e, el,event,d) {
14380         this.fireEvent('evententer', this, el, event);
14381     },
14382     
14383     onEventLeave: function (e, el,event,d) {
14384         this.fireEvent('eventleave', this, el, event);
14385     },
14386     
14387     onEventClick: function (e, el,event,d) {
14388         this.fireEvent('eventclick', this, el, event);
14389     },
14390     
14391     onMonthChange: function () {
14392         this.store.load();
14393     },
14394     
14395     onMoreEventClick: function(e, el, more)
14396     {
14397         var _this = this;
14398         
14399         this.calpopover.placement = 'right';
14400         this.calpopover.setTitle('More');
14401         
14402         this.calpopover.setContent('');
14403         
14404         var ctr = this.calpopover.el.select('.popover-content', true).first();
14405         
14406         Roo.each(more, function(m){
14407             var cfg = {
14408                 cls : 'fc-event-hori fc-event-draggable',
14409                 html : m.title
14410             }
14411             var cg = ctr.createChild(cfg);
14412             
14413             cg.on('click', _this.onEventClick, _this, m);
14414         });
14415         
14416         this.calpopover.show(el);
14417         
14418         
14419     },
14420     
14421     onLoad: function () 
14422     {   
14423         this.calevents = [];
14424         var cal = this;
14425         
14426         if(this.store.getCount() > 0){
14427             this.store.data.each(function(d){
14428                cal.addItem({
14429                     id : d.data.id,
14430                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
14431                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
14432                     time : d.data.start_time,
14433                     title : d.data.title,
14434                     description : d.data.description,
14435                     venue : d.data.venue
14436                 });
14437             });
14438         }
14439         
14440         this.renderEvents();
14441         
14442         if(this.calevents.length && this.loadMask){
14443             this.maskEl.hide();
14444         }
14445     },
14446     
14447     onBeforeLoad: function()
14448     {
14449         this.clearEvents();
14450         if(this.loadMask){
14451             this.maskEl.show();
14452         }
14453     }
14454 });
14455
14456  
14457  /*
14458  * - LGPL
14459  *
14460  * element
14461  * 
14462  */
14463
14464 /**
14465  * @class Roo.bootstrap.Popover
14466  * @extends Roo.bootstrap.Component
14467  * Bootstrap Popover class
14468  * @cfg {String} html contents of the popover   (or false to use children..)
14469  * @cfg {String} title of popover (or false to hide)
14470  * @cfg {String} placement how it is placed
14471  * @cfg {String} trigger click || hover (or false to trigger manually)
14472  * @cfg {String} over what (parent or false to trigger manually.)
14473  * @cfg {Number} delay - delay before showing
14474  
14475  * @constructor
14476  * Create a new Popover
14477  * @param {Object} config The config object
14478  */
14479
14480 Roo.bootstrap.Popover = function(config){
14481     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
14482 };
14483
14484 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
14485     
14486     title: 'Fill in a title',
14487     html: false,
14488     
14489     placement : 'right',
14490     trigger : 'hover', // hover
14491     
14492     delay : 0,
14493     
14494     over: 'parent',
14495     
14496     can_build_overlaid : false,
14497     
14498     getChildContainer : function()
14499     {
14500         return this.el.select('.popover-content',true).first();
14501     },
14502     
14503     getAutoCreate : function(){
14504          Roo.log('make popover?');
14505         var cfg = {
14506            cls : 'popover roo-dynamic',
14507            style: 'display:block',
14508            cn : [
14509                 {
14510                     cls : 'arrow'
14511                 },
14512                 {
14513                     cls : 'popover-inner',
14514                     cn : [
14515                         {
14516                             tag: 'h3',
14517                             cls: 'popover-title',
14518                             html : this.title
14519                         },
14520                         {
14521                             cls : 'popover-content',
14522                             html : this.html
14523                         }
14524                     ]
14525                     
14526                 }
14527            ]
14528         };
14529         
14530         return cfg;
14531     },
14532     setTitle: function(str)
14533     {
14534         this.el.select('.popover-title',true).first().dom.innerHTML = str;
14535     },
14536     setContent: function(str)
14537     {
14538         this.el.select('.popover-content',true).first().dom.innerHTML = str;
14539     },
14540     // as it get's added to the bottom of the page.
14541     onRender : function(ct, position)
14542     {
14543         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
14544         if(!this.el){
14545             var cfg = Roo.apply({},  this.getAutoCreate());
14546             cfg.id = Roo.id();
14547             
14548             if (this.cls) {
14549                 cfg.cls += ' ' + this.cls;
14550             }
14551             if (this.style) {
14552                 cfg.style = this.style;
14553             }
14554             Roo.log("adding to ")
14555             this.el = Roo.get(document.body).createChild(cfg, position);
14556             Roo.log(this.el);
14557         }
14558         this.initEvents();
14559     },
14560     
14561     initEvents : function()
14562     {
14563         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
14564         this.el.enableDisplayMode('block');
14565         this.el.hide();
14566         if (this.over === false) {
14567             return; 
14568         }
14569         if (this.triggers === false) {
14570             return;
14571         }
14572         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
14573         var triggers = this.trigger ? this.trigger.split(' ') : [];
14574         Roo.each(triggers, function(trigger) {
14575         
14576             if (trigger == 'click') {
14577                 on_el.on('click', this.toggle, this);
14578             } else if (trigger != 'manual') {
14579                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
14580                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
14581       
14582                 on_el.on(eventIn  ,this.enter, this);
14583                 on_el.on(eventOut, this.leave, this);
14584             }
14585         }, this);
14586         
14587     },
14588     
14589     
14590     // private
14591     timeout : null,
14592     hoverState : null,
14593     
14594     toggle : function () {
14595         this.hoverState == 'in' ? this.leave() : this.enter();
14596     },
14597     
14598     enter : function () {
14599        
14600     
14601         clearTimeout(this.timeout);
14602     
14603         this.hoverState = 'in';
14604     
14605         if (!this.delay || !this.delay.show) {
14606             this.show();
14607             return;
14608         }
14609         var _t = this;
14610         this.timeout = setTimeout(function () {
14611             if (_t.hoverState == 'in') {
14612                 _t.show();
14613             }
14614         }, this.delay.show)
14615     },
14616     leave : function() {
14617         clearTimeout(this.timeout);
14618     
14619         this.hoverState = 'out';
14620     
14621         if (!this.delay || !this.delay.hide) {
14622             this.hide();
14623             return;
14624         }
14625         var _t = this;
14626         this.timeout = setTimeout(function () {
14627             if (_t.hoverState == 'out') {
14628                 _t.hide();
14629             }
14630         }, this.delay.hide)
14631     },
14632     
14633     show : function (on_el)
14634     {
14635         if (!on_el) {
14636             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
14637         }
14638         // set content.
14639         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
14640         if (this.html !== false) {
14641             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
14642         }
14643         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
14644         if (!this.title.length) {
14645             this.el.select('.popover-title',true).hide();
14646         }
14647         
14648         var placement = typeof this.placement == 'function' ?
14649             this.placement.call(this, this.el, on_el) :
14650             this.placement;
14651             
14652         var autoToken = /\s?auto?\s?/i;
14653         var autoPlace = autoToken.test(placement);
14654         if (autoPlace) {
14655             placement = placement.replace(autoToken, '') || 'top';
14656         }
14657         
14658         //this.el.detach()
14659         //this.el.setXY([0,0]);
14660         this.el.show();
14661         this.el.dom.style.display='block';
14662         this.el.addClass(placement);
14663         
14664         //this.el.appendTo(on_el);
14665         
14666         var p = this.getPosition();
14667         var box = this.el.getBox();
14668         
14669         if (autoPlace) {
14670             // fixme..
14671         }
14672         var align = Roo.bootstrap.Popover.alignment[placement];
14673         this.el.alignTo(on_el, align[0],align[1]);
14674         //var arrow = this.el.select('.arrow',true).first();
14675         //arrow.set(align[2], 
14676         
14677         this.el.addClass('in');
14678         this.hoverState = null;
14679         
14680         if (this.el.hasClass('fade')) {
14681             // fade it?
14682         }
14683         
14684     },
14685     hide : function()
14686     {
14687         this.el.setXY([0,0]);
14688         this.el.removeClass('in');
14689         this.el.hide();
14690         
14691     }
14692     
14693 });
14694
14695 Roo.bootstrap.Popover.alignment = {
14696     'left' : ['r-l', [-10,0], 'right'],
14697     'right' : ['l-r', [10,0], 'left'],
14698     'bottom' : ['t-b', [0,10], 'top'],
14699     'top' : [ 'b-t', [0,-10], 'bottom']
14700 };
14701
14702  /*
14703  * - LGPL
14704  *
14705  * Progress
14706  * 
14707  */
14708
14709 /**
14710  * @class Roo.bootstrap.Progress
14711  * @extends Roo.bootstrap.Component
14712  * Bootstrap Progress class
14713  * @cfg {Boolean} striped striped of the progress bar
14714  * @cfg {Boolean} active animated of the progress bar
14715  * 
14716  * 
14717  * @constructor
14718  * Create a new Progress
14719  * @param {Object} config The config object
14720  */
14721
14722 Roo.bootstrap.Progress = function(config){
14723     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
14724 };
14725
14726 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
14727     
14728     striped : false,
14729     active: false,
14730     
14731     getAutoCreate : function(){
14732         var cfg = {
14733             tag: 'div',
14734             cls: 'progress'
14735         };
14736         
14737         
14738         if(this.striped){
14739             cfg.cls += ' progress-striped';
14740         }
14741       
14742         if(this.active){
14743             cfg.cls += ' active';
14744         }
14745         
14746         
14747         return cfg;
14748     }
14749    
14750 });
14751
14752  
14753
14754  /*
14755  * - LGPL
14756  *
14757  * ProgressBar
14758  * 
14759  */
14760
14761 /**
14762  * @class Roo.bootstrap.ProgressBar
14763  * @extends Roo.bootstrap.Component
14764  * Bootstrap ProgressBar class
14765  * @cfg {Number} aria_valuenow aria-value now
14766  * @cfg {Number} aria_valuemin aria-value min
14767  * @cfg {Number} aria_valuemax aria-value max
14768  * @cfg {String} label label for the progress bar
14769  * @cfg {String} panel (success | info | warning | danger )
14770  * @cfg {String} role role of the progress bar
14771  * @cfg {String} sr_only text
14772  * 
14773  * 
14774  * @constructor
14775  * Create a new ProgressBar
14776  * @param {Object} config The config object
14777  */
14778
14779 Roo.bootstrap.ProgressBar = function(config){
14780     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
14781 };
14782
14783 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
14784     
14785     aria_valuenow : 0,
14786     aria_valuemin : 0,
14787     aria_valuemax : 100,
14788     label : false,
14789     panel : false,
14790     role : false,
14791     sr_only: false,
14792     
14793     getAutoCreate : function()
14794     {
14795         
14796         var cfg = {
14797             tag: 'div',
14798             cls: 'progress-bar',
14799             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
14800         };
14801         
14802         if(this.sr_only){
14803             cfg.cn = {
14804                 tag: 'span',
14805                 cls: 'sr-only',
14806                 html: this.sr_only
14807             }
14808         }
14809         
14810         if(this.role){
14811             cfg.role = this.role;
14812         }
14813         
14814         if(this.aria_valuenow){
14815             cfg['aria-valuenow'] = this.aria_valuenow;
14816         }
14817         
14818         if(this.aria_valuemin){
14819             cfg['aria-valuemin'] = this.aria_valuemin;
14820         }
14821         
14822         if(this.aria_valuemax){
14823             cfg['aria-valuemax'] = this.aria_valuemax;
14824         }
14825         
14826         if(this.label && !this.sr_only){
14827             cfg.html = this.label;
14828         }
14829         
14830         if(this.panel){
14831             cfg.cls += ' progress-bar-' + this.panel;
14832         }
14833         
14834         return cfg;
14835     },
14836     
14837     update : function(aria_valuenow)
14838     {
14839         this.aria_valuenow = aria_valuenow;
14840         
14841         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
14842     }
14843    
14844 });
14845
14846  
14847
14848  /*
14849  * - LGPL
14850  *
14851  * column
14852  * 
14853  */
14854
14855 /**
14856  * @class Roo.bootstrap.TabGroup
14857  * @extends Roo.bootstrap.Column
14858  * Bootstrap Column class
14859  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
14860  * @cfg {Boolean} carousel true to make the group behave like a carousel
14861  * @cfg {Number} bullets show the panel pointer.. default 0
14862  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
14863  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
14864  * @cfg {Number} timer auto slide timer .. default 0 millisecond
14865  * 
14866  * @constructor
14867  * Create a new TabGroup
14868  * @param {Object} config The config object
14869  */
14870
14871 Roo.bootstrap.TabGroup = function(config){
14872     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
14873     if (!this.navId) {
14874         this.navId = Roo.id();
14875     }
14876     this.tabs = [];
14877     Roo.bootstrap.TabGroup.register(this);
14878     
14879 };
14880
14881 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
14882     
14883     carousel : false,
14884     transition : false,
14885     bullets : 0,
14886     timer : 0,
14887     autoslide : false,
14888     slideFn : false,
14889     slideOnTouch : false,
14890     
14891     getAutoCreate : function()
14892     {
14893         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
14894         
14895         cfg.cls += ' tab-content';
14896         
14897         Roo.log('get auto create...............');
14898         
14899         if (this.carousel) {
14900             cfg.cls += ' carousel slide';
14901             
14902             cfg.cn = [{
14903                cls : 'carousel-inner'
14904             }];
14905         
14906             if(this.bullets > 0 && !Roo.isTouch){
14907                 
14908                 var bullets = {
14909                     cls : 'carousel-bullets',
14910                     cn : []
14911                 };
14912                 
14913                 if(this.bullets_cls){
14914                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
14915                 }
14916                 
14917                 for (var i = 0; i < this.bullets; i++){
14918                     bullets.cn.push({
14919                         cls : 'bullet bullet-' + i
14920                     });
14921                 }
14922                 
14923                 bullets.cn.push({
14924                     cls : 'clear'
14925                 });
14926                 
14927                 cfg.cn[0].cn = bullets;
14928             }
14929         }
14930         
14931         return cfg;
14932     },
14933     
14934     initEvents:  function()
14935     {
14936         Roo.log('-------- init events on tab group ---------');
14937         
14938         if(this.bullets > 0 && !Roo.isTouch){
14939             this.initBullet();
14940         }
14941         
14942         Roo.log(this);
14943         
14944         if(Roo.isTouch && this.slideOnTouch){
14945             this.el.on("touchstart", this.onTouchStart, this);
14946         }
14947         
14948         if(this.autoslide){
14949             var _this = this;
14950             
14951             this.slideFn = window.setInterval(function() {
14952                 _this.showPanelNext();
14953             }, this.timer);
14954         }
14955         
14956     },
14957     
14958     onTouchStart : function(e, el, o)
14959     {
14960         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
14961             return;
14962         }
14963         
14964         this.showPanelNext();
14965     },
14966     
14967     getChildContainer : function()
14968     {
14969         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
14970     },
14971     
14972     /**
14973     * register a Navigation item
14974     * @param {Roo.bootstrap.NavItem} the navitem to add
14975     */
14976     register : function(item)
14977     {
14978         this.tabs.push( item);
14979         item.navId = this.navId; // not really needed..
14980     
14981     },
14982     
14983     getActivePanel : function()
14984     {
14985         var r = false;
14986         Roo.each(this.tabs, function(t) {
14987             if (t.active) {
14988                 r = t;
14989                 return false;
14990             }
14991             return null;
14992         });
14993         return r;
14994         
14995     },
14996     getPanelByName : function(n)
14997     {
14998         var r = false;
14999         Roo.each(this.tabs, function(t) {
15000             if (t.tabId == n) {
15001                 r = t;
15002                 return false;
15003             }
15004             return null;
15005         });
15006         return r;
15007     },
15008     indexOfPanel : function(p)
15009     {
15010         var r = false;
15011         Roo.each(this.tabs, function(t,i) {
15012             if (t.tabId == p.tabId) {
15013                 r = i;
15014                 return false;
15015             }
15016             return null;
15017         });
15018         return r;
15019     },
15020     /**
15021      * show a specific panel
15022      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
15023      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
15024      */
15025     showPanel : function (pan)
15026     {
15027         if(this.transition){
15028             Roo.log("waiting for the transitionend");
15029             return;
15030         }
15031         
15032         if (typeof(pan) == 'number') {
15033             pan = this.tabs[pan];
15034         }
15035         if (typeof(pan) == 'string') {
15036             pan = this.getPanelByName(pan);
15037         }
15038         if (pan.tabId == this.getActivePanel().tabId) {
15039             return true;
15040         }
15041         var cur = this.getActivePanel();
15042         
15043         if (false === cur.fireEvent('beforedeactivate')) {
15044             return false;
15045         }
15046         
15047         if(this.bullets > 0 && !Roo.isTouch){
15048             this.setActiveBullet(this.indexOfPanel(pan));
15049         }
15050         
15051         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
15052             
15053             this.transition = true;
15054             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
15055             var lr = dir == 'next' ? 'left' : 'right';
15056             pan.el.addClass(dir); // or prev
15057             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
15058             cur.el.addClass(lr); // or right
15059             pan.el.addClass(lr);
15060             
15061             var _this = this;
15062             cur.el.on('transitionend', function() {
15063                 Roo.log("trans end?");
15064                 
15065                 pan.el.removeClass([lr,dir]);
15066                 pan.setActive(true);
15067                 
15068                 cur.el.removeClass([lr]);
15069                 cur.setActive(false);
15070                 
15071                 _this.transition = false;
15072                 
15073             }, this, { single:  true } );
15074             
15075             return true;
15076         }
15077         
15078         cur.setActive(false);
15079         pan.setActive(true);
15080         
15081         return true;
15082         
15083     },
15084     showPanelNext : function()
15085     {
15086         var i = this.indexOfPanel(this.getActivePanel());
15087         
15088         if (i >= this.tabs.length - 1 && !this.autoslide) {
15089             return;
15090         }
15091         
15092         if (i >= this.tabs.length - 1 && this.autoslide) {
15093             i = -1;
15094         }
15095         
15096         this.showPanel(this.tabs[i+1]);
15097     },
15098     
15099     showPanelPrev : function()
15100     {
15101         var i = this.indexOfPanel(this.getActivePanel());
15102         
15103         if (i  < 1 && !this.autoslide) {
15104             return;
15105         }
15106         
15107         if (i < 1 && this.autoslide) {
15108             i = this.tabs.length;
15109         }
15110         
15111         this.showPanel(this.tabs[i-1]);
15112     },
15113     
15114     initBullet : function()
15115     {
15116         if(Roo.isTouch){
15117             return;
15118         }
15119         
15120         var _this = this;
15121         
15122         for (var i = 0; i < this.bullets; i++){
15123             var bullet = this.el.select('.bullet-' + i, true).first();
15124
15125             if(!bullet){
15126                 continue;
15127             }
15128
15129             bullet.on('click', (function(e, el, o, ii, t){
15130
15131                 e.preventDefault();
15132
15133                 _this.showPanel(ii);
15134
15135                 if(_this.autoslide && _this.slideFn){
15136                     clearInterval(_this.slideFn);
15137                     _this.slideFn = window.setInterval(function() {
15138                         _this.showPanelNext();
15139                     }, _this.timer);
15140                 }
15141
15142             }).createDelegate(this, [i, bullet], true));
15143         }
15144     },
15145     
15146     setActiveBullet : function(i)
15147     {
15148         if(Roo.isTouch){
15149             return;
15150         }
15151         
15152         Roo.each(this.el.select('.bullet', true).elements, function(el){
15153             el.removeClass('selected');
15154         });
15155
15156         var bullet = this.el.select('.bullet-' + i, true).first();
15157         
15158         if(!bullet){
15159             return;
15160         }
15161         
15162         bullet.addClass('selected');
15163     }
15164     
15165     
15166   
15167 });
15168
15169  
15170
15171  
15172  
15173 Roo.apply(Roo.bootstrap.TabGroup, {
15174     
15175     groups: {},
15176      /**
15177     * register a Navigation Group
15178     * @param {Roo.bootstrap.NavGroup} the navgroup to add
15179     */
15180     register : function(navgrp)
15181     {
15182         this.groups[navgrp.navId] = navgrp;
15183         
15184     },
15185     /**
15186     * fetch a Navigation Group based on the navigation ID
15187     * if one does not exist , it will get created.
15188     * @param {string} the navgroup to add
15189     * @returns {Roo.bootstrap.NavGroup} the navgroup 
15190     */
15191     get: function(navId) {
15192         if (typeof(this.groups[navId]) == 'undefined') {
15193             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
15194         }
15195         return this.groups[navId] ;
15196     }
15197     
15198     
15199     
15200 });
15201
15202  /*
15203  * - LGPL
15204  *
15205  * TabPanel
15206  * 
15207  */
15208
15209 /**
15210  * @class Roo.bootstrap.TabPanel
15211  * @extends Roo.bootstrap.Component
15212  * Bootstrap TabPanel class
15213  * @cfg {Boolean} active panel active
15214  * @cfg {String} html panel content
15215  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
15216  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
15217  * 
15218  * 
15219  * @constructor
15220  * Create a new TabPanel
15221  * @param {Object} config The config object
15222  */
15223
15224 Roo.bootstrap.TabPanel = function(config){
15225     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
15226     this.addEvents({
15227         /**
15228              * @event changed
15229              * Fires when the active status changes
15230              * @param {Roo.bootstrap.TabPanel} this
15231              * @param {Boolean} state the new state
15232             
15233          */
15234         'changed': true,
15235         /**
15236              * @event beforedeactivate
15237              * Fires before a tab is de-activated - can be used to do validation on a form.
15238              * @param {Roo.bootstrap.TabPanel} this
15239              * @return {Boolean} false if there is an error
15240             
15241          */
15242         'beforedeactivate': true
15243      });
15244     
15245     this.tabId = this.tabId || Roo.id();
15246   
15247 };
15248
15249 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
15250     
15251     active: false,
15252     html: false,
15253     tabId: false,
15254     navId : false,
15255     
15256     getAutoCreate : function(){
15257         var cfg = {
15258             tag: 'div',
15259             // item is needed for carousel - not sure if it has any effect otherwise
15260             cls: 'tab-pane item',
15261             html: this.html || ''
15262         };
15263         
15264         if(this.active){
15265             cfg.cls += ' active';
15266         }
15267         
15268         if(this.tabId){
15269             cfg.tabId = this.tabId;
15270         }
15271         
15272         
15273         return cfg;
15274     },
15275     
15276     initEvents:  function()
15277     {
15278         Roo.log('-------- init events on tab panel ---------');
15279         
15280         var p = this.parent();
15281         this.navId = this.navId || p.navId;
15282         
15283         if (typeof(this.navId) != 'undefined') {
15284             // not really needed.. but just in case.. parent should be a NavGroup.
15285             var tg = Roo.bootstrap.TabGroup.get(this.navId);
15286             Roo.log(['register', tg, this]);
15287             tg.register(this);
15288             
15289             var i = tg.tabs.length - 1;
15290             
15291             if(this.active && tg.bullets > 0 && i < tg.bullets){
15292                 tg.setActiveBullet(i);
15293             }
15294         }
15295         
15296     },
15297     
15298     
15299     onRender : function(ct, position)
15300     {
15301        // Roo.log("Call onRender: " + this.xtype);
15302         
15303         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
15304         
15305         
15306         
15307         
15308         
15309     },
15310     
15311     setActive: function(state)
15312     {
15313         Roo.log("panel - set active " + this.tabId + "=" + state);
15314         
15315         this.active = state;
15316         if (!state) {
15317             this.el.removeClass('active');
15318             
15319         } else  if (!this.el.hasClass('active')) {
15320             this.el.addClass('active');
15321         }
15322         
15323         this.fireEvent('changed', this, state);
15324     }
15325     
15326     
15327 });
15328  
15329
15330  
15331
15332  /*
15333  * - LGPL
15334  *
15335  * DateField
15336  * 
15337  */
15338
15339 /**
15340  * @class Roo.bootstrap.DateField
15341  * @extends Roo.bootstrap.Input
15342  * Bootstrap DateField class
15343  * @cfg {Number} weekStart default 0
15344  * @cfg {String} viewMode default empty, (months|years)
15345  * @cfg {String} minViewMode default empty, (months|years)
15346  * @cfg {Number} startDate default -Infinity
15347  * @cfg {Number} endDate default Infinity
15348  * @cfg {Boolean} todayHighlight default false
15349  * @cfg {Boolean} todayBtn default false
15350  * @cfg {Boolean} calendarWeeks default false
15351  * @cfg {Object} daysOfWeekDisabled default empty
15352  * @cfg {Boolean} singleMode default false (true | false)
15353  * 
15354  * @cfg {Boolean} keyboardNavigation default true
15355  * @cfg {String} language default en
15356  * 
15357  * @constructor
15358  * Create a new DateField
15359  * @param {Object} config The config object
15360  */
15361
15362 Roo.bootstrap.DateField = function(config){
15363     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
15364      this.addEvents({
15365             /**
15366              * @event show
15367              * Fires when this field show.
15368              * @param {Roo.bootstrap.DateField} this
15369              * @param {Mixed} date The date value
15370              */
15371             show : true,
15372             /**
15373              * @event show
15374              * Fires when this field hide.
15375              * @param {Roo.bootstrap.DateField} this
15376              * @param {Mixed} date The date value
15377              */
15378             hide : true,
15379             /**
15380              * @event select
15381              * Fires when select a date.
15382              * @param {Roo.bootstrap.DateField} this
15383              * @param {Mixed} date The date value
15384              */
15385             select : true
15386         });
15387 };
15388
15389 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
15390     
15391     /**
15392      * @cfg {String} format
15393      * The default date format string which can be overriden for localization support.  The format must be
15394      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
15395      */
15396     format : "m/d/y",
15397     /**
15398      * @cfg {String} altFormats
15399      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
15400      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
15401      */
15402     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
15403     
15404     weekStart : 0,
15405     
15406     viewMode : '',
15407     
15408     minViewMode : '',
15409     
15410     todayHighlight : false,
15411     
15412     todayBtn: false,
15413     
15414     language: 'en',
15415     
15416     keyboardNavigation: true,
15417     
15418     calendarWeeks: false,
15419     
15420     startDate: -Infinity,
15421     
15422     endDate: Infinity,
15423     
15424     daysOfWeekDisabled: [],
15425     
15426     _events: [],
15427     
15428     singleMode : false,
15429     
15430     UTCDate: function()
15431     {
15432         return new Date(Date.UTC.apply(Date, arguments));
15433     },
15434     
15435     UTCToday: function()
15436     {
15437         var today = new Date();
15438         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
15439     },
15440     
15441     getDate: function() {
15442             var d = this.getUTCDate();
15443             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
15444     },
15445     
15446     getUTCDate: function() {
15447             return this.date;
15448     },
15449     
15450     setDate: function(d) {
15451             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
15452     },
15453     
15454     setUTCDate: function(d) {
15455             this.date = d;
15456             this.setValue(this.formatDate(this.date));
15457     },
15458         
15459     onRender: function(ct, position)
15460     {
15461         
15462         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
15463         
15464         this.language = this.language || 'en';
15465         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
15466         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
15467         
15468         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
15469         this.format = this.format || 'm/d/y';
15470         this.isInline = false;
15471         this.isInput = true;
15472         this.component = this.el.select('.add-on', true).first() || false;
15473         this.component = (this.component && this.component.length === 0) ? false : this.component;
15474         this.hasInput = this.component && this.inputEL().length;
15475         
15476         if (typeof(this.minViewMode === 'string')) {
15477             switch (this.minViewMode) {
15478                 case 'months':
15479                     this.minViewMode = 1;
15480                     break;
15481                 case 'years':
15482                     this.minViewMode = 2;
15483                     break;
15484                 default:
15485                     this.minViewMode = 0;
15486                     break;
15487             }
15488         }
15489         
15490         if (typeof(this.viewMode === 'string')) {
15491             switch (this.viewMode) {
15492                 case 'months':
15493                     this.viewMode = 1;
15494                     break;
15495                 case 'years':
15496                     this.viewMode = 2;
15497                     break;
15498                 default:
15499                     this.viewMode = 0;
15500                     break;
15501             }
15502         }
15503                 
15504         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
15505         
15506 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
15507         
15508         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15509         
15510         this.picker().on('mousedown', this.onMousedown, this);
15511         this.picker().on('click', this.onClick, this);
15512         
15513         this.picker().addClass('datepicker-dropdown');
15514         
15515         this.startViewMode = this.viewMode;
15516         
15517         if(this.singleMode){
15518             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
15519                 v.setVisibilityMode(Roo.Element.DISPLAY)
15520                 v.hide();
15521             });
15522             
15523             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
15524                 v.setStyle('width', '189px');
15525             });
15526         }
15527         
15528         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
15529             if(!this.calendarWeeks){
15530                 v.remove();
15531                 return;
15532             }
15533             
15534             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
15535             v.attr('colspan', function(i, val){
15536                 return parseInt(val) + 1;
15537             });
15538         })
15539                         
15540         
15541         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
15542         
15543         this.setStartDate(this.startDate);
15544         this.setEndDate(this.endDate);
15545         
15546         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
15547         
15548         this.fillDow();
15549         this.fillMonths();
15550         this.update();
15551         this.showMode();
15552         
15553         if(this.isInline) {
15554             this.show();
15555         }
15556     },
15557     
15558     picker : function()
15559     {
15560         return this.pickerEl;
15561 //        return this.el.select('.datepicker', true).first();
15562     },
15563     
15564     fillDow: function()
15565     {
15566         var dowCnt = this.weekStart;
15567         
15568         var dow = {
15569             tag: 'tr',
15570             cn: [
15571                 
15572             ]
15573         };
15574         
15575         if(this.calendarWeeks){
15576             dow.cn.push({
15577                 tag: 'th',
15578                 cls: 'cw',
15579                 html: '&nbsp;'
15580             })
15581         }
15582         
15583         while (dowCnt < this.weekStart + 7) {
15584             dow.cn.push({
15585                 tag: 'th',
15586                 cls: 'dow',
15587                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
15588             });
15589         }
15590         
15591         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
15592     },
15593     
15594     fillMonths: function()
15595     {    
15596         var i = 0;
15597         var months = this.picker().select('>.datepicker-months td', true).first();
15598         
15599         months.dom.innerHTML = '';
15600         
15601         while (i < 12) {
15602             var month = {
15603                 tag: 'span',
15604                 cls: 'month',
15605                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
15606             }
15607             
15608             months.createChild(month);
15609         }
15610         
15611     },
15612     
15613     update: function()
15614     {
15615         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;
15616         
15617         if (this.date < this.startDate) {
15618             this.viewDate = new Date(this.startDate);
15619         } else if (this.date > this.endDate) {
15620             this.viewDate = new Date(this.endDate);
15621         } else {
15622             this.viewDate = new Date(this.date);
15623         }
15624         
15625         this.fill();
15626     },
15627     
15628     fill: function() 
15629     {
15630         var d = new Date(this.viewDate),
15631                 year = d.getUTCFullYear(),
15632                 month = d.getUTCMonth(),
15633                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
15634                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
15635                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
15636                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
15637                 currentDate = this.date && this.date.valueOf(),
15638                 today = this.UTCToday();
15639         
15640         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
15641         
15642 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
15643         
15644 //        this.picker.select('>tfoot th.today').
15645 //                                              .text(dates[this.language].today)
15646 //                                              .toggle(this.todayBtn !== false);
15647     
15648         this.updateNavArrows();
15649         this.fillMonths();
15650                                                 
15651         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
15652         
15653         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
15654          
15655         prevMonth.setUTCDate(day);
15656         
15657         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
15658         
15659         var nextMonth = new Date(prevMonth);
15660         
15661         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
15662         
15663         nextMonth = nextMonth.valueOf();
15664         
15665         var fillMonths = false;
15666         
15667         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
15668         
15669         while(prevMonth.valueOf() < nextMonth) {
15670             var clsName = '';
15671             
15672             if (prevMonth.getUTCDay() === this.weekStart) {
15673                 if(fillMonths){
15674                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
15675                 }
15676                     
15677                 fillMonths = {
15678                     tag: 'tr',
15679                     cn: []
15680                 };
15681                 
15682                 if(this.calendarWeeks){
15683                     // ISO 8601: First week contains first thursday.
15684                     // ISO also states week starts on Monday, but we can be more abstract here.
15685                     var
15686                     // Start of current week: based on weekstart/current date
15687                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
15688                     // Thursday of this week
15689                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
15690                     // First Thursday of year, year from thursday
15691                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
15692                     // Calendar week: ms between thursdays, div ms per day, div 7 days
15693                     calWeek =  (th - yth) / 864e5 / 7 + 1;
15694                     
15695                     fillMonths.cn.push({
15696                         tag: 'td',
15697                         cls: 'cw',
15698                         html: calWeek
15699                     });
15700                 }
15701             }
15702             
15703             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
15704                 clsName += ' old';
15705             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
15706                 clsName += ' new';
15707             }
15708             if (this.todayHighlight &&
15709                 prevMonth.getUTCFullYear() == today.getFullYear() &&
15710                 prevMonth.getUTCMonth() == today.getMonth() &&
15711                 prevMonth.getUTCDate() == today.getDate()) {
15712                 clsName += ' today';
15713             }
15714             
15715             if (currentDate && prevMonth.valueOf() === currentDate) {
15716                 clsName += ' active';
15717             }
15718             
15719             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
15720                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
15721                     clsName += ' disabled';
15722             }
15723             
15724             fillMonths.cn.push({
15725                 tag: 'td',
15726                 cls: 'day ' + clsName,
15727                 html: prevMonth.getDate()
15728             })
15729             
15730             prevMonth.setDate(prevMonth.getDate()+1);
15731         }
15732           
15733         var currentYear = this.date && this.date.getUTCFullYear();
15734         var currentMonth = this.date && this.date.getUTCMonth();
15735         
15736         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
15737         
15738         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
15739             v.removeClass('active');
15740             
15741             if(currentYear === year && k === currentMonth){
15742                 v.addClass('active');
15743             }
15744             
15745             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
15746                 v.addClass('disabled');
15747             }
15748             
15749         });
15750         
15751         
15752         year = parseInt(year/10, 10) * 10;
15753         
15754         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
15755         
15756         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
15757         
15758         year -= 1;
15759         for (var i = -1; i < 11; i++) {
15760             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
15761                 tag: 'span',
15762                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
15763                 html: year
15764             })
15765             
15766             year += 1;
15767         }
15768     },
15769     
15770     showMode: function(dir) 
15771     {
15772         if (dir) {
15773             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
15774         }
15775         
15776         Roo.each(this.picker().select('>div',true).elements, function(v){
15777             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15778             v.hide();
15779         });
15780         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
15781     },
15782     
15783     place: function()
15784     {
15785         if(this.isInline) return;
15786         
15787         this.picker().removeClass(['bottom', 'top']);
15788         
15789         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
15790             /*
15791              * place to the top of element!
15792              *
15793              */
15794             
15795             this.picker().addClass('top');
15796             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
15797             
15798             return;
15799         }
15800         
15801         this.picker().addClass('bottom');
15802         
15803         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
15804     },
15805     
15806     parseDate : function(value)
15807     {
15808         if(!value || value instanceof Date){
15809             return value;
15810         }
15811         var v = Date.parseDate(value, this.format);
15812         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
15813             v = Date.parseDate(value, 'Y-m-d');
15814         }
15815         if(!v && this.altFormats){
15816             if(!this.altFormatsArray){
15817                 this.altFormatsArray = this.altFormats.split("|");
15818             }
15819             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
15820                 v = Date.parseDate(value, this.altFormatsArray[i]);
15821             }
15822         }
15823         return v;
15824     },
15825     
15826     formatDate : function(date, fmt)
15827     {   
15828         return (!date || !(date instanceof Date)) ?
15829         date : date.dateFormat(fmt || this.format);
15830     },
15831     
15832     onFocus : function()
15833     {
15834         Roo.bootstrap.DateField.superclass.onFocus.call(this);
15835         this.show();
15836     },
15837     
15838     onBlur : function()
15839     {
15840         Roo.bootstrap.DateField.superclass.onBlur.call(this);
15841         
15842         var d = this.inputEl().getValue();
15843         
15844         this.setValue(d);
15845                 
15846         this.hide();
15847     },
15848     
15849     show : function()
15850     {
15851         this.picker().show();
15852         this.update();
15853         this.place();
15854         
15855         this.fireEvent('show', this, this.date);
15856     },
15857     
15858     hide : function()
15859     {
15860         if(this.isInline) return;
15861         this.picker().hide();
15862         this.viewMode = this.startViewMode;
15863         this.showMode();
15864         
15865         this.fireEvent('hide', this, this.date);
15866         
15867     },
15868     
15869     onMousedown: function(e)
15870     {
15871         e.stopPropagation();
15872         e.preventDefault();
15873     },
15874     
15875     keyup: function(e)
15876     {
15877         Roo.bootstrap.DateField.superclass.keyup.call(this);
15878         this.update();
15879     },
15880
15881     setValue: function(v)
15882     {
15883         
15884         // v can be a string or a date..
15885         
15886         
15887         var d = new Date(this.parseDate(v) ).clearTime();
15888         
15889         if(isNaN(d.getTime())){
15890             this.date = this.viewDate = '';
15891             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
15892             return;
15893         }
15894         
15895         v = this.formatDate(d);
15896         
15897         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
15898         
15899         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
15900      
15901         this.update();
15902
15903         this.fireEvent('select', this, this.date);
15904         
15905     },
15906     
15907     getValue: function()
15908     {
15909         return this.formatDate(this.date);
15910     },
15911     
15912     fireKey: function(e)
15913     {
15914         if (!this.picker().isVisible()){
15915             if (e.keyCode == 27) // allow escape to hide and re-show picker
15916                 this.show();
15917             return;
15918         }
15919         
15920         var dateChanged = false,
15921         dir, day, month,
15922         newDate, newViewDate;
15923         
15924         switch(e.keyCode){
15925             case 27: // escape
15926                 this.hide();
15927                 e.preventDefault();
15928                 break;
15929             case 37: // left
15930             case 39: // right
15931                 if (!this.keyboardNavigation) break;
15932                 dir = e.keyCode == 37 ? -1 : 1;
15933                 
15934                 if (e.ctrlKey){
15935                     newDate = this.moveYear(this.date, dir);
15936                     newViewDate = this.moveYear(this.viewDate, dir);
15937                 } else if (e.shiftKey){
15938                     newDate = this.moveMonth(this.date, dir);
15939                     newViewDate = this.moveMonth(this.viewDate, dir);
15940                 } else {
15941                     newDate = new Date(this.date);
15942                     newDate.setUTCDate(this.date.getUTCDate() + dir);
15943                     newViewDate = new Date(this.viewDate);
15944                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
15945                 }
15946                 if (this.dateWithinRange(newDate)){
15947                     this.date = newDate;
15948                     this.viewDate = newViewDate;
15949                     this.setValue(this.formatDate(this.date));
15950 //                    this.update();
15951                     e.preventDefault();
15952                     dateChanged = true;
15953                 }
15954                 break;
15955             case 38: // up
15956             case 40: // down
15957                 if (!this.keyboardNavigation) break;
15958                 dir = e.keyCode == 38 ? -1 : 1;
15959                 if (e.ctrlKey){
15960                     newDate = this.moveYear(this.date, dir);
15961                     newViewDate = this.moveYear(this.viewDate, dir);
15962                 } else if (e.shiftKey){
15963                     newDate = this.moveMonth(this.date, dir);
15964                     newViewDate = this.moveMonth(this.viewDate, dir);
15965                 } else {
15966                     newDate = new Date(this.date);
15967                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
15968                     newViewDate = new Date(this.viewDate);
15969                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
15970                 }
15971                 if (this.dateWithinRange(newDate)){
15972                     this.date = newDate;
15973                     this.viewDate = newViewDate;
15974                     this.setValue(this.formatDate(this.date));
15975 //                    this.update();
15976                     e.preventDefault();
15977                     dateChanged = true;
15978                 }
15979                 break;
15980             case 13: // enter
15981                 this.setValue(this.formatDate(this.date));
15982                 this.hide();
15983                 e.preventDefault();
15984                 break;
15985             case 9: // tab
15986                 this.setValue(this.formatDate(this.date));
15987                 this.hide();
15988                 break;
15989             case 16: // shift
15990             case 17: // ctrl
15991             case 18: // alt
15992                 break;
15993             default :
15994                 this.hide();
15995                 
15996         }
15997     },
15998     
15999     
16000     onClick: function(e) 
16001     {
16002         e.stopPropagation();
16003         e.preventDefault();
16004         
16005         var target = e.getTarget();
16006         
16007         if(target.nodeName.toLowerCase() === 'i'){
16008             target = Roo.get(target).dom.parentNode;
16009         }
16010         
16011         var nodeName = target.nodeName;
16012         var className = target.className;
16013         var html = target.innerHTML;
16014         //Roo.log(nodeName);
16015         
16016         switch(nodeName.toLowerCase()) {
16017             case 'th':
16018                 switch(className) {
16019                     case 'switch':
16020                         this.showMode(1);
16021                         break;
16022                     case 'prev':
16023                     case 'next':
16024                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
16025                         switch(this.viewMode){
16026                                 case 0:
16027                                         this.viewDate = this.moveMonth(this.viewDate, dir);
16028                                         break;
16029                                 case 1:
16030                                 case 2:
16031                                         this.viewDate = this.moveYear(this.viewDate, dir);
16032                                         break;
16033                         }
16034                         this.fill();
16035                         break;
16036                     case 'today':
16037                         var date = new Date();
16038                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
16039 //                        this.fill()
16040                         this.setValue(this.formatDate(this.date));
16041                         
16042                         this.hide();
16043                         break;
16044                 }
16045                 break;
16046             case 'span':
16047                 if (className.indexOf('disabled') < 0) {
16048                     this.viewDate.setUTCDate(1);
16049                     if (className.indexOf('month') > -1) {
16050                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
16051                     } else {
16052                         var year = parseInt(html, 10) || 0;
16053                         this.viewDate.setUTCFullYear(year);
16054                         
16055                     }
16056                     
16057                     if(this.singleMode){
16058                         this.setValue(this.formatDate(this.viewDate));
16059                         this.hide();
16060                         return;
16061                     }
16062                     
16063                     this.showMode(-1);
16064                     this.fill();
16065                 }
16066                 break;
16067                 
16068             case 'td':
16069                 //Roo.log(className);
16070                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
16071                     var day = parseInt(html, 10) || 1;
16072                     var year = this.viewDate.getUTCFullYear(),
16073                         month = this.viewDate.getUTCMonth();
16074
16075                     if (className.indexOf('old') > -1) {
16076                         if(month === 0 ){
16077                             month = 11;
16078                             year -= 1;
16079                         }else{
16080                             month -= 1;
16081                         }
16082                     } else if (className.indexOf('new') > -1) {
16083                         if (month == 11) {
16084                             month = 0;
16085                             year += 1;
16086                         } else {
16087                             month += 1;
16088                         }
16089                     }
16090                     //Roo.log([year,month,day]);
16091                     this.date = this.UTCDate(year, month, day,0,0,0,0);
16092                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
16093 //                    this.fill();
16094                     //Roo.log(this.formatDate(this.date));
16095                     this.setValue(this.formatDate(this.date));
16096                     this.hide();
16097                 }
16098                 break;
16099         }
16100     },
16101     
16102     setStartDate: function(startDate)
16103     {
16104         this.startDate = startDate || -Infinity;
16105         if (this.startDate !== -Infinity) {
16106             this.startDate = this.parseDate(this.startDate);
16107         }
16108         this.update();
16109         this.updateNavArrows();
16110     },
16111
16112     setEndDate: function(endDate)
16113     {
16114         this.endDate = endDate || Infinity;
16115         if (this.endDate !== Infinity) {
16116             this.endDate = this.parseDate(this.endDate);
16117         }
16118         this.update();
16119         this.updateNavArrows();
16120     },
16121     
16122     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
16123     {
16124         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
16125         if (typeof(this.daysOfWeekDisabled) !== 'object') {
16126             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
16127         }
16128         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
16129             return parseInt(d, 10);
16130         });
16131         this.update();
16132         this.updateNavArrows();
16133     },
16134     
16135     updateNavArrows: function() 
16136     {
16137         if(this.singleMode){
16138             return;
16139         }
16140         
16141         var d = new Date(this.viewDate),
16142         year = d.getUTCFullYear(),
16143         month = d.getUTCMonth();
16144         
16145         Roo.each(this.picker().select('.prev', true).elements, function(v){
16146             v.show();
16147             switch (this.viewMode) {
16148                 case 0:
16149
16150                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
16151                         v.hide();
16152                     }
16153                     break;
16154                 case 1:
16155                 case 2:
16156                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
16157                         v.hide();
16158                     }
16159                     break;
16160             }
16161         });
16162         
16163         Roo.each(this.picker().select('.next', true).elements, function(v){
16164             v.show();
16165             switch (this.viewMode) {
16166                 case 0:
16167
16168                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
16169                         v.hide();
16170                     }
16171                     break;
16172                 case 1:
16173                 case 2:
16174                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
16175                         v.hide();
16176                     }
16177                     break;
16178             }
16179         })
16180     },
16181     
16182     moveMonth: function(date, dir)
16183     {
16184         if (!dir) return date;
16185         var new_date = new Date(date.valueOf()),
16186         day = new_date.getUTCDate(),
16187         month = new_date.getUTCMonth(),
16188         mag = Math.abs(dir),
16189         new_month, test;
16190         dir = dir > 0 ? 1 : -1;
16191         if (mag == 1){
16192             test = dir == -1
16193             // If going back one month, make sure month is not current month
16194             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
16195             ? function(){
16196                 return new_date.getUTCMonth() == month;
16197             }
16198             // If going forward one month, make sure month is as expected
16199             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
16200             : function(){
16201                 return new_date.getUTCMonth() != new_month;
16202             };
16203             new_month = month + dir;
16204             new_date.setUTCMonth(new_month);
16205             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
16206             if (new_month < 0 || new_month > 11)
16207                 new_month = (new_month + 12) % 12;
16208         } else {
16209             // For magnitudes >1, move one month at a time...
16210             for (var i=0; i<mag; i++)
16211                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
16212                 new_date = this.moveMonth(new_date, dir);
16213             // ...then reset the day, keeping it in the new month
16214             new_month = new_date.getUTCMonth();
16215             new_date.setUTCDate(day);
16216             test = function(){
16217                 return new_month != new_date.getUTCMonth();
16218             };
16219         }
16220         // Common date-resetting loop -- if date is beyond end of month, make it
16221         // end of month
16222         while (test()){
16223             new_date.setUTCDate(--day);
16224             new_date.setUTCMonth(new_month);
16225         }
16226         return new_date;
16227     },
16228
16229     moveYear: function(date, dir)
16230     {
16231         return this.moveMonth(date, dir*12);
16232     },
16233
16234     dateWithinRange: function(date)
16235     {
16236         return date >= this.startDate && date <= this.endDate;
16237     },
16238
16239     
16240     remove: function() 
16241     {
16242         this.picker().remove();
16243     }
16244    
16245 });
16246
16247 Roo.apply(Roo.bootstrap.DateField,  {
16248     
16249     head : {
16250         tag: 'thead',
16251         cn: [
16252         {
16253             tag: 'tr',
16254             cn: [
16255             {
16256                 tag: 'th',
16257                 cls: 'prev',
16258                 html: '<i class="fa fa-arrow-left"/>'
16259             },
16260             {
16261                 tag: 'th',
16262                 cls: 'switch',
16263                 colspan: '5'
16264             },
16265             {
16266                 tag: 'th',
16267                 cls: 'next',
16268                 html: '<i class="fa fa-arrow-right"/>'
16269             }
16270
16271             ]
16272         }
16273         ]
16274     },
16275     
16276     content : {
16277         tag: 'tbody',
16278         cn: [
16279         {
16280             tag: 'tr',
16281             cn: [
16282             {
16283                 tag: 'td',
16284                 colspan: '7'
16285             }
16286             ]
16287         }
16288         ]
16289     },
16290     
16291     footer : {
16292         tag: 'tfoot',
16293         cn: [
16294         {
16295             tag: 'tr',
16296             cn: [
16297             {
16298                 tag: 'th',
16299                 colspan: '7',
16300                 cls: 'today'
16301             }
16302                     
16303             ]
16304         }
16305         ]
16306     },
16307     
16308     dates:{
16309         en: {
16310             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
16311             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
16312             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
16313             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
16314             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
16315             today: "Today"
16316         }
16317     },
16318     
16319     modes: [
16320     {
16321         clsName: 'days',
16322         navFnc: 'Month',
16323         navStep: 1
16324     },
16325     {
16326         clsName: 'months',
16327         navFnc: 'FullYear',
16328         navStep: 1
16329     },
16330     {
16331         clsName: 'years',
16332         navFnc: 'FullYear',
16333         navStep: 10
16334     }]
16335 });
16336
16337 Roo.apply(Roo.bootstrap.DateField,  {
16338   
16339     template : {
16340         tag: 'div',
16341         cls: 'datepicker dropdown-menu roo-dynamic',
16342         cn: [
16343         {
16344             tag: 'div',
16345             cls: 'datepicker-days',
16346             cn: [
16347             {
16348                 tag: 'table',
16349                 cls: 'table-condensed',
16350                 cn:[
16351                 Roo.bootstrap.DateField.head,
16352                 {
16353                     tag: 'tbody'
16354                 },
16355                 Roo.bootstrap.DateField.footer
16356                 ]
16357             }
16358             ]
16359         },
16360         {
16361             tag: 'div',
16362             cls: 'datepicker-months',
16363             cn: [
16364             {
16365                 tag: 'table',
16366                 cls: 'table-condensed',
16367                 cn:[
16368                 Roo.bootstrap.DateField.head,
16369                 Roo.bootstrap.DateField.content,
16370                 Roo.bootstrap.DateField.footer
16371                 ]
16372             }
16373             ]
16374         },
16375         {
16376             tag: 'div',
16377             cls: 'datepicker-years',
16378             cn: [
16379             {
16380                 tag: 'table',
16381                 cls: 'table-condensed',
16382                 cn:[
16383                 Roo.bootstrap.DateField.head,
16384                 Roo.bootstrap.DateField.content,
16385                 Roo.bootstrap.DateField.footer
16386                 ]
16387             }
16388             ]
16389         }
16390         ]
16391     }
16392 });
16393
16394  
16395
16396  /*
16397  * - LGPL
16398  *
16399  * TimeField
16400  * 
16401  */
16402
16403 /**
16404  * @class Roo.bootstrap.TimeField
16405  * @extends Roo.bootstrap.Input
16406  * Bootstrap DateField class
16407  * 
16408  * 
16409  * @constructor
16410  * Create a new TimeField
16411  * @param {Object} config The config object
16412  */
16413
16414 Roo.bootstrap.TimeField = function(config){
16415     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
16416     this.addEvents({
16417             /**
16418              * @event show
16419              * Fires when this field show.
16420              * @param {Roo.bootstrap.DateField} thisthis
16421              * @param {Mixed} date The date value
16422              */
16423             show : true,
16424             /**
16425              * @event show
16426              * Fires when this field hide.
16427              * @param {Roo.bootstrap.DateField} this
16428              * @param {Mixed} date The date value
16429              */
16430             hide : true,
16431             /**
16432              * @event select
16433              * Fires when select a date.
16434              * @param {Roo.bootstrap.DateField} this
16435              * @param {Mixed} date The date value
16436              */
16437             select : true
16438         });
16439 };
16440
16441 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
16442     
16443     /**
16444      * @cfg {String} format
16445      * The default time format string which can be overriden for localization support.  The format must be
16446      * valid according to {@link Date#parseDate} (defaults to 'H:i').
16447      */
16448     format : "H:i",
16449        
16450     onRender: function(ct, position)
16451     {
16452         
16453         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
16454                 
16455         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
16456         
16457         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16458         
16459         this.pop = this.picker().select('>.datepicker-time',true).first();
16460         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16461         
16462         this.picker().on('mousedown', this.onMousedown, this);
16463         this.picker().on('click', this.onClick, this);
16464         
16465         this.picker().addClass('datepicker-dropdown');
16466     
16467         this.fillTime();
16468         this.update();
16469             
16470         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
16471         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
16472         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
16473         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
16474         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
16475         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
16476
16477     },
16478     
16479     fireKey: function(e){
16480         if (!this.picker().isVisible()){
16481             if (e.keyCode == 27) { // allow escape to hide and re-show picker
16482                 this.show();
16483             }
16484             return;
16485         }
16486
16487         e.preventDefault();
16488         
16489         switch(e.keyCode){
16490             case 27: // escape
16491                 this.hide();
16492                 break;
16493             case 37: // left
16494             case 39: // right
16495                 this.onTogglePeriod();
16496                 break;
16497             case 38: // up
16498                 this.onIncrementMinutes();
16499                 break;
16500             case 40: // down
16501                 this.onDecrementMinutes();
16502                 break;
16503             case 13: // enter
16504             case 9: // tab
16505                 this.setTime();
16506                 break;
16507         }
16508     },
16509     
16510     onClick: function(e) {
16511         e.stopPropagation();
16512         e.preventDefault();
16513     },
16514     
16515     picker : function()
16516     {
16517         return this.el.select('.datepicker', true).first();
16518     },
16519     
16520     fillTime: function()
16521     {    
16522         var time = this.pop.select('tbody', true).first();
16523         
16524         time.dom.innerHTML = '';
16525         
16526         time.createChild({
16527             tag: 'tr',
16528             cn: [
16529                 {
16530                     tag: 'td',
16531                     cn: [
16532                         {
16533                             tag: 'a',
16534                             href: '#',
16535                             cls: 'btn',
16536                             cn: [
16537                                 {
16538                                     tag: 'span',
16539                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
16540                                 }
16541                             ]
16542                         } 
16543                     ]
16544                 },
16545                 {
16546                     tag: 'td',
16547                     cls: 'separator'
16548                 },
16549                 {
16550                     tag: 'td',
16551                     cn: [
16552                         {
16553                             tag: 'a',
16554                             href: '#',
16555                             cls: 'btn',
16556                             cn: [
16557                                 {
16558                                     tag: 'span',
16559                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
16560                                 }
16561                             ]
16562                         }
16563                     ]
16564                 },
16565                 {
16566                     tag: 'td',
16567                     cls: 'separator'
16568                 }
16569             ]
16570         });
16571         
16572         time.createChild({
16573             tag: 'tr',
16574             cn: [
16575                 {
16576                     tag: 'td',
16577                     cn: [
16578                         {
16579                             tag: 'span',
16580                             cls: 'timepicker-hour',
16581                             html: '00'
16582                         }  
16583                     ]
16584                 },
16585                 {
16586                     tag: 'td',
16587                     cls: 'separator',
16588                     html: ':'
16589                 },
16590                 {
16591                     tag: 'td',
16592                     cn: [
16593                         {
16594                             tag: 'span',
16595                             cls: 'timepicker-minute',
16596                             html: '00'
16597                         }  
16598                     ]
16599                 },
16600                 {
16601                     tag: 'td',
16602                     cls: 'separator'
16603                 },
16604                 {
16605                     tag: 'td',
16606                     cn: [
16607                         {
16608                             tag: 'button',
16609                             type: 'button',
16610                             cls: 'btn btn-primary period',
16611                             html: 'AM'
16612                             
16613                         }
16614                     ]
16615                 }
16616             ]
16617         });
16618         
16619         time.createChild({
16620             tag: 'tr',
16621             cn: [
16622                 {
16623                     tag: 'td',
16624                     cn: [
16625                         {
16626                             tag: 'a',
16627                             href: '#',
16628                             cls: 'btn',
16629                             cn: [
16630                                 {
16631                                     tag: 'span',
16632                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
16633                                 }
16634                             ]
16635                         }
16636                     ]
16637                 },
16638                 {
16639                     tag: 'td',
16640                     cls: 'separator'
16641                 },
16642                 {
16643                     tag: 'td',
16644                     cn: [
16645                         {
16646                             tag: 'a',
16647                             href: '#',
16648                             cls: 'btn',
16649                             cn: [
16650                                 {
16651                                     tag: 'span',
16652                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
16653                                 }
16654                             ]
16655                         }
16656                     ]
16657                 },
16658                 {
16659                     tag: 'td',
16660                     cls: 'separator'
16661                 }
16662             ]
16663         });
16664         
16665     },
16666     
16667     update: function()
16668     {
16669         
16670         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
16671         
16672         this.fill();
16673     },
16674     
16675     fill: function() 
16676     {
16677         var hours = this.time.getHours();
16678         var minutes = this.time.getMinutes();
16679         var period = 'AM';
16680         
16681         if(hours > 11){
16682             period = 'PM';
16683         }
16684         
16685         if(hours == 0){
16686             hours = 12;
16687         }
16688         
16689         
16690         if(hours > 12){
16691             hours = hours - 12;
16692         }
16693         
16694         if(hours < 10){
16695             hours = '0' + hours;
16696         }
16697         
16698         if(minutes < 10){
16699             minutes = '0' + minutes;
16700         }
16701         
16702         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
16703         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
16704         this.pop.select('button', true).first().dom.innerHTML = period;
16705         
16706     },
16707     
16708     place: function()
16709     {   
16710         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
16711         
16712         var cls = ['bottom'];
16713         
16714         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
16715             cls.pop();
16716             cls.push('top');
16717         }
16718         
16719         cls.push('right');
16720         
16721         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
16722             cls.pop();
16723             cls.push('left');
16724         }
16725         
16726         this.picker().addClass(cls.join('-'));
16727         
16728         var _this = this;
16729         
16730         Roo.each(cls, function(c){
16731             if(c == 'bottom'){
16732                 _this.picker().setTop(_this.inputEl().getHeight());
16733                 return;
16734             }
16735             if(c == 'top'){
16736                 _this.picker().setTop(0 - _this.picker().getHeight());
16737                 return;
16738             }
16739             
16740             if(c == 'left'){
16741                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
16742                 return;
16743             }
16744             if(c == 'right'){
16745                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
16746                 return;
16747             }
16748         });
16749         
16750     },
16751   
16752     onFocus : function()
16753     {
16754         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
16755         this.show();
16756     },
16757     
16758     onBlur : function()
16759     {
16760         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
16761         this.hide();
16762     },
16763     
16764     show : function()
16765     {
16766         this.picker().show();
16767         this.pop.show();
16768         this.update();
16769         this.place();
16770         
16771         this.fireEvent('show', this, this.date);
16772     },
16773     
16774     hide : function()
16775     {
16776         this.picker().hide();
16777         this.pop.hide();
16778         
16779         this.fireEvent('hide', this, this.date);
16780     },
16781     
16782     setTime : function()
16783     {
16784         this.hide();
16785         this.setValue(this.time.format(this.format));
16786         
16787         this.fireEvent('select', this, this.date);
16788         
16789         
16790     },
16791     
16792     onMousedown: function(e){
16793         e.stopPropagation();
16794         e.preventDefault();
16795     },
16796     
16797     onIncrementHours: function()
16798     {
16799         Roo.log('onIncrementHours');
16800         this.time = this.time.add(Date.HOUR, 1);
16801         this.update();
16802         
16803     },
16804     
16805     onDecrementHours: function()
16806     {
16807         Roo.log('onDecrementHours');
16808         this.time = this.time.add(Date.HOUR, -1);
16809         this.update();
16810     },
16811     
16812     onIncrementMinutes: function()
16813     {
16814         Roo.log('onIncrementMinutes');
16815         this.time = this.time.add(Date.MINUTE, 1);
16816         this.update();
16817     },
16818     
16819     onDecrementMinutes: function()
16820     {
16821         Roo.log('onDecrementMinutes');
16822         this.time = this.time.add(Date.MINUTE, -1);
16823         this.update();
16824     },
16825     
16826     onTogglePeriod: function()
16827     {
16828         Roo.log('onTogglePeriod');
16829         this.time = this.time.add(Date.HOUR, 12);
16830         this.update();
16831     }
16832     
16833    
16834 });
16835
16836 Roo.apply(Roo.bootstrap.TimeField,  {
16837     
16838     content : {
16839         tag: 'tbody',
16840         cn: [
16841             {
16842                 tag: 'tr',
16843                 cn: [
16844                 {
16845                     tag: 'td',
16846                     colspan: '7'
16847                 }
16848                 ]
16849             }
16850         ]
16851     },
16852     
16853     footer : {
16854         tag: 'tfoot',
16855         cn: [
16856             {
16857                 tag: 'tr',
16858                 cn: [
16859                 {
16860                     tag: 'th',
16861                     colspan: '7',
16862                     cls: '',
16863                     cn: [
16864                         {
16865                             tag: 'button',
16866                             cls: 'btn btn-info ok',
16867                             html: 'OK'
16868                         }
16869                     ]
16870                 }
16871
16872                 ]
16873             }
16874         ]
16875     }
16876 });
16877
16878 Roo.apply(Roo.bootstrap.TimeField,  {
16879   
16880     template : {
16881         tag: 'div',
16882         cls: 'datepicker dropdown-menu',
16883         cn: [
16884             {
16885                 tag: 'div',
16886                 cls: 'datepicker-time',
16887                 cn: [
16888                 {
16889                     tag: 'table',
16890                     cls: 'table-condensed',
16891                     cn:[
16892                     Roo.bootstrap.TimeField.content,
16893                     Roo.bootstrap.TimeField.footer
16894                     ]
16895                 }
16896                 ]
16897             }
16898         ]
16899     }
16900 });
16901
16902  
16903
16904  /*
16905  * - LGPL
16906  *
16907  * MonthField
16908  * 
16909  */
16910
16911 /**
16912  * @class Roo.bootstrap.MonthField
16913  * @extends Roo.bootstrap.Input
16914  * Bootstrap MonthField class
16915  * 
16916  * @cfg {String} language default en
16917  * 
16918  * @constructor
16919  * Create a new MonthField
16920  * @param {Object} config The config object
16921  */
16922
16923 Roo.bootstrap.MonthField = function(config){
16924     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
16925     
16926     this.addEvents({
16927         /**
16928          * @event show
16929          * Fires when this field show.
16930          * @param {Roo.bootstrap.MonthField} this
16931          * @param {Mixed} date The date value
16932          */
16933         show : true,
16934         /**
16935          * @event show
16936          * Fires when this field hide.
16937          * @param {Roo.bootstrap.MonthField} this
16938          * @param {Mixed} date The date value
16939          */
16940         hide : true,
16941         /**
16942          * @event select
16943          * Fires when select a date.
16944          * @param {Roo.bootstrap.MonthField} this
16945          * @param {String} oldvalue The old value
16946          * @param {String} newvalue The new value
16947          */
16948         select : true
16949     });
16950 };
16951
16952 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
16953     
16954     onRender: function(ct, position)
16955     {
16956         
16957         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
16958         
16959         this.language = this.language || 'en';
16960         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
16961         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
16962         
16963         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
16964         this.isInline = false;
16965         this.isInput = true;
16966         this.component = this.el.select('.add-on', true).first() || false;
16967         this.component = (this.component && this.component.length === 0) ? false : this.component;
16968         this.hasInput = this.component && this.inputEL().length;
16969         
16970         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
16971         
16972         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16973         
16974         this.picker().on('mousedown', this.onMousedown, this);
16975         this.picker().on('click', this.onClick, this);
16976         
16977         this.picker().addClass('datepicker-dropdown');
16978         
16979         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16980             v.setStyle('width', '189px');
16981         });
16982         
16983         this.fillMonths();
16984         
16985         this.update();
16986         
16987         if(this.isInline) {
16988             this.show();
16989         }
16990         
16991     },
16992     
16993     setValue: function(v, suppressEvent)
16994     {   
16995         var o = this.getValue();
16996         
16997         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
16998         
16999         this.update();
17000
17001         if(suppressEvent !== true){
17002             this.fireEvent('select', this, o, v);
17003         }
17004         
17005     },
17006     
17007     getValue: function()
17008     {
17009         return this.value;
17010     },
17011     
17012     onClick: function(e) 
17013     {
17014         e.stopPropagation();
17015         e.preventDefault();
17016         
17017         var target = e.getTarget();
17018         
17019         if(target.nodeName.toLowerCase() === 'i'){
17020             target = Roo.get(target).dom.parentNode;
17021         }
17022         
17023         var nodeName = target.nodeName;
17024         var className = target.className;
17025         var html = target.innerHTML;
17026         
17027         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
17028             return;
17029         }
17030         
17031         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
17032         
17033         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17034         
17035         this.hide();
17036                         
17037     },
17038     
17039     picker : function()
17040     {
17041         return this.pickerEl;
17042     },
17043     
17044     fillMonths: function()
17045     {    
17046         var i = 0;
17047         var months = this.picker().select('>.datepicker-months td', true).first();
17048         
17049         months.dom.innerHTML = '';
17050         
17051         while (i < 12) {
17052             var month = {
17053                 tag: 'span',
17054                 cls: 'month',
17055                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
17056             }
17057             
17058             months.createChild(month);
17059         }
17060         
17061     },
17062     
17063     update: function()
17064     {
17065         var _this = this;
17066         
17067         if(typeof(this.vIndex) == 'undefined' && this.value.length){
17068             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
17069         }
17070         
17071         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
17072             e.removeClass('active');
17073             
17074             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
17075                 e.addClass('active');
17076             }
17077         })
17078     },
17079     
17080     place: function()
17081     {
17082         if(this.isInline) return;
17083         
17084         this.picker().removeClass(['bottom', 'top']);
17085         
17086         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17087             /*
17088              * place to the top of element!
17089              *
17090              */
17091             
17092             this.picker().addClass('top');
17093             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17094             
17095             return;
17096         }
17097         
17098         this.picker().addClass('bottom');
17099         
17100         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17101     },
17102     
17103     onFocus : function()
17104     {
17105         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
17106         this.show();
17107     },
17108     
17109     onBlur : function()
17110     {
17111         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
17112         
17113         var d = this.inputEl().getValue();
17114         
17115         this.setValue(d);
17116                 
17117         this.hide();
17118     },
17119     
17120     show : function()
17121     {
17122         this.picker().show();
17123         this.picker().select('>.datepicker-months', true).first().show();
17124         this.update();
17125         this.place();
17126         
17127         this.fireEvent('show', this, this.date);
17128     },
17129     
17130     hide : function()
17131     {
17132         if(this.isInline) return;
17133         this.picker().hide();
17134         this.fireEvent('hide', this, this.date);
17135         
17136     },
17137     
17138     onMousedown: function(e)
17139     {
17140         e.stopPropagation();
17141         e.preventDefault();
17142     },
17143     
17144     keyup: function(e)
17145     {
17146         Roo.bootstrap.MonthField.superclass.keyup.call(this);
17147         this.update();
17148     },
17149
17150     fireKey: function(e)
17151     {
17152         if (!this.picker().isVisible()){
17153             if (e.keyCode == 27) // allow escape to hide and re-show picker
17154                 this.show();
17155             return;
17156         }
17157         
17158         var dir;
17159         
17160         switch(e.keyCode){
17161             case 27: // escape
17162                 this.hide();
17163                 e.preventDefault();
17164                 break;
17165             case 37: // left
17166             case 39: // right
17167                 dir = e.keyCode == 37 ? -1 : 1;
17168                 
17169                 this.vIndex = this.vIndex + dir;
17170                 
17171                 if(this.vIndex < 0){
17172                     this.vIndex = 0;
17173                 }
17174                 
17175                 if(this.vIndex > 11){
17176                     this.vIndex = 11;
17177                 }
17178                 
17179                 if(isNaN(this.vIndex)){
17180                     this.vIndex = 0;
17181                 }
17182                 
17183                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17184                 
17185                 break;
17186             case 38: // up
17187             case 40: // down
17188                 
17189                 dir = e.keyCode == 38 ? -1 : 1;
17190                 
17191                 this.vIndex = this.vIndex + dir * 4;
17192                 
17193                 if(this.vIndex < 0){
17194                     this.vIndex = 0;
17195                 }
17196                 
17197                 if(this.vIndex > 11){
17198                     this.vIndex = 11;
17199                 }
17200                 
17201                 if(isNaN(this.vIndex)){
17202                     this.vIndex = 0;
17203                 }
17204                 
17205                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17206                 break;
17207                 
17208             case 13: // enter
17209                 
17210                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17211                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17212                 }
17213                 
17214                 this.hide();
17215                 e.preventDefault();
17216                 break;
17217             case 9: // tab
17218                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17219                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17220                 }
17221                 this.hide();
17222                 break;
17223             case 16: // shift
17224             case 17: // ctrl
17225             case 18: // alt
17226                 break;
17227             default :
17228                 this.hide();
17229                 
17230         }
17231     },
17232     
17233     remove: function() 
17234     {
17235         this.picker().remove();
17236     }
17237    
17238 });
17239
17240 Roo.apply(Roo.bootstrap.MonthField,  {
17241     
17242     content : {
17243         tag: 'tbody',
17244         cn: [
17245         {
17246             tag: 'tr',
17247             cn: [
17248             {
17249                 tag: 'td',
17250                 colspan: '7'
17251             }
17252             ]
17253         }
17254         ]
17255     },
17256     
17257     dates:{
17258         en: {
17259             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17260             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
17261         }
17262     }
17263 });
17264
17265 Roo.apply(Roo.bootstrap.MonthField,  {
17266   
17267     template : {
17268         tag: 'div',
17269         cls: 'datepicker dropdown-menu roo-dynamic',
17270         cn: [
17271             {
17272                 tag: 'div',
17273                 cls: 'datepicker-months',
17274                 cn: [
17275                 {
17276                     tag: 'table',
17277                     cls: 'table-condensed',
17278                     cn:[
17279                         Roo.bootstrap.DateField.content
17280                     ]
17281                 }
17282                 ]
17283             }
17284         ]
17285     }
17286 });
17287
17288  
17289
17290  
17291  /*
17292  * - LGPL
17293  *
17294  * CheckBox
17295  * 
17296  */
17297
17298 /**
17299  * @class Roo.bootstrap.CheckBox
17300  * @extends Roo.bootstrap.Input
17301  * Bootstrap CheckBox class
17302  * 
17303  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
17304  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
17305  * @cfg {String} boxLabel The text that appears beside the checkbox
17306  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
17307  * @cfg {Boolean} checked initnal the element
17308  * @cfg {Boolean} inline inline the element (default false)
17309  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
17310  * 
17311  * @constructor
17312  * Create a new CheckBox
17313  * @param {Object} config The config object
17314  */
17315
17316 Roo.bootstrap.CheckBox = function(config){
17317     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
17318    
17319     this.addEvents({
17320         /**
17321         * @event check
17322         * Fires when the element is checked or unchecked.
17323         * @param {Roo.bootstrap.CheckBox} this This input
17324         * @param {Boolean} checked The new checked value
17325         */
17326        check : true
17327     });
17328     
17329 };
17330
17331 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
17332   
17333     inputType: 'checkbox',
17334     inputValue: 1,
17335     valueOff: 0,
17336     boxLabel: false,
17337     checked: false,
17338     weight : false,
17339     inline: false,
17340     
17341     getAutoCreate : function()
17342     {
17343         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
17344         
17345         var id = Roo.id();
17346         
17347         var cfg = {};
17348         
17349         cfg.cls = 'form-group ' + this.inputType; //input-group
17350         
17351         if(this.inline){
17352             cfg.cls += ' ' + this.inputType + '-inline';
17353         }
17354         
17355         var input =  {
17356             tag: 'input',
17357             id : id,
17358             type : this.inputType,
17359             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
17360             cls : 'roo-' + this.inputType, //'form-box',
17361             placeholder : this.placeholder || ''
17362             
17363         };
17364         
17365         if (this.weight) { // Validity check?
17366             cfg.cls += " " + this.inputType + "-" + this.weight;
17367         }
17368         
17369         if (this.disabled) {
17370             input.disabled=true;
17371         }
17372         
17373         if(this.checked){
17374             input.checked = this.checked;
17375         }
17376         
17377         if (this.name) {
17378             input.name = this.name;
17379         }
17380         
17381         if (this.size) {
17382             input.cls += ' input-' + this.size;
17383         }
17384         
17385         var settings=this;
17386         
17387         ['xs','sm','md','lg'].map(function(size){
17388             if (settings[size]) {
17389                 cfg.cls += ' col-' + size + '-' + settings[size];
17390             }
17391         });
17392         
17393         var inputblock = input;
17394          
17395         if (this.before || this.after) {
17396             
17397             inputblock = {
17398                 cls : 'input-group',
17399                 cn :  [] 
17400             };
17401             
17402             if (this.before) {
17403                 inputblock.cn.push({
17404                     tag :'span',
17405                     cls : 'input-group-addon',
17406                     html : this.before
17407                 });
17408             }
17409             
17410             inputblock.cn.push(input);
17411             
17412             if (this.after) {
17413                 inputblock.cn.push({
17414                     tag :'span',
17415                     cls : 'input-group-addon',
17416                     html : this.after
17417                 });
17418             }
17419             
17420         }
17421         
17422         if (align ==='left' && this.fieldLabel.length) {
17423                 Roo.log("left and has label");
17424                 cfg.cn = [
17425                     
17426                     {
17427                         tag: 'label',
17428                         'for' :  id,
17429                         cls : 'control-label col-md-' + this.labelWidth,
17430                         html : this.fieldLabel
17431                         
17432                     },
17433                     {
17434                         cls : "col-md-" + (12 - this.labelWidth), 
17435                         cn: [
17436                             inputblock
17437                         ]
17438                     }
17439                     
17440                 ];
17441         } else if ( this.fieldLabel.length) {
17442                 Roo.log(" label");
17443                 cfg.cn = [
17444                    
17445                     {
17446                         tag: this.boxLabel ? 'span' : 'label',
17447                         'for': id,
17448                         cls: 'control-label box-input-label',
17449                         //cls : 'input-group-addon',
17450                         html : this.fieldLabel
17451                         
17452                     },
17453                     
17454                     inputblock
17455                     
17456                 ];
17457
17458         } else {
17459             
17460                 Roo.log(" no label && no align");
17461                 cfg.cn = [  inputblock ] ;
17462                 
17463                 
17464         }
17465         if(this.boxLabel){
17466              var boxLabelCfg = {
17467                 tag: 'label',
17468                 //'for': id, // box label is handled by onclick - so no for...
17469                 cls: 'box-label',
17470                 html: this.boxLabel
17471             }
17472             
17473             if(this.tooltip){
17474                 boxLabelCfg.tooltip = this.tooltip;
17475             }
17476              
17477             cfg.cn.push(boxLabelCfg);
17478         }
17479         
17480         
17481        
17482         return cfg;
17483         
17484     },
17485     
17486     /**
17487      * return the real input element.
17488      */
17489     inputEl: function ()
17490     {
17491         return this.el.select('input.roo-' + this.inputType,true).first();
17492     },
17493     
17494     labelEl: function()
17495     {
17496         return this.el.select('label.control-label',true).first();
17497     },
17498     /* depricated... */
17499     
17500     label: function()
17501     {
17502         return this.labelEl();
17503     },
17504     
17505     initEvents : function()
17506     {
17507 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
17508         
17509         this.inputEl().on('click', this.onClick,  this);
17510         
17511         if (this.boxLabel) { 
17512             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
17513         }
17514         
17515         this.startValue = this.getValue();
17516         
17517         if(this.groupId){
17518             Roo.bootstrap.CheckBox.register(this);
17519         }
17520     },
17521     
17522     onClick : function()
17523     {   
17524         this.setChecked(!this.checked);
17525     },
17526     
17527     setChecked : function(state,suppressEvent)
17528     {
17529         this.startValue = this.getValue();
17530         
17531         if(this.inputType == 'radio'){
17532             
17533             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17534                 e.dom.checked = false;
17535             });
17536             
17537             this.inputEl().dom.checked = true;
17538             
17539             this.inputEl().dom.value = this.inputValue;
17540             
17541             if(suppressEvent !== true){
17542                 this.fireEvent('check', this, true);
17543             }
17544             
17545             this.validate();
17546             
17547             return;
17548         }
17549         
17550         this.checked = state;
17551         
17552         this.inputEl().dom.checked = state;
17553         
17554         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
17555         
17556         if(suppressEvent !== true){
17557             this.fireEvent('check', this, state);
17558         }
17559         
17560         this.validate();
17561     },
17562     
17563     getValue : function()
17564     {
17565         if(this.inputType == 'radio'){
17566             return this.getGroupValue();
17567         }
17568         
17569         return this.inputEl().getValue();
17570         
17571     },
17572     
17573     getGroupValue : function()
17574     {
17575         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
17576             return '';
17577         }
17578         
17579         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
17580     },
17581     
17582     setValue : function(v,suppressEvent)
17583     {
17584         if(this.inputType == 'radio'){
17585             this.setGroupValue(v, suppressEvent);
17586             return;
17587         }
17588         
17589         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
17590         
17591         this.validate();
17592     },
17593     
17594     setGroupValue : function(v, suppressEvent)
17595     {
17596         this.startValue = this.getValue();
17597         
17598         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17599             e.dom.checked = false;
17600             
17601             if(e.dom.value == v){
17602                 e.dom.checked = true;
17603             }
17604         });
17605         
17606         if(suppressEvent !== true){
17607             this.fireEvent('check', this, true);
17608         }
17609
17610         this.validate();
17611         
17612         return;
17613     },
17614     
17615     validate : function()
17616     {
17617         if(
17618                 this.disabled || 
17619                 (this.inputType == 'radio' && this.validateRadio()) ||
17620                 (this.inputType == 'checkbox' && this.validateCheckbox())
17621         ){
17622             this.markValid();
17623             return true;
17624         }
17625         
17626         this.markInvalid();
17627         return false;
17628     },
17629     
17630     validateRadio : function()
17631     {
17632         var valid = false;
17633         
17634         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17635             if(!e.dom.checked){
17636                 return;
17637             }
17638             
17639             valid = true;
17640             
17641             return false;
17642         });
17643         
17644         return valid;
17645     },
17646     
17647     validateCheckbox : function()
17648     {
17649         if(!this.groupId){
17650             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
17651         }
17652         
17653         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17654         
17655         if(!group){
17656             return false;
17657         }
17658         
17659         var r = false;
17660         
17661         for(var i in group){
17662             if(r){
17663                 break;
17664             }
17665             
17666             r = (group[i].getValue() == group[i].inputValue) ? true : false;
17667         }
17668         
17669         return r;
17670     },
17671     
17672     /**
17673      * Mark this field as valid
17674      */
17675     markValid : function()
17676     {
17677         if(this.allowBlank){
17678             return;
17679         }
17680         
17681         var _this = this;
17682         
17683         this.fireEvent('valid', this);
17684         
17685         if(this.inputType == 'radio'){
17686             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17687                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
17688                 e.findParent('.form-group', false, true).addClass(_this.validClass);
17689             });
17690             
17691             return;
17692         }
17693         
17694         if(!this.groupId){
17695             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17696             this.el.findParent('.form-group', false, true).addClass(this.validClass);
17697             return;
17698         }
17699         
17700         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17701             
17702         if(!group){
17703             return;
17704         }
17705         
17706         for(var i in group){
17707             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17708             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
17709         }
17710     },
17711     
17712      /**
17713      * Mark this field as invalid
17714      * @param {String} msg The validation message
17715      */
17716     markInvalid : function(msg)
17717     {
17718         if(this.allowBlank){
17719             return;
17720         }
17721         
17722         var _this = this;
17723         
17724         this.fireEvent('invalid', this, msg);
17725         
17726         if(this.inputType == 'radio'){
17727             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17728                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
17729                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
17730             });
17731             
17732             return;
17733         }
17734         
17735         if(!this.groupId){
17736             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17737             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
17738             return;
17739         }
17740         
17741         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17742             
17743         if(!group){
17744             return;
17745         }
17746         
17747         for(var i in group){
17748             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17749             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
17750         }
17751         
17752     }
17753     
17754 });
17755
17756 Roo.apply(Roo.bootstrap.CheckBox, {
17757     
17758     groups: {},
17759     
17760      /**
17761     * register a CheckBox Group
17762     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
17763     */
17764     register : function(checkbox)
17765     {
17766         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
17767             this.groups[checkbox.groupId] = {};
17768         }
17769         
17770         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
17771             return;
17772         }
17773         
17774         this.groups[checkbox.groupId][checkbox.name] = checkbox;
17775         
17776     },
17777     /**
17778     * fetch a CheckBox Group based on the group ID
17779     * @param {string} the group ID
17780     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
17781     */
17782     get: function(groupId) {
17783         if (typeof(this.groups[groupId]) == 'undefined') {
17784             return false;
17785         }
17786         
17787         return this.groups[groupId] ;
17788     }
17789     
17790     
17791 });
17792 /*
17793  * - LGPL
17794  *
17795  * Radio
17796  *
17797  *
17798  * not inline
17799  *<div class="radio">
17800   <label>
17801     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
17802     Option one is this and that&mdash;be sure to include why it's great
17803   </label>
17804 </div>
17805  *
17806  *
17807  *inline
17808  *<span>
17809  *<label class="radio-inline">fieldLabel</label>
17810  *<label class="radio-inline">
17811   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
17812 </label>
17813 <span>
17814  * 
17815  * 
17816  */
17817
17818 /**
17819  * @class Roo.bootstrap.Radio
17820  * @extends Roo.bootstrap.CheckBox
17821  * Bootstrap Radio class
17822
17823  * @constructor
17824  * Create a new Radio
17825  * @param {Object} config The config object
17826  */
17827
17828 Roo.bootstrap.Radio = function(config){
17829     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
17830    
17831 };
17832
17833 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
17834     
17835     inputType: 'radio',
17836     inputValue: '',
17837     valueOff: '',
17838     
17839     getAutoCreate : function()
17840     {
17841         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
17842         align = align || 'left'; // default...
17843         
17844         
17845         
17846         var id = Roo.id();
17847         
17848         var cfg = {
17849                 tag : this.inline ? 'span' : 'div',
17850                 cls : '',
17851                 cn : []
17852         };
17853         
17854         var inline = this.inline ? ' radio-inline' : '';
17855         
17856         var lbl = {
17857                 tag: 'label' ,
17858                 // does not need for, as we wrap the input with it..
17859                 'for' : id,
17860                 cls : 'control-label box-label' + inline,
17861                 cn : []
17862         };
17863         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
17864         
17865         var fieldLabel = {
17866             tag: 'label' ,
17867             //cls : 'control-label' + inline,
17868             html : this.fieldLabel,
17869             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
17870         };
17871         
17872  
17873         
17874         
17875         var input =  {
17876             tag: 'input',
17877             id : id,
17878             type : this.inputType,
17879             //value : (!this.checked) ? this.valueOff : this.inputValue,
17880             value : this.inputValue,
17881             cls : 'roo-radio',
17882             placeholder : this.placeholder || '' // ?? needed????
17883             
17884         };
17885         if (this.weight) { // Validity check?
17886             input.cls += " radio-" + this.weight;
17887         }
17888         if (this.disabled) {
17889             input.disabled=true;
17890         }
17891         
17892         if(this.checked){
17893             input.checked = this.checked;
17894         }
17895         
17896         if (this.name) {
17897             input.name = this.name;
17898         }
17899         
17900         if (this.size) {
17901             input.cls += ' input-' + this.size;
17902         }
17903         
17904         //?? can span's inline have a width??
17905         
17906         var settings=this;
17907         ['xs','sm','md','lg'].map(function(size){
17908             if (settings[size]) {
17909                 cfg.cls += ' col-' + size + '-' + settings[size];
17910             }
17911         });
17912         
17913         var inputblock = input;
17914         
17915         if (this.before || this.after) {
17916             
17917             inputblock = {
17918                 cls : 'input-group',
17919                 tag : 'span',
17920                 cn :  [] 
17921             };
17922             if (this.before) {
17923                 inputblock.cn.push({
17924                     tag :'span',
17925                     cls : 'input-group-addon',
17926                     html : this.before
17927                 });
17928             }
17929             inputblock.cn.push(input);
17930             if (this.after) {
17931                 inputblock.cn.push({
17932                     tag :'span',
17933                     cls : 'input-group-addon',
17934                     html : this.after
17935                 });
17936             }
17937             
17938         };
17939         
17940         
17941         if (this.fieldLabel && this.fieldLabel.length) {
17942             cfg.cn.push(fieldLabel);
17943         }
17944        
17945         // normal bootstrap puts the input inside the label.
17946         // however with our styled version - it has to go after the input.
17947        
17948         //lbl.cn.push(inputblock);
17949         
17950         var lblwrap =  {
17951             tag: 'span',
17952             cls: 'radio' + inline,
17953             cn: [
17954                 inputblock,
17955                 lbl
17956             ]
17957         };
17958         
17959         cfg.cn.push( lblwrap);
17960         
17961         if(this.boxLabel){
17962             lbl.cn.push({
17963                 tag: 'span',
17964                 html: this.boxLabel
17965             })
17966         }
17967          
17968         
17969         return cfg;
17970         
17971     },
17972     
17973     initEvents : function()
17974     {
17975 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
17976         
17977         this.inputEl().on('click', this.onClick,  this);
17978         if (this.boxLabel) {
17979             Roo.log('find label')
17980             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
17981         }
17982         
17983     },
17984     
17985     inputEl: function ()
17986     {
17987         return this.el.select('input.roo-radio',true).first();
17988     },
17989     onClick : function()
17990     {   
17991         Roo.log("click");
17992         this.setChecked(true);
17993     },
17994     
17995     setChecked : function(state,suppressEvent)
17996     {
17997         if(state){
17998             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
17999                 v.dom.checked = false;
18000             });
18001         }
18002         Roo.log(this.inputEl().dom);
18003         this.checked = state;
18004         this.inputEl().dom.checked = state;
18005         
18006         if(suppressEvent !== true){
18007             this.fireEvent('check', this, state);
18008         }
18009         
18010         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18011         
18012     },
18013     
18014     getGroupValue : function()
18015     {
18016         var value = '';
18017         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18018             if(v.dom.checked == true){
18019                 value = v.dom.value;
18020             }
18021         });
18022         
18023         return value;
18024     },
18025     
18026     /**
18027      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
18028      * @return {Mixed} value The field value
18029      */
18030     getValue : function(){
18031         return this.getGroupValue();
18032     }
18033     
18034 });
18035
18036  
18037 //<script type="text/javascript">
18038
18039 /*
18040  * Based  Ext JS Library 1.1.1
18041  * Copyright(c) 2006-2007, Ext JS, LLC.
18042  * LGPL
18043  *
18044  */
18045  
18046 /**
18047  * @class Roo.HtmlEditorCore
18048  * @extends Roo.Component
18049  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
18050  *
18051  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
18052  */
18053
18054 Roo.HtmlEditorCore = function(config){
18055     
18056     
18057     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
18058     
18059     
18060     this.addEvents({
18061         /**
18062          * @event initialize
18063          * Fires when the editor is fully initialized (including the iframe)
18064          * @param {Roo.HtmlEditorCore} this
18065          */
18066         initialize: true,
18067         /**
18068          * @event activate
18069          * Fires when the editor is first receives the focus. Any insertion must wait
18070          * until after this event.
18071          * @param {Roo.HtmlEditorCore} this
18072          */
18073         activate: true,
18074          /**
18075          * @event beforesync
18076          * Fires before the textarea is updated with content from the editor iframe. Return false
18077          * to cancel the sync.
18078          * @param {Roo.HtmlEditorCore} this
18079          * @param {String} html
18080          */
18081         beforesync: true,
18082          /**
18083          * @event beforepush
18084          * Fires before the iframe editor is updated with content from the textarea. Return false
18085          * to cancel the push.
18086          * @param {Roo.HtmlEditorCore} this
18087          * @param {String} html
18088          */
18089         beforepush: true,
18090          /**
18091          * @event sync
18092          * Fires when the textarea is updated with content from the editor iframe.
18093          * @param {Roo.HtmlEditorCore} this
18094          * @param {String} html
18095          */
18096         sync: true,
18097          /**
18098          * @event push
18099          * Fires when the iframe editor is updated with content from the textarea.
18100          * @param {Roo.HtmlEditorCore} this
18101          * @param {String} html
18102          */
18103         push: true,
18104         
18105         /**
18106          * @event editorevent
18107          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
18108          * @param {Roo.HtmlEditorCore} this
18109          */
18110         editorevent: true
18111         
18112     });
18113     
18114     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
18115     
18116     // defaults : white / black...
18117     this.applyBlacklists();
18118     
18119     
18120     
18121 };
18122
18123
18124 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
18125
18126
18127      /**
18128      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
18129      */
18130     
18131     owner : false,
18132     
18133      /**
18134      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
18135      *                        Roo.resizable.
18136      */
18137     resizable : false,
18138      /**
18139      * @cfg {Number} height (in pixels)
18140      */   
18141     height: 300,
18142    /**
18143      * @cfg {Number} width (in pixels)
18144      */   
18145     width: 500,
18146     
18147     /**
18148      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
18149      * 
18150      */
18151     stylesheets: false,
18152     
18153     // id of frame..
18154     frameId: false,
18155     
18156     // private properties
18157     validationEvent : false,
18158     deferHeight: true,
18159     initialized : false,
18160     activated : false,
18161     sourceEditMode : false,
18162     onFocus : Roo.emptyFn,
18163     iframePad:3,
18164     hideMode:'offsets',
18165     
18166     clearUp: true,
18167     
18168     // blacklist + whitelisted elements..
18169     black: false,
18170     white: false,
18171      
18172     
18173
18174     /**
18175      * Protected method that will not generally be called directly. It
18176      * is called when the editor initializes the iframe with HTML contents. Override this method if you
18177      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
18178      */
18179     getDocMarkup : function(){
18180         // body styles..
18181         var st = '';
18182         
18183         // inherit styels from page...?? 
18184         if (this.stylesheets === false) {
18185             
18186             Roo.get(document.head).select('style').each(function(node) {
18187                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18188             });
18189             
18190             Roo.get(document.head).select('link').each(function(node) { 
18191                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18192             });
18193             
18194         } else if (!this.stylesheets.length) {
18195                 // simple..
18196                 st = '<style type="text/css">' +
18197                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18198                    '</style>';
18199         } else { 
18200             
18201         }
18202         
18203         st +=  '<style type="text/css">' +
18204             'IMG { cursor: pointer } ' +
18205         '</style>';
18206
18207         
18208         return '<html><head>' + st  +
18209             //<style type="text/css">' +
18210             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18211             //'</style>' +
18212             ' </head><body class="roo-htmleditor-body"></body></html>';
18213     },
18214
18215     // private
18216     onRender : function(ct, position)
18217     {
18218         var _t = this;
18219         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
18220         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
18221         
18222         
18223         this.el.dom.style.border = '0 none';
18224         this.el.dom.setAttribute('tabIndex', -1);
18225         this.el.addClass('x-hidden hide');
18226         
18227         
18228         
18229         if(Roo.isIE){ // fix IE 1px bogus margin
18230             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
18231         }
18232        
18233         
18234         this.frameId = Roo.id();
18235         
18236          
18237         
18238         var iframe = this.owner.wrap.createChild({
18239             tag: 'iframe',
18240             cls: 'form-control', // bootstrap..
18241             id: this.frameId,
18242             name: this.frameId,
18243             frameBorder : 'no',
18244             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
18245         }, this.el
18246         );
18247         
18248         
18249         this.iframe = iframe.dom;
18250
18251          this.assignDocWin();
18252         
18253         this.doc.designMode = 'on';
18254        
18255         this.doc.open();
18256         this.doc.write(this.getDocMarkup());
18257         this.doc.close();
18258
18259         
18260         var task = { // must defer to wait for browser to be ready
18261             run : function(){
18262                 //console.log("run task?" + this.doc.readyState);
18263                 this.assignDocWin();
18264                 if(this.doc.body || this.doc.readyState == 'complete'){
18265                     try {
18266                         this.doc.designMode="on";
18267                     } catch (e) {
18268                         return;
18269                     }
18270                     Roo.TaskMgr.stop(task);
18271                     this.initEditor.defer(10, this);
18272                 }
18273             },
18274             interval : 10,
18275             duration: 10000,
18276             scope: this
18277         };
18278         Roo.TaskMgr.start(task);
18279
18280     },
18281
18282     // private
18283     onResize : function(w, h)
18284     {
18285          Roo.log('resize: ' +w + ',' + h );
18286         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
18287         if(!this.iframe){
18288             return;
18289         }
18290         if(typeof w == 'number'){
18291             
18292             this.iframe.style.width = w + 'px';
18293         }
18294         if(typeof h == 'number'){
18295             
18296             this.iframe.style.height = h + 'px';
18297             if(this.doc){
18298                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
18299             }
18300         }
18301         
18302     },
18303
18304     /**
18305      * Toggles the editor between standard and source edit mode.
18306      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
18307      */
18308     toggleSourceEdit : function(sourceEditMode){
18309         
18310         this.sourceEditMode = sourceEditMode === true;
18311         
18312         if(this.sourceEditMode){
18313  
18314             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
18315             
18316         }else{
18317             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
18318             //this.iframe.className = '';
18319             this.deferFocus();
18320         }
18321         //this.setSize(this.owner.wrap.getSize());
18322         //this.fireEvent('editmodechange', this, this.sourceEditMode);
18323     },
18324
18325     
18326   
18327
18328     /**
18329      * Protected method that will not generally be called directly. If you need/want
18330      * custom HTML cleanup, this is the method you should override.
18331      * @param {String} html The HTML to be cleaned
18332      * return {String} The cleaned HTML
18333      */
18334     cleanHtml : function(html){
18335         html = String(html);
18336         if(html.length > 5){
18337             if(Roo.isSafari){ // strip safari nonsense
18338                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
18339             }
18340         }
18341         if(html == '&nbsp;'){
18342             html = '';
18343         }
18344         return html;
18345     },
18346
18347     /**
18348      * HTML Editor -> Textarea
18349      * Protected method that will not generally be called directly. Syncs the contents
18350      * of the editor iframe with the textarea.
18351      */
18352     syncValue : function(){
18353         if(this.initialized){
18354             var bd = (this.doc.body || this.doc.documentElement);
18355             //this.cleanUpPaste(); -- this is done else where and causes havoc..
18356             var html = bd.innerHTML;
18357             if(Roo.isSafari){
18358                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
18359                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
18360                 if(m && m[1]){
18361                     html = '<div style="'+m[0]+'">' + html + '</div>';
18362                 }
18363             }
18364             html = this.cleanHtml(html);
18365             // fix up the special chars.. normaly like back quotes in word...
18366             // however we do not want to do this with chinese..
18367             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
18368                 var cc = b.charCodeAt();
18369                 if (
18370                     (cc >= 0x4E00 && cc < 0xA000 ) ||
18371                     (cc >= 0x3400 && cc < 0x4E00 ) ||
18372                     (cc >= 0xf900 && cc < 0xfb00 )
18373                 ) {
18374                         return b;
18375                 }
18376                 return "&#"+cc+";" 
18377             });
18378             if(this.owner.fireEvent('beforesync', this, html) !== false){
18379                 this.el.dom.value = html;
18380                 this.owner.fireEvent('sync', this, html);
18381             }
18382         }
18383     },
18384
18385     /**
18386      * Protected method that will not generally be called directly. Pushes the value of the textarea
18387      * into the iframe editor.
18388      */
18389     pushValue : function(){
18390         if(this.initialized){
18391             var v = this.el.dom.value.trim();
18392             
18393 //            if(v.length < 1){
18394 //                v = '&#160;';
18395 //            }
18396             
18397             if(this.owner.fireEvent('beforepush', this, v) !== false){
18398                 var d = (this.doc.body || this.doc.documentElement);
18399                 d.innerHTML = v;
18400                 this.cleanUpPaste();
18401                 this.el.dom.value = d.innerHTML;
18402                 this.owner.fireEvent('push', this, v);
18403             }
18404         }
18405     },
18406
18407     // private
18408     deferFocus : function(){
18409         this.focus.defer(10, this);
18410     },
18411
18412     // doc'ed in Field
18413     focus : function(){
18414         if(this.win && !this.sourceEditMode){
18415             this.win.focus();
18416         }else{
18417             this.el.focus();
18418         }
18419     },
18420     
18421     assignDocWin: function()
18422     {
18423         var iframe = this.iframe;
18424         
18425          if(Roo.isIE){
18426             this.doc = iframe.contentWindow.document;
18427             this.win = iframe.contentWindow;
18428         } else {
18429 //            if (!Roo.get(this.frameId)) {
18430 //                return;
18431 //            }
18432 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
18433 //            this.win = Roo.get(this.frameId).dom.contentWindow;
18434             
18435             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
18436                 return;
18437             }
18438             
18439             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
18440             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
18441         }
18442     },
18443     
18444     // private
18445     initEditor : function(){
18446         //console.log("INIT EDITOR");
18447         this.assignDocWin();
18448         
18449         
18450         
18451         this.doc.designMode="on";
18452         this.doc.open();
18453         this.doc.write(this.getDocMarkup());
18454         this.doc.close();
18455         
18456         var dbody = (this.doc.body || this.doc.documentElement);
18457         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
18458         // this copies styles from the containing element into thsi one..
18459         // not sure why we need all of this..
18460         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
18461         
18462         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
18463         //ss['background-attachment'] = 'fixed'; // w3c
18464         dbody.bgProperties = 'fixed'; // ie
18465         //Roo.DomHelper.applyStyles(dbody, ss);
18466         Roo.EventManager.on(this.doc, {
18467             //'mousedown': this.onEditorEvent,
18468             'mouseup': this.onEditorEvent,
18469             'dblclick': this.onEditorEvent,
18470             'click': this.onEditorEvent,
18471             'keyup': this.onEditorEvent,
18472             buffer:100,
18473             scope: this
18474         });
18475         if(Roo.isGecko){
18476             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
18477         }
18478         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
18479             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
18480         }
18481         this.initialized = true;
18482
18483         this.owner.fireEvent('initialize', this);
18484         this.pushValue();
18485     },
18486
18487     // private
18488     onDestroy : function(){
18489         
18490         
18491         
18492         if(this.rendered){
18493             
18494             //for (var i =0; i < this.toolbars.length;i++) {
18495             //    // fixme - ask toolbars for heights?
18496             //    this.toolbars[i].onDestroy();
18497            // }
18498             
18499             //this.wrap.dom.innerHTML = '';
18500             //this.wrap.remove();
18501         }
18502     },
18503
18504     // private
18505     onFirstFocus : function(){
18506         
18507         this.assignDocWin();
18508         
18509         
18510         this.activated = true;
18511          
18512     
18513         if(Roo.isGecko){ // prevent silly gecko errors
18514             this.win.focus();
18515             var s = this.win.getSelection();
18516             if(!s.focusNode || s.focusNode.nodeType != 3){
18517                 var r = s.getRangeAt(0);
18518                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
18519                 r.collapse(true);
18520                 this.deferFocus();
18521             }
18522             try{
18523                 this.execCmd('useCSS', true);
18524                 this.execCmd('styleWithCSS', false);
18525             }catch(e){}
18526         }
18527         this.owner.fireEvent('activate', this);
18528     },
18529
18530     // private
18531     adjustFont: function(btn){
18532         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
18533         //if(Roo.isSafari){ // safari
18534         //    adjust *= 2;
18535        // }
18536         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
18537         if(Roo.isSafari){ // safari
18538             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
18539             v =  (v < 10) ? 10 : v;
18540             v =  (v > 48) ? 48 : v;
18541             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
18542             
18543         }
18544         
18545         
18546         v = Math.max(1, v+adjust);
18547         
18548         this.execCmd('FontSize', v  );
18549     },
18550
18551     onEditorEvent : function(e)
18552     {
18553         this.owner.fireEvent('editorevent', this, e);
18554       //  this.updateToolbar();
18555         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
18556     },
18557
18558     insertTag : function(tg)
18559     {
18560         // could be a bit smarter... -> wrap the current selected tRoo..
18561         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
18562             
18563             range = this.createRange(this.getSelection());
18564             var wrappingNode = this.doc.createElement(tg.toLowerCase());
18565             wrappingNode.appendChild(range.extractContents());
18566             range.insertNode(wrappingNode);
18567
18568             return;
18569             
18570             
18571             
18572         }
18573         this.execCmd("formatblock",   tg);
18574         
18575     },
18576     
18577     insertText : function(txt)
18578     {
18579         
18580         
18581         var range = this.createRange();
18582         range.deleteContents();
18583                //alert(Sender.getAttribute('label'));
18584                
18585         range.insertNode(this.doc.createTextNode(txt));
18586     } ,
18587     
18588      
18589
18590     /**
18591      * Executes a Midas editor command on the editor document and performs necessary focus and
18592      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
18593      * @param {String} cmd The Midas command
18594      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
18595      */
18596     relayCmd : function(cmd, value){
18597         this.win.focus();
18598         this.execCmd(cmd, value);
18599         this.owner.fireEvent('editorevent', this);
18600         //this.updateToolbar();
18601         this.owner.deferFocus();
18602     },
18603
18604     /**
18605      * Executes a Midas editor command directly on the editor document.
18606      * For visual commands, you should use {@link #relayCmd} instead.
18607      * <b>This should only be called after the editor is initialized.</b>
18608      * @param {String} cmd The Midas command
18609      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
18610      */
18611     execCmd : function(cmd, value){
18612         this.doc.execCommand(cmd, false, value === undefined ? null : value);
18613         this.syncValue();
18614     },
18615  
18616  
18617    
18618     /**
18619      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
18620      * to insert tRoo.
18621      * @param {String} text | dom node.. 
18622      */
18623     insertAtCursor : function(text)
18624     {
18625         
18626         
18627         
18628         if(!this.activated){
18629             return;
18630         }
18631         /*
18632         if(Roo.isIE){
18633             this.win.focus();
18634             var r = this.doc.selection.createRange();
18635             if(r){
18636                 r.collapse(true);
18637                 r.pasteHTML(text);
18638                 this.syncValue();
18639                 this.deferFocus();
18640             
18641             }
18642             return;
18643         }
18644         */
18645         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
18646             this.win.focus();
18647             
18648             
18649             // from jquery ui (MIT licenced)
18650             var range, node;
18651             var win = this.win;
18652             
18653             if (win.getSelection && win.getSelection().getRangeAt) {
18654                 range = win.getSelection().getRangeAt(0);
18655                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
18656                 range.insertNode(node);
18657             } else if (win.document.selection && win.document.selection.createRange) {
18658                 // no firefox support
18659                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
18660                 win.document.selection.createRange().pasteHTML(txt);
18661             } else {
18662                 // no firefox support
18663                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
18664                 this.execCmd('InsertHTML', txt);
18665             } 
18666             
18667             this.syncValue();
18668             
18669             this.deferFocus();
18670         }
18671     },
18672  // private
18673     mozKeyPress : function(e){
18674         if(e.ctrlKey){
18675             var c = e.getCharCode(), cmd;
18676           
18677             if(c > 0){
18678                 c = String.fromCharCode(c).toLowerCase();
18679                 switch(c){
18680                     case 'b':
18681                         cmd = 'bold';
18682                         break;
18683                     case 'i':
18684                         cmd = 'italic';
18685                         break;
18686                     
18687                     case 'u':
18688                         cmd = 'underline';
18689                         break;
18690                     
18691                     case 'v':
18692                         this.cleanUpPaste.defer(100, this);
18693                         return;
18694                         
18695                 }
18696                 if(cmd){
18697                     this.win.focus();
18698                     this.execCmd(cmd);
18699                     this.deferFocus();
18700                     e.preventDefault();
18701                 }
18702                 
18703             }
18704         }
18705     },
18706
18707     // private
18708     fixKeys : function(){ // load time branching for fastest keydown performance
18709         if(Roo.isIE){
18710             return function(e){
18711                 var k = e.getKey(), r;
18712                 if(k == e.TAB){
18713                     e.stopEvent();
18714                     r = this.doc.selection.createRange();
18715                     if(r){
18716                         r.collapse(true);
18717                         r.pasteHTML('&#160;&#160;&#160;&#160;');
18718                         this.deferFocus();
18719                     }
18720                     return;
18721                 }
18722                 
18723                 if(k == e.ENTER){
18724                     r = this.doc.selection.createRange();
18725                     if(r){
18726                         var target = r.parentElement();
18727                         if(!target || target.tagName.toLowerCase() != 'li'){
18728                             e.stopEvent();
18729                             r.pasteHTML('<br />');
18730                             r.collapse(false);
18731                             r.select();
18732                         }
18733                     }
18734                 }
18735                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18736                     this.cleanUpPaste.defer(100, this);
18737                     return;
18738                 }
18739                 
18740                 
18741             };
18742         }else if(Roo.isOpera){
18743             return function(e){
18744                 var k = e.getKey();
18745                 if(k == e.TAB){
18746                     e.stopEvent();
18747                     this.win.focus();
18748                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
18749                     this.deferFocus();
18750                 }
18751                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18752                     this.cleanUpPaste.defer(100, this);
18753                     return;
18754                 }
18755                 
18756             };
18757         }else if(Roo.isSafari){
18758             return function(e){
18759                 var k = e.getKey();
18760                 
18761                 if(k == e.TAB){
18762                     e.stopEvent();
18763                     this.execCmd('InsertText','\t');
18764                     this.deferFocus();
18765                     return;
18766                 }
18767                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18768                     this.cleanUpPaste.defer(100, this);
18769                     return;
18770                 }
18771                 
18772              };
18773         }
18774     }(),
18775     
18776     getAllAncestors: function()
18777     {
18778         var p = this.getSelectedNode();
18779         var a = [];
18780         if (!p) {
18781             a.push(p); // push blank onto stack..
18782             p = this.getParentElement();
18783         }
18784         
18785         
18786         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
18787             a.push(p);
18788             p = p.parentNode;
18789         }
18790         a.push(this.doc.body);
18791         return a;
18792     },
18793     lastSel : false,
18794     lastSelNode : false,
18795     
18796     
18797     getSelection : function() 
18798     {
18799         this.assignDocWin();
18800         return Roo.isIE ? this.doc.selection : this.win.getSelection();
18801     },
18802     
18803     getSelectedNode: function() 
18804     {
18805         // this may only work on Gecko!!!
18806         
18807         // should we cache this!!!!
18808         
18809         
18810         
18811          
18812         var range = this.createRange(this.getSelection()).cloneRange();
18813         
18814         if (Roo.isIE) {
18815             var parent = range.parentElement();
18816             while (true) {
18817                 var testRange = range.duplicate();
18818                 testRange.moveToElementText(parent);
18819                 if (testRange.inRange(range)) {
18820                     break;
18821                 }
18822                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
18823                     break;
18824                 }
18825                 parent = parent.parentElement;
18826             }
18827             return parent;
18828         }
18829         
18830         // is ancestor a text element.
18831         var ac =  range.commonAncestorContainer;
18832         if (ac.nodeType == 3) {
18833             ac = ac.parentNode;
18834         }
18835         
18836         var ar = ac.childNodes;
18837          
18838         var nodes = [];
18839         var other_nodes = [];
18840         var has_other_nodes = false;
18841         for (var i=0;i<ar.length;i++) {
18842             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
18843                 continue;
18844             }
18845             // fullly contained node.
18846             
18847             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
18848                 nodes.push(ar[i]);
18849                 continue;
18850             }
18851             
18852             // probably selected..
18853             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
18854                 other_nodes.push(ar[i]);
18855                 continue;
18856             }
18857             // outer..
18858             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
18859                 continue;
18860             }
18861             
18862             
18863             has_other_nodes = true;
18864         }
18865         if (!nodes.length && other_nodes.length) {
18866             nodes= other_nodes;
18867         }
18868         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
18869             return false;
18870         }
18871         
18872         return nodes[0];
18873     },
18874     createRange: function(sel)
18875     {
18876         // this has strange effects when using with 
18877         // top toolbar - not sure if it's a great idea.
18878         //this.editor.contentWindow.focus();
18879         if (typeof sel != "undefined") {
18880             try {
18881                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
18882             } catch(e) {
18883                 return this.doc.createRange();
18884             }
18885         } else {
18886             return this.doc.createRange();
18887         }
18888     },
18889     getParentElement: function()
18890     {
18891         
18892         this.assignDocWin();
18893         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
18894         
18895         var range = this.createRange(sel);
18896          
18897         try {
18898             var p = range.commonAncestorContainer;
18899             while (p.nodeType == 3) { // text node
18900                 p = p.parentNode;
18901             }
18902             return p;
18903         } catch (e) {
18904             return null;
18905         }
18906     
18907     },
18908     /***
18909      *
18910      * Range intersection.. the hard stuff...
18911      *  '-1' = before
18912      *  '0' = hits..
18913      *  '1' = after.
18914      *         [ -- selected range --- ]
18915      *   [fail]                        [fail]
18916      *
18917      *    basically..
18918      *      if end is before start or  hits it. fail.
18919      *      if start is after end or hits it fail.
18920      *
18921      *   if either hits (but other is outside. - then it's not 
18922      *   
18923      *    
18924      **/
18925     
18926     
18927     // @see http://www.thismuchiknow.co.uk/?p=64.
18928     rangeIntersectsNode : function(range, node)
18929     {
18930         var nodeRange = node.ownerDocument.createRange();
18931         try {
18932             nodeRange.selectNode(node);
18933         } catch (e) {
18934             nodeRange.selectNodeContents(node);
18935         }
18936     
18937         var rangeStartRange = range.cloneRange();
18938         rangeStartRange.collapse(true);
18939     
18940         var rangeEndRange = range.cloneRange();
18941         rangeEndRange.collapse(false);
18942     
18943         var nodeStartRange = nodeRange.cloneRange();
18944         nodeStartRange.collapse(true);
18945     
18946         var nodeEndRange = nodeRange.cloneRange();
18947         nodeEndRange.collapse(false);
18948     
18949         return rangeStartRange.compareBoundaryPoints(
18950                  Range.START_TO_START, nodeEndRange) == -1 &&
18951                rangeEndRange.compareBoundaryPoints(
18952                  Range.START_TO_START, nodeStartRange) == 1;
18953         
18954          
18955     },
18956     rangeCompareNode : function(range, node)
18957     {
18958         var nodeRange = node.ownerDocument.createRange();
18959         try {
18960             nodeRange.selectNode(node);
18961         } catch (e) {
18962             nodeRange.selectNodeContents(node);
18963         }
18964         
18965         
18966         range.collapse(true);
18967     
18968         nodeRange.collapse(true);
18969      
18970         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
18971         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
18972          
18973         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
18974         
18975         var nodeIsBefore   =  ss == 1;
18976         var nodeIsAfter    = ee == -1;
18977         
18978         if (nodeIsBefore && nodeIsAfter)
18979             return 0; // outer
18980         if (!nodeIsBefore && nodeIsAfter)
18981             return 1; //right trailed.
18982         
18983         if (nodeIsBefore && !nodeIsAfter)
18984             return 2;  // left trailed.
18985         // fully contined.
18986         return 3;
18987     },
18988
18989     // private? - in a new class?
18990     cleanUpPaste :  function()
18991     {
18992         // cleans up the whole document..
18993         Roo.log('cleanuppaste');
18994         
18995         this.cleanUpChildren(this.doc.body);
18996         var clean = this.cleanWordChars(this.doc.body.innerHTML);
18997         if (clean != this.doc.body.innerHTML) {
18998             this.doc.body.innerHTML = clean;
18999         }
19000         
19001     },
19002     
19003     cleanWordChars : function(input) {// change the chars to hex code
19004         var he = Roo.HtmlEditorCore;
19005         
19006         var output = input;
19007         Roo.each(he.swapCodes, function(sw) { 
19008             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
19009             
19010             output = output.replace(swapper, sw[1]);
19011         });
19012         
19013         return output;
19014     },
19015     
19016     
19017     cleanUpChildren : function (n)
19018     {
19019         if (!n.childNodes.length) {
19020             return;
19021         }
19022         for (var i = n.childNodes.length-1; i > -1 ; i--) {
19023            this.cleanUpChild(n.childNodes[i]);
19024         }
19025     },
19026     
19027     
19028         
19029     
19030     cleanUpChild : function (node)
19031     {
19032         var ed = this;
19033         //console.log(node);
19034         if (node.nodeName == "#text") {
19035             // clean up silly Windows -- stuff?
19036             return; 
19037         }
19038         if (node.nodeName == "#comment") {
19039             node.parentNode.removeChild(node);
19040             // clean up silly Windows -- stuff?
19041             return; 
19042         }
19043         var lcname = node.tagName.toLowerCase();
19044         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
19045         // whitelist of tags..
19046         
19047         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
19048             // remove node.
19049             node.parentNode.removeChild(node);
19050             return;
19051             
19052         }
19053         
19054         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
19055         
19056         // remove <a name=....> as rendering on yahoo mailer is borked with this.
19057         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
19058         
19059         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
19060         //    remove_keep_children = true;
19061         //}
19062         
19063         if (remove_keep_children) {
19064             this.cleanUpChildren(node);
19065             // inserts everything just before this node...
19066             while (node.childNodes.length) {
19067                 var cn = node.childNodes[0];
19068                 node.removeChild(cn);
19069                 node.parentNode.insertBefore(cn, node);
19070             }
19071             node.parentNode.removeChild(node);
19072             return;
19073         }
19074         
19075         if (!node.attributes || !node.attributes.length) {
19076             this.cleanUpChildren(node);
19077             return;
19078         }
19079         
19080         function cleanAttr(n,v)
19081         {
19082             
19083             if (v.match(/^\./) || v.match(/^\//)) {
19084                 return;
19085             }
19086             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
19087                 return;
19088             }
19089             if (v.match(/^#/)) {
19090                 return;
19091             }
19092 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
19093             node.removeAttribute(n);
19094             
19095         }
19096         
19097         var cwhite = this.cwhite;
19098         var cblack = this.cblack;
19099             
19100         function cleanStyle(n,v)
19101         {
19102             if (v.match(/expression/)) { //XSS?? should we even bother..
19103                 node.removeAttribute(n);
19104                 return;
19105             }
19106             
19107             var parts = v.split(/;/);
19108             var clean = [];
19109             
19110             Roo.each(parts, function(p) {
19111                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
19112                 if (!p.length) {
19113                     return true;
19114                 }
19115                 var l = p.split(':').shift().replace(/\s+/g,'');
19116                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
19117                 
19118                 if ( cwhite.length && cblack.indexOf(l) > -1) {
19119 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19120                     //node.removeAttribute(n);
19121                     return true;
19122                 }
19123                 //Roo.log()
19124                 // only allow 'c whitelisted system attributes'
19125                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
19126 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19127                     //node.removeAttribute(n);
19128                     return true;
19129                 }
19130                 
19131                 
19132                  
19133                 
19134                 clean.push(p);
19135                 return true;
19136             });
19137             if (clean.length) { 
19138                 node.setAttribute(n, clean.join(';'));
19139             } else {
19140                 node.removeAttribute(n);
19141             }
19142             
19143         }
19144         
19145         
19146         for (var i = node.attributes.length-1; i > -1 ; i--) {
19147             var a = node.attributes[i];
19148             //console.log(a);
19149             
19150             if (a.name.toLowerCase().substr(0,2)=='on')  {
19151                 node.removeAttribute(a.name);
19152                 continue;
19153             }
19154             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
19155                 node.removeAttribute(a.name);
19156                 continue;
19157             }
19158             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
19159                 cleanAttr(a.name,a.value); // fixme..
19160                 continue;
19161             }
19162             if (a.name == 'style') {
19163                 cleanStyle(a.name,a.value);
19164                 continue;
19165             }
19166             /// clean up MS crap..
19167             // tecnically this should be a list of valid class'es..
19168             
19169             
19170             if (a.name == 'class') {
19171                 if (a.value.match(/^Mso/)) {
19172                     node.className = '';
19173                 }
19174                 
19175                 if (a.value.match(/body/)) {
19176                     node.className = '';
19177                 }
19178                 continue;
19179             }
19180             
19181             // style cleanup!?
19182             // class cleanup?
19183             
19184         }
19185         
19186         
19187         this.cleanUpChildren(node);
19188         
19189         
19190     },
19191     
19192     /**
19193      * Clean up MS wordisms...
19194      */
19195     cleanWord : function(node)
19196     {
19197         
19198         
19199         if (!node) {
19200             this.cleanWord(this.doc.body);
19201             return;
19202         }
19203         if (node.nodeName == "#text") {
19204             // clean up silly Windows -- stuff?
19205             return; 
19206         }
19207         if (node.nodeName == "#comment") {
19208             node.parentNode.removeChild(node);
19209             // clean up silly Windows -- stuff?
19210             return; 
19211         }
19212         
19213         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
19214             node.parentNode.removeChild(node);
19215             return;
19216         }
19217         
19218         // remove - but keep children..
19219         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
19220             while (node.childNodes.length) {
19221                 var cn = node.childNodes[0];
19222                 node.removeChild(cn);
19223                 node.parentNode.insertBefore(cn, node);
19224             }
19225             node.parentNode.removeChild(node);
19226             this.iterateChildren(node, this.cleanWord);
19227             return;
19228         }
19229         // clean styles
19230         if (node.className.length) {
19231             
19232             var cn = node.className.split(/\W+/);
19233             var cna = [];
19234             Roo.each(cn, function(cls) {
19235                 if (cls.match(/Mso[a-zA-Z]+/)) {
19236                     return;
19237                 }
19238                 cna.push(cls);
19239             });
19240             node.className = cna.length ? cna.join(' ') : '';
19241             if (!cna.length) {
19242                 node.removeAttribute("class");
19243             }
19244         }
19245         
19246         if (node.hasAttribute("lang")) {
19247             node.removeAttribute("lang");
19248         }
19249         
19250         if (node.hasAttribute("style")) {
19251             
19252             var styles = node.getAttribute("style").split(";");
19253             var nstyle = [];
19254             Roo.each(styles, function(s) {
19255                 if (!s.match(/:/)) {
19256                     return;
19257                 }
19258                 var kv = s.split(":");
19259                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
19260                     return;
19261                 }
19262                 // what ever is left... we allow.
19263                 nstyle.push(s);
19264             });
19265             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
19266             if (!nstyle.length) {
19267                 node.removeAttribute('style');
19268             }
19269         }
19270         this.iterateChildren(node, this.cleanWord);
19271         
19272         
19273         
19274     },
19275     /**
19276      * iterateChildren of a Node, calling fn each time, using this as the scole..
19277      * @param {DomNode} node node to iterate children of.
19278      * @param {Function} fn method of this class to call on each item.
19279      */
19280     iterateChildren : function(node, fn)
19281     {
19282         if (!node.childNodes.length) {
19283                 return;
19284         }
19285         for (var i = node.childNodes.length-1; i > -1 ; i--) {
19286            fn.call(this, node.childNodes[i])
19287         }
19288     },
19289     
19290     
19291     /**
19292      * cleanTableWidths.
19293      *
19294      * Quite often pasting from word etc.. results in tables with column and widths.
19295      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
19296      *
19297      */
19298     cleanTableWidths : function(node)
19299     {
19300          
19301          
19302         if (!node) {
19303             this.cleanTableWidths(this.doc.body);
19304             return;
19305         }
19306         
19307         // ignore list...
19308         if (node.nodeName == "#text" || node.nodeName == "#comment") {
19309             return; 
19310         }
19311         Roo.log(node.tagName);
19312         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
19313             this.iterateChildren(node, this.cleanTableWidths);
19314             return;
19315         }
19316         if (node.hasAttribute('width')) {
19317             node.removeAttribute('width');
19318         }
19319         
19320          
19321         if (node.hasAttribute("style")) {
19322             // pretty basic...
19323             
19324             var styles = node.getAttribute("style").split(";");
19325             var nstyle = [];
19326             Roo.each(styles, function(s) {
19327                 if (!s.match(/:/)) {
19328                     return;
19329                 }
19330                 var kv = s.split(":");
19331                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
19332                     return;
19333                 }
19334                 // what ever is left... we allow.
19335                 nstyle.push(s);
19336             });
19337             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
19338             if (!nstyle.length) {
19339                 node.removeAttribute('style');
19340             }
19341         }
19342         
19343         this.iterateChildren(node, this.cleanTableWidths);
19344         
19345         
19346     },
19347     
19348     
19349     
19350     
19351     domToHTML : function(currentElement, depth, nopadtext) {
19352         
19353         depth = depth || 0;
19354         nopadtext = nopadtext || false;
19355     
19356         if (!currentElement) {
19357             return this.domToHTML(this.doc.body);
19358         }
19359         
19360         //Roo.log(currentElement);
19361         var j;
19362         var allText = false;
19363         var nodeName = currentElement.nodeName;
19364         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
19365         
19366         if  (nodeName == '#text') {
19367             
19368             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
19369         }
19370         
19371         
19372         var ret = '';
19373         if (nodeName != 'BODY') {
19374              
19375             var i = 0;
19376             // Prints the node tagName, such as <A>, <IMG>, etc
19377             if (tagName) {
19378                 var attr = [];
19379                 for(i = 0; i < currentElement.attributes.length;i++) {
19380                     // quoting?
19381                     var aname = currentElement.attributes.item(i).name;
19382                     if (!currentElement.attributes.item(i).value.length) {
19383                         continue;
19384                     }
19385                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
19386                 }
19387                 
19388                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
19389             } 
19390             else {
19391                 
19392                 // eack
19393             }
19394         } else {
19395             tagName = false;
19396         }
19397         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
19398             return ret;
19399         }
19400         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
19401             nopadtext = true;
19402         }
19403         
19404         
19405         // Traverse the tree
19406         i = 0;
19407         var currentElementChild = currentElement.childNodes.item(i);
19408         var allText = true;
19409         var innerHTML  = '';
19410         lastnode = '';
19411         while (currentElementChild) {
19412             // Formatting code (indent the tree so it looks nice on the screen)
19413             var nopad = nopadtext;
19414             if (lastnode == 'SPAN') {
19415                 nopad  = true;
19416             }
19417             // text
19418             if  (currentElementChild.nodeName == '#text') {
19419                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
19420                 toadd = nopadtext ? toadd : toadd.trim();
19421                 if (!nopad && toadd.length > 80) {
19422                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
19423                 }
19424                 innerHTML  += toadd;
19425                 
19426                 i++;
19427                 currentElementChild = currentElement.childNodes.item(i);
19428                 lastNode = '';
19429                 continue;
19430             }
19431             allText = false;
19432             
19433             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
19434                 
19435             // Recursively traverse the tree structure of the child node
19436             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
19437             lastnode = currentElementChild.nodeName;
19438             i++;
19439             currentElementChild=currentElement.childNodes.item(i);
19440         }
19441         
19442         ret += innerHTML;
19443         
19444         if (!allText) {
19445                 // The remaining code is mostly for formatting the tree
19446             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
19447         }
19448         
19449         
19450         if (tagName) {
19451             ret+= "</"+tagName+">";
19452         }
19453         return ret;
19454         
19455     },
19456         
19457     applyBlacklists : function()
19458     {
19459         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
19460         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
19461         
19462         this.white = [];
19463         this.black = [];
19464         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
19465             if (b.indexOf(tag) > -1) {
19466                 return;
19467             }
19468             this.white.push(tag);
19469             
19470         }, this);
19471         
19472         Roo.each(w, function(tag) {
19473             if (b.indexOf(tag) > -1) {
19474                 return;
19475             }
19476             if (this.white.indexOf(tag) > -1) {
19477                 return;
19478             }
19479             this.white.push(tag);
19480             
19481         }, this);
19482         
19483         
19484         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
19485             if (w.indexOf(tag) > -1) {
19486                 return;
19487             }
19488             this.black.push(tag);
19489             
19490         }, this);
19491         
19492         Roo.each(b, function(tag) {
19493             if (w.indexOf(tag) > -1) {
19494                 return;
19495             }
19496             if (this.black.indexOf(tag) > -1) {
19497                 return;
19498             }
19499             this.black.push(tag);
19500             
19501         }, this);
19502         
19503         
19504         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
19505         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
19506         
19507         this.cwhite = [];
19508         this.cblack = [];
19509         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
19510             if (b.indexOf(tag) > -1) {
19511                 return;
19512             }
19513             this.cwhite.push(tag);
19514             
19515         }, this);
19516         
19517         Roo.each(w, function(tag) {
19518             if (b.indexOf(tag) > -1) {
19519                 return;
19520             }
19521             if (this.cwhite.indexOf(tag) > -1) {
19522                 return;
19523             }
19524             this.cwhite.push(tag);
19525             
19526         }, this);
19527         
19528         
19529         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
19530             if (w.indexOf(tag) > -1) {
19531                 return;
19532             }
19533             this.cblack.push(tag);
19534             
19535         }, this);
19536         
19537         Roo.each(b, function(tag) {
19538             if (w.indexOf(tag) > -1) {
19539                 return;
19540             }
19541             if (this.cblack.indexOf(tag) > -1) {
19542                 return;
19543             }
19544             this.cblack.push(tag);
19545             
19546         }, this);
19547     },
19548     
19549     setStylesheets : function(stylesheets)
19550     {
19551         if(typeof(stylesheets) == 'string'){
19552             Roo.get(this.iframe.contentDocument.head).createChild({
19553                 tag : 'link',
19554                 rel : 'stylesheet',
19555                 type : 'text/css',
19556                 href : stylesheets
19557             });
19558             
19559             return;
19560         }
19561         var _this = this;
19562      
19563         Roo.each(stylesheets, function(s) {
19564             if(!s.length){
19565                 return;
19566             }
19567             
19568             Roo.get(_this.iframe.contentDocument.head).createChild({
19569                 tag : 'link',
19570                 rel : 'stylesheet',
19571                 type : 'text/css',
19572                 href : s
19573             });
19574         });
19575
19576         
19577     },
19578     
19579     removeStylesheets : function()
19580     {
19581         var _this = this;
19582         
19583         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
19584             s.remove();
19585         });
19586     }
19587     
19588     // hide stuff that is not compatible
19589     /**
19590      * @event blur
19591      * @hide
19592      */
19593     /**
19594      * @event change
19595      * @hide
19596      */
19597     /**
19598      * @event focus
19599      * @hide
19600      */
19601     /**
19602      * @event specialkey
19603      * @hide
19604      */
19605     /**
19606      * @cfg {String} fieldClass @hide
19607      */
19608     /**
19609      * @cfg {String} focusClass @hide
19610      */
19611     /**
19612      * @cfg {String} autoCreate @hide
19613      */
19614     /**
19615      * @cfg {String} inputType @hide
19616      */
19617     /**
19618      * @cfg {String} invalidClass @hide
19619      */
19620     /**
19621      * @cfg {String} invalidText @hide
19622      */
19623     /**
19624      * @cfg {String} msgFx @hide
19625      */
19626     /**
19627      * @cfg {String} validateOnBlur @hide
19628      */
19629 });
19630
19631 Roo.HtmlEditorCore.white = [
19632         'area', 'br', 'img', 'input', 'hr', 'wbr',
19633         
19634        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
19635        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
19636        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
19637        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
19638        'table',   'ul',         'xmp', 
19639        
19640        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
19641       'thead',   'tr', 
19642      
19643       'dir', 'menu', 'ol', 'ul', 'dl',
19644        
19645       'embed',  'object'
19646 ];
19647
19648
19649 Roo.HtmlEditorCore.black = [
19650     //    'embed',  'object', // enable - backend responsiblity to clean thiese
19651         'applet', // 
19652         'base',   'basefont', 'bgsound', 'blink',  'body', 
19653         'frame',  'frameset', 'head',    'html',   'ilayer', 
19654         'iframe', 'layer',  'link',     'meta',    'object',   
19655         'script', 'style' ,'title',  'xml' // clean later..
19656 ];
19657 Roo.HtmlEditorCore.clean = [
19658     'script', 'style', 'title', 'xml'
19659 ];
19660 Roo.HtmlEditorCore.remove = [
19661     'font'
19662 ];
19663 // attributes..
19664
19665 Roo.HtmlEditorCore.ablack = [
19666     'on'
19667 ];
19668     
19669 Roo.HtmlEditorCore.aclean = [ 
19670     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
19671 ];
19672
19673 // protocols..
19674 Roo.HtmlEditorCore.pwhite= [
19675         'http',  'https',  'mailto'
19676 ];
19677
19678 // white listed style attributes.
19679 Roo.HtmlEditorCore.cwhite= [
19680       //  'text-align', /// default is to allow most things..
19681       
19682          
19683 //        'font-size'//??
19684 ];
19685
19686 // black listed style attributes.
19687 Roo.HtmlEditorCore.cblack= [
19688       //  'font-size' -- this can be set by the project 
19689 ];
19690
19691
19692 Roo.HtmlEditorCore.swapCodes   =[ 
19693     [    8211, "--" ], 
19694     [    8212, "--" ], 
19695     [    8216,  "'" ],  
19696     [    8217, "'" ],  
19697     [    8220, '"' ],  
19698     [    8221, '"' ],  
19699     [    8226, "*" ],  
19700     [    8230, "..." ]
19701 ]; 
19702
19703     /*
19704  * - LGPL
19705  *
19706  * HtmlEditor
19707  * 
19708  */
19709
19710 /**
19711  * @class Roo.bootstrap.HtmlEditor
19712  * @extends Roo.bootstrap.TextArea
19713  * Bootstrap HtmlEditor class
19714
19715  * @constructor
19716  * Create a new HtmlEditor
19717  * @param {Object} config The config object
19718  */
19719
19720 Roo.bootstrap.HtmlEditor = function(config){
19721     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
19722     if (!this.toolbars) {
19723         this.toolbars = [];
19724     }
19725     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
19726     this.addEvents({
19727             /**
19728              * @event initialize
19729              * Fires when the editor is fully initialized (including the iframe)
19730              * @param {HtmlEditor} this
19731              */
19732             initialize: true,
19733             /**
19734              * @event activate
19735              * Fires when the editor is first receives the focus. Any insertion must wait
19736              * until after this event.
19737              * @param {HtmlEditor} this
19738              */
19739             activate: true,
19740              /**
19741              * @event beforesync
19742              * Fires before the textarea is updated with content from the editor iframe. Return false
19743              * to cancel the sync.
19744              * @param {HtmlEditor} this
19745              * @param {String} html
19746              */
19747             beforesync: true,
19748              /**
19749              * @event beforepush
19750              * Fires before the iframe editor is updated with content from the textarea. Return false
19751              * to cancel the push.
19752              * @param {HtmlEditor} this
19753              * @param {String} html
19754              */
19755             beforepush: true,
19756              /**
19757              * @event sync
19758              * Fires when the textarea is updated with content from the editor iframe.
19759              * @param {HtmlEditor} this
19760              * @param {String} html
19761              */
19762             sync: true,
19763              /**
19764              * @event push
19765              * Fires when the iframe editor is updated with content from the textarea.
19766              * @param {HtmlEditor} this
19767              * @param {String} html
19768              */
19769             push: true,
19770              /**
19771              * @event editmodechange
19772              * Fires when the editor switches edit modes
19773              * @param {HtmlEditor} this
19774              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
19775              */
19776             editmodechange: true,
19777             /**
19778              * @event editorevent
19779              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19780              * @param {HtmlEditor} this
19781              */
19782             editorevent: true,
19783             /**
19784              * @event firstfocus
19785              * Fires when on first focus - needed by toolbars..
19786              * @param {HtmlEditor} this
19787              */
19788             firstfocus: true,
19789             /**
19790              * @event autosave
19791              * Auto save the htmlEditor value as a file into Events
19792              * @param {HtmlEditor} this
19793              */
19794             autosave: true,
19795             /**
19796              * @event savedpreview
19797              * preview the saved version of htmlEditor
19798              * @param {HtmlEditor} this
19799              */
19800             savedpreview: true
19801         });
19802 };
19803
19804
19805 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
19806     
19807     
19808       /**
19809      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
19810      */
19811     toolbars : false,
19812    
19813      /**
19814      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
19815      *                        Roo.resizable.
19816      */
19817     resizable : false,
19818      /**
19819      * @cfg {Number} height (in pixels)
19820      */   
19821     height: 300,
19822    /**
19823      * @cfg {Number} width (in pixels)
19824      */   
19825     width: false,
19826     
19827     /**
19828      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19829      * 
19830      */
19831     stylesheets: false,
19832     
19833     // id of frame..
19834     frameId: false,
19835     
19836     // private properties
19837     validationEvent : false,
19838     deferHeight: true,
19839     initialized : false,
19840     activated : false,
19841     
19842     onFocus : Roo.emptyFn,
19843     iframePad:3,
19844     hideMode:'offsets',
19845     
19846     
19847     tbContainer : false,
19848     
19849     toolbarContainer :function() {
19850         return this.wrap.select('.x-html-editor-tb',true).first();
19851     },
19852
19853     /**
19854      * Protected method that will not generally be called directly. It
19855      * is called when the editor creates its toolbar. Override this method if you need to
19856      * add custom toolbar buttons.
19857      * @param {HtmlEditor} editor
19858      */
19859     createToolbar : function(){
19860         
19861         Roo.log("create toolbars");
19862         
19863         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
19864         this.toolbars[0].render(this.toolbarContainer());
19865         
19866         return;
19867         
19868 //        if (!editor.toolbars || !editor.toolbars.length) {
19869 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
19870 //        }
19871 //        
19872 //        for (var i =0 ; i < editor.toolbars.length;i++) {
19873 //            editor.toolbars[i] = Roo.factory(
19874 //                    typeof(editor.toolbars[i]) == 'string' ?
19875 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
19876 //                Roo.bootstrap.HtmlEditor);
19877 //            editor.toolbars[i].init(editor);
19878 //        }
19879     },
19880
19881      
19882     // private
19883     onRender : function(ct, position)
19884     {
19885        // Roo.log("Call onRender: " + this.xtype);
19886         var _t = this;
19887         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
19888       
19889         this.wrap = this.inputEl().wrap({
19890             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
19891         });
19892         
19893         this.editorcore.onRender(ct, position);
19894          
19895         if (this.resizable) {
19896             this.resizeEl = new Roo.Resizable(this.wrap, {
19897                 pinned : true,
19898                 wrap: true,
19899                 dynamic : true,
19900                 minHeight : this.height,
19901                 height: this.height,
19902                 handles : this.resizable,
19903                 width: this.width,
19904                 listeners : {
19905                     resize : function(r, w, h) {
19906                         _t.onResize(w,h); // -something
19907                     }
19908                 }
19909             });
19910             
19911         }
19912         this.createToolbar(this);
19913        
19914         
19915         if(!this.width && this.resizable){
19916             this.setSize(this.wrap.getSize());
19917         }
19918         if (this.resizeEl) {
19919             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
19920             // should trigger onReize..
19921         }
19922         
19923     },
19924
19925     // private
19926     onResize : function(w, h)
19927     {
19928         Roo.log('resize: ' +w + ',' + h );
19929         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
19930         var ew = false;
19931         var eh = false;
19932         
19933         if(this.inputEl() ){
19934             if(typeof w == 'number'){
19935                 var aw = w - this.wrap.getFrameWidth('lr');
19936                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
19937                 ew = aw;
19938             }
19939             if(typeof h == 'number'){
19940                  var tbh = -11;  // fixme it needs to tool bar size!
19941                 for (var i =0; i < this.toolbars.length;i++) {
19942                     // fixme - ask toolbars for heights?
19943                     tbh += this.toolbars[i].el.getHeight();
19944                     //if (this.toolbars[i].footer) {
19945                     //    tbh += this.toolbars[i].footer.el.getHeight();
19946                     //}
19947                 }
19948               
19949                 
19950                 
19951                 
19952                 
19953                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
19954                 ah -= 5; // knock a few pixes off for look..
19955                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
19956                 var eh = ah;
19957             }
19958         }
19959         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
19960         this.editorcore.onResize(ew,eh);
19961         
19962     },
19963
19964     /**
19965      * Toggles the editor between standard and source edit mode.
19966      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19967      */
19968     toggleSourceEdit : function(sourceEditMode)
19969     {
19970         this.editorcore.toggleSourceEdit(sourceEditMode);
19971         
19972         if(this.editorcore.sourceEditMode){
19973             Roo.log('editor - showing textarea');
19974             
19975 //            Roo.log('in');
19976 //            Roo.log(this.syncValue());
19977             this.syncValue();
19978             this.inputEl().removeClass(['hide', 'x-hidden']);
19979             this.inputEl().dom.removeAttribute('tabIndex');
19980             this.inputEl().focus();
19981         }else{
19982             Roo.log('editor - hiding textarea');
19983 //            Roo.log('out')
19984 //            Roo.log(this.pushValue()); 
19985             this.pushValue();
19986             
19987             this.inputEl().addClass(['hide', 'x-hidden']);
19988             this.inputEl().dom.setAttribute('tabIndex', -1);
19989             //this.deferFocus();
19990         }
19991          
19992         if(this.resizable){
19993             this.setSize(this.wrap.getSize());
19994         }
19995         
19996         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
19997     },
19998  
19999     // private (for BoxComponent)
20000     adjustSize : Roo.BoxComponent.prototype.adjustSize,
20001
20002     // private (for BoxComponent)
20003     getResizeEl : function(){
20004         return this.wrap;
20005     },
20006
20007     // private (for BoxComponent)
20008     getPositionEl : function(){
20009         return this.wrap;
20010     },
20011
20012     // private
20013     initEvents : function(){
20014         this.originalValue = this.getValue();
20015     },
20016
20017 //    /**
20018 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20019 //     * @method
20020 //     */
20021 //    markInvalid : Roo.emptyFn,
20022 //    /**
20023 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20024 //     * @method
20025 //     */
20026 //    clearInvalid : Roo.emptyFn,
20027
20028     setValue : function(v){
20029         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
20030         this.editorcore.pushValue();
20031     },
20032
20033      
20034     // private
20035     deferFocus : function(){
20036         this.focus.defer(10, this);
20037     },
20038
20039     // doc'ed in Field
20040     focus : function(){
20041         this.editorcore.focus();
20042         
20043     },
20044       
20045
20046     // private
20047     onDestroy : function(){
20048         
20049         
20050         
20051         if(this.rendered){
20052             
20053             for (var i =0; i < this.toolbars.length;i++) {
20054                 // fixme - ask toolbars for heights?
20055                 this.toolbars[i].onDestroy();
20056             }
20057             
20058             this.wrap.dom.innerHTML = '';
20059             this.wrap.remove();
20060         }
20061     },
20062
20063     // private
20064     onFirstFocus : function(){
20065         //Roo.log("onFirstFocus");
20066         this.editorcore.onFirstFocus();
20067          for (var i =0; i < this.toolbars.length;i++) {
20068             this.toolbars[i].onFirstFocus();
20069         }
20070         
20071     },
20072     
20073     // private
20074     syncValue : function()
20075     {   
20076         this.editorcore.syncValue();
20077     },
20078     
20079     pushValue : function()
20080     {   
20081         this.editorcore.pushValue();
20082     }
20083      
20084     
20085     // hide stuff that is not compatible
20086     /**
20087      * @event blur
20088      * @hide
20089      */
20090     /**
20091      * @event change
20092      * @hide
20093      */
20094     /**
20095      * @event focus
20096      * @hide
20097      */
20098     /**
20099      * @event specialkey
20100      * @hide
20101      */
20102     /**
20103      * @cfg {String} fieldClass @hide
20104      */
20105     /**
20106      * @cfg {String} focusClass @hide
20107      */
20108     /**
20109      * @cfg {String} autoCreate @hide
20110      */
20111     /**
20112      * @cfg {String} inputType @hide
20113      */
20114     /**
20115      * @cfg {String} invalidClass @hide
20116      */
20117     /**
20118      * @cfg {String} invalidText @hide
20119      */
20120     /**
20121      * @cfg {String} msgFx @hide
20122      */
20123     /**
20124      * @cfg {String} validateOnBlur @hide
20125      */
20126 });
20127  
20128     
20129    
20130    
20131    
20132       
20133 Roo.namespace('Roo.bootstrap.htmleditor');
20134 /**
20135  * @class Roo.bootstrap.HtmlEditorToolbar1
20136  * Basic Toolbar
20137  * 
20138  * Usage:
20139  *
20140  new Roo.bootstrap.HtmlEditor({
20141     ....
20142     toolbars : [
20143         new Roo.bootstrap.HtmlEditorToolbar1({
20144             disable : { fonts: 1 , format: 1, ..., ... , ...],
20145             btns : [ .... ]
20146         })
20147     }
20148      
20149  * 
20150  * @cfg {Object} disable List of elements to disable..
20151  * @cfg {Array} btns List of additional buttons.
20152  * 
20153  * 
20154  * NEEDS Extra CSS? 
20155  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
20156  */
20157  
20158 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
20159 {
20160     
20161     Roo.apply(this, config);
20162     
20163     // default disabled, based on 'good practice'..
20164     this.disable = this.disable || {};
20165     Roo.applyIf(this.disable, {
20166         fontSize : true,
20167         colors : true,
20168         specialElements : true
20169     });
20170     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
20171     
20172     this.editor = config.editor;
20173     this.editorcore = config.editor.editorcore;
20174     
20175     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
20176     
20177     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
20178     // dont call parent... till later.
20179 }
20180 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
20181      
20182     bar : true,
20183     
20184     editor : false,
20185     editorcore : false,
20186     
20187     
20188     formats : [
20189         "p" ,  
20190         "h1","h2","h3","h4","h5","h6", 
20191         "pre", "code", 
20192         "abbr", "acronym", "address", "cite", "samp", "var",
20193         'div','span'
20194     ],
20195     
20196     onRender : function(ct, position)
20197     {
20198        // Roo.log("Call onRender: " + this.xtype);
20199         
20200        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
20201        Roo.log(this.el);
20202        this.el.dom.style.marginBottom = '0';
20203        var _this = this;
20204        var editorcore = this.editorcore;
20205        var editor= this.editor;
20206        
20207        var children = [];
20208        var btn = function(id,cmd , toggle, handler){
20209        
20210             var  event = toggle ? 'toggle' : 'click';
20211        
20212             var a = {
20213                 size : 'sm',
20214                 xtype: 'Button',
20215                 xns: Roo.bootstrap,
20216                 glyphicon : id,
20217                 cmd : id || cmd,
20218                 enableToggle:toggle !== false,
20219                 //html : 'submit'
20220                 pressed : toggle ? false : null,
20221                 listeners : {}
20222             }
20223             a.listeners[toggle ? 'toggle' : 'click'] = function() {
20224                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
20225             }
20226             children.push(a);
20227             return a;
20228        }
20229         
20230         var style = {
20231                 xtype: 'Button',
20232                 size : 'sm',
20233                 xns: Roo.bootstrap,
20234                 glyphicon : 'font',
20235                 //html : 'submit'
20236                 menu : {
20237                     xtype: 'Menu',
20238                     xns: Roo.bootstrap,
20239                     items:  []
20240                 }
20241         };
20242         Roo.each(this.formats, function(f) {
20243             style.menu.items.push({
20244                 xtype :'MenuItem',
20245                 xns: Roo.bootstrap,
20246                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
20247                 tagname : f,
20248                 listeners : {
20249                     click : function()
20250                     {
20251                         editorcore.insertTag(this.tagname);
20252                         editor.focus();
20253                     }
20254                 }
20255                 
20256             });
20257         });
20258          children.push(style);   
20259             
20260             
20261         btn('bold',false,true);
20262         btn('italic',false,true);
20263         btn('align-left', 'justifyleft',true);
20264         btn('align-center', 'justifycenter',true);
20265         btn('align-right' , 'justifyright',true);
20266         btn('link', false, false, function(btn) {
20267             //Roo.log("create link?");
20268             var url = prompt(this.createLinkText, this.defaultLinkValue);
20269             if(url && url != 'http:/'+'/'){
20270                 this.editorcore.relayCmd('createlink', url);
20271             }
20272         }),
20273         btn('list','insertunorderedlist',true);
20274         btn('pencil', false,true, function(btn){
20275                 Roo.log(this);
20276                 
20277                 this.toggleSourceEdit(btn.pressed);
20278         });
20279         /*
20280         var cog = {
20281                 xtype: 'Button',
20282                 size : 'sm',
20283                 xns: Roo.bootstrap,
20284                 glyphicon : 'cog',
20285                 //html : 'submit'
20286                 menu : {
20287                     xtype: 'Menu',
20288                     xns: Roo.bootstrap,
20289                     items:  []
20290                 }
20291         };
20292         
20293         cog.menu.items.push({
20294             xtype :'MenuItem',
20295             xns: Roo.bootstrap,
20296             html : Clean styles,
20297             tagname : f,
20298             listeners : {
20299                 click : function()
20300                 {
20301                     editorcore.insertTag(this.tagname);
20302                     editor.focus();
20303                 }
20304             }
20305             
20306         });
20307        */
20308         
20309          
20310        this.xtype = 'NavSimplebar';
20311         
20312         for(var i=0;i< children.length;i++) {
20313             
20314             this.buttons.add(this.addxtypeChild(children[i]));
20315             
20316         }
20317         
20318         editor.on('editorevent', this.updateToolbar, this);
20319     },
20320     onBtnClick : function(id)
20321     {
20322        this.editorcore.relayCmd(id);
20323        this.editorcore.focus();
20324     },
20325     
20326     /**
20327      * Protected method that will not generally be called directly. It triggers
20328      * a toolbar update by reading the markup state of the current selection in the editor.
20329      */
20330     updateToolbar: function(){
20331
20332         if(!this.editorcore.activated){
20333             this.editor.onFirstFocus(); // is this neeed?
20334             return;
20335         }
20336
20337         var btns = this.buttons; 
20338         var doc = this.editorcore.doc;
20339         btns.get('bold').setActive(doc.queryCommandState('bold'));
20340         btns.get('italic').setActive(doc.queryCommandState('italic'));
20341         //btns.get('underline').setActive(doc.queryCommandState('underline'));
20342         
20343         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
20344         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
20345         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
20346         
20347         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
20348         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
20349          /*
20350         
20351         var ans = this.editorcore.getAllAncestors();
20352         if (this.formatCombo) {
20353             
20354             
20355             var store = this.formatCombo.store;
20356             this.formatCombo.setValue("");
20357             for (var i =0; i < ans.length;i++) {
20358                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
20359                     // select it..
20360                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
20361                     break;
20362                 }
20363             }
20364         }
20365         
20366         
20367         
20368         // hides menus... - so this cant be on a menu...
20369         Roo.bootstrap.MenuMgr.hideAll();
20370         */
20371         Roo.bootstrap.MenuMgr.hideAll();
20372         //this.editorsyncValue();
20373     },
20374     onFirstFocus: function() {
20375         this.buttons.each(function(item){
20376            item.enable();
20377         });
20378     },
20379     toggleSourceEdit : function(sourceEditMode){
20380         
20381           
20382         if(sourceEditMode){
20383             Roo.log("disabling buttons");
20384            this.buttons.each( function(item){
20385                 if(item.cmd != 'pencil'){
20386                     item.disable();
20387                 }
20388             });
20389           
20390         }else{
20391             Roo.log("enabling buttons");
20392             if(this.editorcore.initialized){
20393                 this.buttons.each( function(item){
20394                     item.enable();
20395                 });
20396             }
20397             
20398         }
20399         Roo.log("calling toggole on editor");
20400         // tell the editor that it's been pressed..
20401         this.editor.toggleSourceEdit(sourceEditMode);
20402        
20403     }
20404 });
20405
20406
20407
20408
20409
20410 /**
20411  * @class Roo.bootstrap.Table.AbstractSelectionModel
20412  * @extends Roo.util.Observable
20413  * Abstract base class for grid SelectionModels.  It provides the interface that should be
20414  * implemented by descendant classes.  This class should not be directly instantiated.
20415  * @constructor
20416  */
20417 Roo.bootstrap.Table.AbstractSelectionModel = function(){
20418     this.locked = false;
20419     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
20420 };
20421
20422
20423 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
20424     /** @ignore Called by the grid automatically. Do not call directly. */
20425     init : function(grid){
20426         this.grid = grid;
20427         this.initEvents();
20428     },
20429
20430     /**
20431      * Locks the selections.
20432      */
20433     lock : function(){
20434         this.locked = true;
20435     },
20436
20437     /**
20438      * Unlocks the selections.
20439      */
20440     unlock : function(){
20441         this.locked = false;
20442     },
20443
20444     /**
20445      * Returns true if the selections are locked.
20446      * @return {Boolean}
20447      */
20448     isLocked : function(){
20449         return this.locked;
20450     }
20451 });
20452 /**
20453  * @extends Roo.bootstrap.Table.AbstractSelectionModel
20454  * @class Roo.bootstrap.Table.RowSelectionModel
20455  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
20456  * It supports multiple selections and keyboard selection/navigation. 
20457  * @constructor
20458  * @param {Object} config
20459  */
20460
20461 Roo.bootstrap.Table.RowSelectionModel = function(config){
20462     Roo.apply(this, config);
20463     this.selections = new Roo.util.MixedCollection(false, function(o){
20464         return o.id;
20465     });
20466
20467     this.last = false;
20468     this.lastActive = false;
20469
20470     this.addEvents({
20471         /**
20472              * @event selectionchange
20473              * Fires when the selection changes
20474              * @param {SelectionModel} this
20475              */
20476             "selectionchange" : true,
20477         /**
20478              * @event afterselectionchange
20479              * Fires after the selection changes (eg. by key press or clicking)
20480              * @param {SelectionModel} this
20481              */
20482             "afterselectionchange" : true,
20483         /**
20484              * @event beforerowselect
20485              * Fires when a row is selected being selected, return false to cancel.
20486              * @param {SelectionModel} this
20487              * @param {Number} rowIndex The selected index
20488              * @param {Boolean} keepExisting False if other selections will be cleared
20489              */
20490             "beforerowselect" : true,
20491         /**
20492              * @event rowselect
20493              * Fires when a row is selected.
20494              * @param {SelectionModel} this
20495              * @param {Number} rowIndex The selected index
20496              * @param {Roo.data.Record} r The record
20497              */
20498             "rowselect" : true,
20499         /**
20500              * @event rowdeselect
20501              * Fires when a row is deselected.
20502              * @param {SelectionModel} this
20503              * @param {Number} rowIndex The selected index
20504              */
20505         "rowdeselect" : true
20506     });
20507     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
20508     this.locked = false;
20509 };
20510
20511 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
20512     /**
20513      * @cfg {Boolean} singleSelect
20514      * True to allow selection of only one row at a time (defaults to false)
20515      */
20516     singleSelect : false,
20517
20518     // private
20519     initEvents : function(){
20520
20521         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
20522             this.grid.on("mousedown", this.handleMouseDown, this);
20523         }else{ // allow click to work like normal
20524             this.grid.on("rowclick", this.handleDragableRowClick, this);
20525         }
20526
20527         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
20528             "up" : function(e){
20529                 if(!e.shiftKey){
20530                     this.selectPrevious(e.shiftKey);
20531                 }else if(this.last !== false && this.lastActive !== false){
20532                     var last = this.last;
20533                     this.selectRange(this.last,  this.lastActive-1);
20534                     this.grid.getView().focusRow(this.lastActive);
20535                     if(last !== false){
20536                         this.last = last;
20537                     }
20538                 }else{
20539                     this.selectFirstRow();
20540                 }
20541                 this.fireEvent("afterselectionchange", this);
20542             },
20543             "down" : function(e){
20544                 if(!e.shiftKey){
20545                     this.selectNext(e.shiftKey);
20546                 }else if(this.last !== false && this.lastActive !== false){
20547                     var last = this.last;
20548                     this.selectRange(this.last,  this.lastActive+1);
20549                     this.grid.getView().focusRow(this.lastActive);
20550                     if(last !== false){
20551                         this.last = last;
20552                     }
20553                 }else{
20554                     this.selectFirstRow();
20555                 }
20556                 this.fireEvent("afterselectionchange", this);
20557             },
20558             scope: this
20559         });
20560
20561         var view = this.grid.view;
20562         view.on("refresh", this.onRefresh, this);
20563         view.on("rowupdated", this.onRowUpdated, this);
20564         view.on("rowremoved", this.onRemove, this);
20565     },
20566
20567     // private
20568     onRefresh : function(){
20569         var ds = this.grid.dataSource, i, v = this.grid.view;
20570         var s = this.selections;
20571         s.each(function(r){
20572             if((i = ds.indexOfId(r.id)) != -1){
20573                 v.onRowSelect(i);
20574             }else{
20575                 s.remove(r);
20576             }
20577         });
20578     },
20579
20580     // private
20581     onRemove : function(v, index, r){
20582         this.selections.remove(r);
20583     },
20584
20585     // private
20586     onRowUpdated : function(v, index, r){
20587         if(this.isSelected(r)){
20588             v.onRowSelect(index);
20589         }
20590     },
20591
20592     /**
20593      * Select records.
20594      * @param {Array} records The records to select
20595      * @param {Boolean} keepExisting (optional) True to keep existing selections
20596      */
20597     selectRecords : function(records, keepExisting){
20598         if(!keepExisting){
20599             this.clearSelections();
20600         }
20601         var ds = this.grid.dataSource;
20602         for(var i = 0, len = records.length; i < len; i++){
20603             this.selectRow(ds.indexOf(records[i]), true);
20604         }
20605     },
20606
20607     /**
20608      * Gets the number of selected rows.
20609      * @return {Number}
20610      */
20611     getCount : function(){
20612         return this.selections.length;
20613     },
20614
20615     /**
20616      * Selects the first row in the grid.
20617      */
20618     selectFirstRow : function(){
20619         this.selectRow(0);
20620     },
20621
20622     /**
20623      * Select the last row.
20624      * @param {Boolean} keepExisting (optional) True to keep existing selections
20625      */
20626     selectLastRow : function(keepExisting){
20627         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
20628     },
20629
20630     /**
20631      * Selects the row immediately following the last selected row.
20632      * @param {Boolean} keepExisting (optional) True to keep existing selections
20633      */
20634     selectNext : function(keepExisting){
20635         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
20636             this.selectRow(this.last+1, keepExisting);
20637             this.grid.getView().focusRow(this.last);
20638         }
20639     },
20640
20641     /**
20642      * Selects the row that precedes the last selected row.
20643      * @param {Boolean} keepExisting (optional) True to keep existing selections
20644      */
20645     selectPrevious : function(keepExisting){
20646         if(this.last){
20647             this.selectRow(this.last-1, keepExisting);
20648             this.grid.getView().focusRow(this.last);
20649         }
20650     },
20651
20652     /**
20653      * Returns the selected records
20654      * @return {Array} Array of selected records
20655      */
20656     getSelections : function(){
20657         return [].concat(this.selections.items);
20658     },
20659
20660     /**
20661      * Returns the first selected record.
20662      * @return {Record}
20663      */
20664     getSelected : function(){
20665         return this.selections.itemAt(0);
20666     },
20667
20668
20669     /**
20670      * Clears all selections.
20671      */
20672     clearSelections : function(fast){
20673         if(this.locked) return;
20674         if(fast !== true){
20675             var ds = this.grid.dataSource;
20676             var s = this.selections;
20677             s.each(function(r){
20678                 this.deselectRow(ds.indexOfId(r.id));
20679             }, this);
20680             s.clear();
20681         }else{
20682             this.selections.clear();
20683         }
20684         this.last = false;
20685     },
20686
20687
20688     /**
20689      * Selects all rows.
20690      */
20691     selectAll : function(){
20692         if(this.locked) return;
20693         this.selections.clear();
20694         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
20695             this.selectRow(i, true);
20696         }
20697     },
20698
20699     /**
20700      * Returns True if there is a selection.
20701      * @return {Boolean}
20702      */
20703     hasSelection : function(){
20704         return this.selections.length > 0;
20705     },
20706
20707     /**
20708      * Returns True if the specified row is selected.
20709      * @param {Number/Record} record The record or index of the record to check
20710      * @return {Boolean}
20711      */
20712     isSelected : function(index){
20713         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
20714         return (r && this.selections.key(r.id) ? true : false);
20715     },
20716
20717     /**
20718      * Returns True if the specified record id is selected.
20719      * @param {String} id The id of record to check
20720      * @return {Boolean}
20721      */
20722     isIdSelected : function(id){
20723         return (this.selections.key(id) ? true : false);
20724     },
20725
20726     // private
20727     handleMouseDown : function(e, t){
20728         var view = this.grid.getView(), rowIndex;
20729         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
20730             return;
20731         };
20732         if(e.shiftKey && this.last !== false){
20733             var last = this.last;
20734             this.selectRange(last, rowIndex, e.ctrlKey);
20735             this.last = last; // reset the last
20736             view.focusRow(rowIndex);
20737         }else{
20738             var isSelected = this.isSelected(rowIndex);
20739             if(e.button !== 0 && isSelected){
20740                 view.focusRow(rowIndex);
20741             }else if(e.ctrlKey && isSelected){
20742                 this.deselectRow(rowIndex);
20743             }else if(!isSelected){
20744                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
20745                 view.focusRow(rowIndex);
20746             }
20747         }
20748         this.fireEvent("afterselectionchange", this);
20749     },
20750     // private
20751     handleDragableRowClick :  function(grid, rowIndex, e) 
20752     {
20753         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
20754             this.selectRow(rowIndex, false);
20755             grid.view.focusRow(rowIndex);
20756              this.fireEvent("afterselectionchange", this);
20757         }
20758     },
20759     
20760     /**
20761      * Selects multiple rows.
20762      * @param {Array} rows Array of the indexes of the row to select
20763      * @param {Boolean} keepExisting (optional) True to keep existing selections
20764      */
20765     selectRows : function(rows, keepExisting){
20766         if(!keepExisting){
20767             this.clearSelections();
20768         }
20769         for(var i = 0, len = rows.length; i < len; i++){
20770             this.selectRow(rows[i], true);
20771         }
20772     },
20773
20774     /**
20775      * Selects a range of rows. All rows in between startRow and endRow are also selected.
20776      * @param {Number} startRow The index of the first row in the range
20777      * @param {Number} endRow The index of the last row in the range
20778      * @param {Boolean} keepExisting (optional) True to retain existing selections
20779      */
20780     selectRange : function(startRow, endRow, keepExisting){
20781         if(this.locked) return;
20782         if(!keepExisting){
20783             this.clearSelections();
20784         }
20785         if(startRow <= endRow){
20786             for(var i = startRow; i <= endRow; i++){
20787                 this.selectRow(i, true);
20788             }
20789         }else{
20790             for(var i = startRow; i >= endRow; i--){
20791                 this.selectRow(i, true);
20792             }
20793         }
20794     },
20795
20796     /**
20797      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
20798      * @param {Number} startRow The index of the first row in the range
20799      * @param {Number} endRow The index of the last row in the range
20800      */
20801     deselectRange : function(startRow, endRow, preventViewNotify){
20802         if(this.locked) return;
20803         for(var i = startRow; i <= endRow; i++){
20804             this.deselectRow(i, preventViewNotify);
20805         }
20806     },
20807
20808     /**
20809      * Selects a row.
20810      * @param {Number} row The index of the row to select
20811      * @param {Boolean} keepExisting (optional) True to keep existing selections
20812      */
20813     selectRow : function(index, keepExisting, preventViewNotify){
20814         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
20815         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
20816             if(!keepExisting || this.singleSelect){
20817                 this.clearSelections();
20818             }
20819             var r = this.grid.dataSource.getAt(index);
20820             this.selections.add(r);
20821             this.last = this.lastActive = index;
20822             if(!preventViewNotify){
20823                 this.grid.getView().onRowSelect(index);
20824             }
20825             this.fireEvent("rowselect", this, index, r);
20826             this.fireEvent("selectionchange", this);
20827         }
20828     },
20829
20830     /**
20831      * Deselects a row.
20832      * @param {Number} row The index of the row to deselect
20833      */
20834     deselectRow : function(index, preventViewNotify){
20835         if(this.locked) return;
20836         if(this.last == index){
20837             this.last = false;
20838         }
20839         if(this.lastActive == index){
20840             this.lastActive = false;
20841         }
20842         var r = this.grid.dataSource.getAt(index);
20843         this.selections.remove(r);
20844         if(!preventViewNotify){
20845             this.grid.getView().onRowDeselect(index);
20846         }
20847         this.fireEvent("rowdeselect", this, index);
20848         this.fireEvent("selectionchange", this);
20849     },
20850
20851     // private
20852     restoreLast : function(){
20853         if(this._last){
20854             this.last = this._last;
20855         }
20856     },
20857
20858     // private
20859     acceptsNav : function(row, col, cm){
20860         return !cm.isHidden(col) && cm.isCellEditable(col, row);
20861     },
20862
20863     // private
20864     onEditorKey : function(field, e){
20865         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
20866         if(k == e.TAB){
20867             e.stopEvent();
20868             ed.completeEdit();
20869             if(e.shiftKey){
20870                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
20871             }else{
20872                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
20873             }
20874         }else if(k == e.ENTER && !e.ctrlKey){
20875             e.stopEvent();
20876             ed.completeEdit();
20877             if(e.shiftKey){
20878                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
20879             }else{
20880                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
20881             }
20882         }else if(k == e.ESC){
20883             ed.cancelEdit();
20884         }
20885         if(newCell){
20886             g.startEditing(newCell[0], newCell[1]);
20887         }
20888     }
20889 });/*
20890  * Based on:
20891  * Ext JS Library 1.1.1
20892  * Copyright(c) 2006-2007, Ext JS, LLC.
20893  *
20894  * Originally Released Under LGPL - original licence link has changed is not relivant.
20895  *
20896  * Fork - LGPL
20897  * <script type="text/javascript">
20898  */
20899  
20900 /**
20901  * @class Roo.bootstrap.PagingToolbar
20902  * @extends Roo.Row
20903  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
20904  * @constructor
20905  * Create a new PagingToolbar
20906  * @param {Object} config The config object
20907  */
20908 Roo.bootstrap.PagingToolbar = function(config)
20909 {
20910     // old args format still supported... - xtype is prefered..
20911         // created from xtype...
20912     var ds = config.dataSource;
20913     this.toolbarItems = [];
20914     if (config.items) {
20915         this.toolbarItems = config.items;
20916 //        config.items = [];
20917     }
20918     
20919     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
20920     this.ds = ds;
20921     this.cursor = 0;
20922     if (ds) { 
20923         this.bind(ds);
20924     }
20925     
20926     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
20927     
20928 };
20929
20930 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
20931     /**
20932      * @cfg {Roo.data.Store} dataSource
20933      * The underlying data store providing the paged data
20934      */
20935     /**
20936      * @cfg {String/HTMLElement/Element} container
20937      * container The id or element that will contain the toolbar
20938      */
20939     /**
20940      * @cfg {Boolean} displayInfo
20941      * True to display the displayMsg (defaults to false)
20942      */
20943     /**
20944      * @cfg {Number} pageSize
20945      * The number of records to display per page (defaults to 20)
20946      */
20947     pageSize: 20,
20948     /**
20949      * @cfg {String} displayMsg
20950      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
20951      */
20952     displayMsg : 'Displaying {0} - {1} of {2}',
20953     /**
20954      * @cfg {String} emptyMsg
20955      * The message to display when no records are found (defaults to "No data to display")
20956      */
20957     emptyMsg : 'No data to display',
20958     /**
20959      * Customizable piece of the default paging text (defaults to "Page")
20960      * @type String
20961      */
20962     beforePageText : "Page",
20963     /**
20964      * Customizable piece of the default paging text (defaults to "of %0")
20965      * @type String
20966      */
20967     afterPageText : "of {0}",
20968     /**
20969      * Customizable piece of the default paging text (defaults to "First Page")
20970      * @type String
20971      */
20972     firstText : "First Page",
20973     /**
20974      * Customizable piece of the default paging text (defaults to "Previous Page")
20975      * @type String
20976      */
20977     prevText : "Previous Page",
20978     /**
20979      * Customizable piece of the default paging text (defaults to "Next Page")
20980      * @type String
20981      */
20982     nextText : "Next Page",
20983     /**
20984      * Customizable piece of the default paging text (defaults to "Last Page")
20985      * @type String
20986      */
20987     lastText : "Last Page",
20988     /**
20989      * Customizable piece of the default paging text (defaults to "Refresh")
20990      * @type String
20991      */
20992     refreshText : "Refresh",
20993
20994     buttons : false,
20995     // private
20996     onRender : function(ct, position) 
20997     {
20998         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
20999         this.navgroup.parentId = this.id;
21000         this.navgroup.onRender(this.el, null);
21001         // add the buttons to the navgroup
21002         
21003         if(this.displayInfo){
21004             Roo.log(this.el.select('ul.navbar-nav',true).first());
21005             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
21006             this.displayEl = this.el.select('.x-paging-info', true).first();
21007 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
21008 //            this.displayEl = navel.el.select('span',true).first();
21009         }
21010         
21011         var _this = this;
21012         
21013         if(this.buttons){
21014             Roo.each(_this.buttons, function(e){
21015                Roo.factory(e).onRender(_this.el, null);
21016             });
21017         }
21018             
21019         Roo.each(_this.toolbarItems, function(e) {
21020             _this.navgroup.addItem(e);
21021         });
21022         
21023         
21024         this.first = this.navgroup.addItem({
21025             tooltip: this.firstText,
21026             cls: "prev",
21027             icon : 'fa fa-backward',
21028             disabled: true,
21029             preventDefault: true,
21030             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
21031         });
21032         
21033         this.prev =  this.navgroup.addItem({
21034             tooltip: this.prevText,
21035             cls: "prev",
21036             icon : 'fa fa-step-backward',
21037             disabled: true,
21038             preventDefault: true,
21039             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
21040         });
21041     //this.addSeparator();
21042         
21043         
21044         var field = this.navgroup.addItem( {
21045             tagtype : 'span',
21046             cls : 'x-paging-position',
21047             
21048             html : this.beforePageText  +
21049                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
21050                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
21051          } ); //?? escaped?
21052         
21053         this.field = field.el.select('input', true).first();
21054         this.field.on("keydown", this.onPagingKeydown, this);
21055         this.field.on("focus", function(){this.dom.select();});
21056     
21057     
21058         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
21059         //this.field.setHeight(18);
21060         //this.addSeparator();
21061         this.next = this.navgroup.addItem({
21062             tooltip: this.nextText,
21063             cls: "next",
21064             html : ' <i class="fa fa-step-forward">',
21065             disabled: true,
21066             preventDefault: true,
21067             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
21068         });
21069         this.last = this.navgroup.addItem({
21070             tooltip: this.lastText,
21071             icon : 'fa fa-forward',
21072             cls: "next",
21073             disabled: true,
21074             preventDefault: true,
21075             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
21076         });
21077     //this.addSeparator();
21078         this.loading = this.navgroup.addItem({
21079             tooltip: this.refreshText,
21080             icon: 'fa fa-refresh',
21081             preventDefault: true,
21082             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
21083         });
21084
21085     },
21086
21087     // private
21088     updateInfo : function(){
21089         if(this.displayEl){
21090             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
21091             var msg = count == 0 ?
21092                 this.emptyMsg :
21093                 String.format(
21094                     this.displayMsg,
21095                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
21096                 );
21097             this.displayEl.update(msg);
21098         }
21099     },
21100
21101     // private
21102     onLoad : function(ds, r, o){
21103        this.cursor = o.params ? o.params.start : 0;
21104        var d = this.getPageData(),
21105             ap = d.activePage,
21106             ps = d.pages;
21107         
21108        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
21109        this.field.dom.value = ap;
21110        this.first.setDisabled(ap == 1);
21111        this.prev.setDisabled(ap == 1);
21112        this.next.setDisabled(ap == ps);
21113        this.last.setDisabled(ap == ps);
21114        this.loading.enable();
21115        this.updateInfo();
21116     },
21117
21118     // private
21119     getPageData : function(){
21120         var total = this.ds.getTotalCount();
21121         return {
21122             total : total,
21123             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
21124             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
21125         };
21126     },
21127
21128     // private
21129     onLoadError : function(){
21130         this.loading.enable();
21131     },
21132
21133     // private
21134     onPagingKeydown : function(e){
21135         var k = e.getKey();
21136         var d = this.getPageData();
21137         if(k == e.RETURN){
21138             var v = this.field.dom.value, pageNum;
21139             if(!v || isNaN(pageNum = parseInt(v, 10))){
21140                 this.field.dom.value = d.activePage;
21141                 return;
21142             }
21143             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
21144             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
21145             e.stopEvent();
21146         }
21147         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))
21148         {
21149           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
21150           this.field.dom.value = pageNum;
21151           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
21152           e.stopEvent();
21153         }
21154         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
21155         {
21156           var v = this.field.dom.value, pageNum; 
21157           var increment = (e.shiftKey) ? 10 : 1;
21158           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
21159             increment *= -1;
21160           if(!v || isNaN(pageNum = parseInt(v, 10))) {
21161             this.field.dom.value = d.activePage;
21162             return;
21163           }
21164           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
21165           {
21166             this.field.dom.value = parseInt(v, 10) + increment;
21167             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
21168             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
21169           }
21170           e.stopEvent();
21171         }
21172     },
21173
21174     // private
21175     beforeLoad : function(){
21176         if(this.loading){
21177             this.loading.disable();
21178         }
21179     },
21180
21181     // private
21182     onClick : function(which){
21183         
21184         var ds = this.ds;
21185         if (!ds) {
21186             return;
21187         }
21188         
21189         switch(which){
21190             case "first":
21191                 ds.load({params:{start: 0, limit: this.pageSize}});
21192             break;
21193             case "prev":
21194                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
21195             break;
21196             case "next":
21197                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
21198             break;
21199             case "last":
21200                 var total = ds.getTotalCount();
21201                 var extra = total % this.pageSize;
21202                 var lastStart = extra ? (total - extra) : total-this.pageSize;
21203                 ds.load({params:{start: lastStart, limit: this.pageSize}});
21204             break;
21205             case "refresh":
21206                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
21207             break;
21208         }
21209     },
21210
21211     /**
21212      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
21213      * @param {Roo.data.Store} store The data store to unbind
21214      */
21215     unbind : function(ds){
21216         ds.un("beforeload", this.beforeLoad, this);
21217         ds.un("load", this.onLoad, this);
21218         ds.un("loadexception", this.onLoadError, this);
21219         ds.un("remove", this.updateInfo, this);
21220         ds.un("add", this.updateInfo, this);
21221         this.ds = undefined;
21222     },
21223
21224     /**
21225      * Binds the paging toolbar to the specified {@link Roo.data.Store}
21226      * @param {Roo.data.Store} store The data store to bind
21227      */
21228     bind : function(ds){
21229         ds.on("beforeload", this.beforeLoad, this);
21230         ds.on("load", this.onLoad, this);
21231         ds.on("loadexception", this.onLoadError, this);
21232         ds.on("remove", this.updateInfo, this);
21233         ds.on("add", this.updateInfo, this);
21234         this.ds = ds;
21235     }
21236 });/*
21237  * - LGPL
21238  *
21239  * element
21240  * 
21241  */
21242
21243 /**
21244  * @class Roo.bootstrap.MessageBar
21245  * @extends Roo.bootstrap.Component
21246  * Bootstrap MessageBar class
21247  * @cfg {String} html contents of the MessageBar
21248  * @cfg {String} weight (info | success | warning | danger) default info
21249  * @cfg {String} beforeClass insert the bar before the given class
21250  * @cfg {Boolean} closable (true | false) default false
21251  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
21252  * 
21253  * @constructor
21254  * Create a new Element
21255  * @param {Object} config The config object
21256  */
21257
21258 Roo.bootstrap.MessageBar = function(config){
21259     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
21260 };
21261
21262 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
21263     
21264     html: '',
21265     weight: 'info',
21266     closable: false,
21267     fixed: false,
21268     beforeClass: 'bootstrap-sticky-wrap',
21269     
21270     getAutoCreate : function(){
21271         
21272         var cfg = {
21273             tag: 'div',
21274             cls: 'alert alert-dismissable alert-' + this.weight,
21275             cn: [
21276                 {
21277                     tag: 'span',
21278                     cls: 'message',
21279                     html: this.html || ''
21280                 }
21281             ]
21282         }
21283         
21284         if(this.fixed){
21285             cfg.cls += ' alert-messages-fixed';
21286         }
21287         
21288         if(this.closable){
21289             cfg.cn.push({
21290                 tag: 'button',
21291                 cls: 'close',
21292                 html: 'x'
21293             });
21294         }
21295         
21296         return cfg;
21297     },
21298     
21299     onRender : function(ct, position)
21300     {
21301         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21302         
21303         if(!this.el){
21304             var cfg = Roo.apply({},  this.getAutoCreate());
21305             cfg.id = Roo.id();
21306             
21307             if (this.cls) {
21308                 cfg.cls += ' ' + this.cls;
21309             }
21310             if (this.style) {
21311                 cfg.style = this.style;
21312             }
21313             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
21314             
21315             this.el.setVisibilityMode(Roo.Element.DISPLAY);
21316         }
21317         
21318         this.el.select('>button.close').on('click', this.hide, this);
21319         
21320     },
21321     
21322     show : function()
21323     {
21324         if (!this.rendered) {
21325             this.render();
21326         }
21327         
21328         this.el.show();
21329         
21330         this.fireEvent('show', this);
21331         
21332     },
21333     
21334     hide : function()
21335     {
21336         if (!this.rendered) {
21337             this.render();
21338         }
21339         
21340         this.el.hide();
21341         
21342         this.fireEvent('hide', this);
21343     },
21344     
21345     update : function()
21346     {
21347 //        var e = this.el.dom.firstChild;
21348 //        
21349 //        if(this.closable){
21350 //            e = e.nextSibling;
21351 //        }
21352 //        
21353 //        e.data = this.html || '';
21354
21355         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
21356     }
21357    
21358 });
21359
21360  
21361
21362      /*
21363  * - LGPL
21364  *
21365  * Graph
21366  * 
21367  */
21368
21369
21370 /**
21371  * @class Roo.bootstrap.Graph
21372  * @extends Roo.bootstrap.Component
21373  * Bootstrap Graph class
21374 > Prameters
21375  -sm {number} sm 4
21376  -md {number} md 5
21377  @cfg {String} graphtype  bar | vbar | pie
21378  @cfg {number} g_x coodinator | centre x (pie)
21379  @cfg {number} g_y coodinator | centre y (pie)
21380  @cfg {number} g_r radius (pie)
21381  @cfg {number} g_height height of the chart (respected by all elements in the set)
21382  @cfg {number} g_width width of the chart (respected by all elements in the set)
21383  @cfg {Object} title The title of the chart
21384     
21385  -{Array}  values
21386  -opts (object) options for the chart 
21387      o {
21388      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
21389      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
21390      o vgutter (number)
21391      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.
21392      o stacked (boolean) whether or not to tread values as in a stacked bar chart
21393      o to
21394      o stretch (boolean)
21395      o }
21396  -opts (object) options for the pie
21397      o{
21398      o cut
21399      o startAngle (number)
21400      o endAngle (number)
21401      } 
21402  *
21403  * @constructor
21404  * Create a new Input
21405  * @param {Object} config The config object
21406  */
21407
21408 Roo.bootstrap.Graph = function(config){
21409     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
21410     
21411     this.addEvents({
21412         // img events
21413         /**
21414          * @event click
21415          * The img click event for the img.
21416          * @param {Roo.EventObject} e
21417          */
21418         "click" : true
21419     });
21420 };
21421
21422 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
21423     
21424     sm: 4,
21425     md: 5,
21426     graphtype: 'bar',
21427     g_height: 250,
21428     g_width: 400,
21429     g_x: 50,
21430     g_y: 50,
21431     g_r: 30,
21432     opts:{
21433         //g_colors: this.colors,
21434         g_type: 'soft',
21435         g_gutter: '20%'
21436
21437     },
21438     title : false,
21439
21440     getAutoCreate : function(){
21441         
21442         var cfg = {
21443             tag: 'div',
21444             html : null
21445         }
21446         
21447         
21448         return  cfg;
21449     },
21450
21451     onRender : function(ct,position){
21452         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
21453         this.raphael = Raphael(this.el.dom);
21454         
21455                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
21456                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
21457                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
21458                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
21459                 /*
21460                 r.text(160, 10, "Single Series Chart").attr(txtattr);
21461                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
21462                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
21463                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
21464                 
21465                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
21466                 r.barchart(330, 10, 300, 220, data1);
21467                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
21468                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
21469                 */
21470                 
21471                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
21472                 // r.barchart(30, 30, 560, 250,  xdata, {
21473                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
21474                 //     axis : "0 0 1 1",
21475                 //     axisxlabels :  xdata
21476                 //     //yvalues : cols,
21477                    
21478                 // });
21479 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
21480 //        
21481 //        this.load(null,xdata,{
21482 //                axis : "0 0 1 1",
21483 //                axisxlabels :  xdata
21484 //                });
21485
21486     },
21487
21488     load : function(graphtype,xdata,opts){
21489         this.raphael.clear();
21490         if(!graphtype) {
21491             graphtype = this.graphtype;
21492         }
21493         if(!opts){
21494             opts = this.opts;
21495         }
21496         var r = this.raphael,
21497             fin = function () {
21498                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
21499             },
21500             fout = function () {
21501                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
21502             },
21503             pfin = function() {
21504                 this.sector.stop();
21505                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
21506
21507                 if (this.label) {
21508                     this.label[0].stop();
21509                     this.label[0].attr({ r: 7.5 });
21510                     this.label[1].attr({ "font-weight": 800 });
21511                 }
21512             },
21513             pfout = function() {
21514                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
21515
21516                 if (this.label) {
21517                     this.label[0].animate({ r: 5 }, 500, "bounce");
21518                     this.label[1].attr({ "font-weight": 400 });
21519                 }
21520             };
21521
21522         switch(graphtype){
21523             case 'bar':
21524                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
21525                 break;
21526             case 'hbar':
21527                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
21528                 break;
21529             case 'pie':
21530 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
21531 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
21532 //            
21533                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
21534                 
21535                 break;
21536
21537         }
21538         
21539         if(this.title){
21540             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
21541         }
21542         
21543     },
21544     
21545     setTitle: function(o)
21546     {
21547         this.title = o;
21548     },
21549     
21550     initEvents: function() {
21551         
21552         if(!this.href){
21553             this.el.on('click', this.onClick, this);
21554         }
21555     },
21556     
21557     onClick : function(e)
21558     {
21559         Roo.log('img onclick');
21560         this.fireEvent('click', this, e);
21561     }
21562    
21563 });
21564
21565  
21566 /*
21567  * - LGPL
21568  *
21569  * numberBox
21570  * 
21571  */
21572 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21573
21574 /**
21575  * @class Roo.bootstrap.dash.NumberBox
21576  * @extends Roo.bootstrap.Component
21577  * Bootstrap NumberBox class
21578  * @cfg {String} headline Box headline
21579  * @cfg {String} content Box content
21580  * @cfg {String} icon Box icon
21581  * @cfg {String} footer Footer text
21582  * @cfg {String} fhref Footer href
21583  * 
21584  * @constructor
21585  * Create a new NumberBox
21586  * @param {Object} config The config object
21587  */
21588
21589
21590 Roo.bootstrap.dash.NumberBox = function(config){
21591     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
21592     
21593 };
21594
21595 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
21596     
21597     headline : '',
21598     content : '',
21599     icon : '',
21600     footer : '',
21601     fhref : '',
21602     ficon : '',
21603     
21604     getAutoCreate : function(){
21605         
21606         var cfg = {
21607             tag : 'div',
21608             cls : 'small-box ',
21609             cn : [
21610                 {
21611                     tag : 'div',
21612                     cls : 'inner',
21613                     cn :[
21614                         {
21615                             tag : 'h3',
21616                             cls : 'roo-headline',
21617                             html : this.headline
21618                         },
21619                         {
21620                             tag : 'p',
21621                             cls : 'roo-content',
21622                             html : this.content
21623                         }
21624                     ]
21625                 }
21626             ]
21627         }
21628         
21629         if(this.icon){
21630             cfg.cn.push({
21631                 tag : 'div',
21632                 cls : 'icon',
21633                 cn :[
21634                     {
21635                         tag : 'i',
21636                         cls : 'ion ' + this.icon
21637                     }
21638                 ]
21639             });
21640         }
21641         
21642         if(this.footer){
21643             var footer = {
21644                 tag : 'a',
21645                 cls : 'small-box-footer',
21646                 href : this.fhref || '#',
21647                 html : this.footer
21648             };
21649             
21650             cfg.cn.push(footer);
21651             
21652         }
21653         
21654         return  cfg;
21655     },
21656
21657     onRender : function(ct,position){
21658         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
21659
21660
21661        
21662                 
21663     },
21664
21665     setHeadline: function (value)
21666     {
21667         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
21668     },
21669     
21670     setFooter: function (value, href)
21671     {
21672         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
21673         
21674         if(href){
21675             this.el.select('a.small-box-footer',true).first().attr('href', href);
21676         }
21677         
21678     },
21679
21680     setContent: function (value)
21681     {
21682         this.el.select('.roo-content',true).first().dom.innerHTML = value;
21683     },
21684
21685     initEvents: function() 
21686     {   
21687         
21688     }
21689     
21690 });
21691
21692  
21693 /*
21694  * - LGPL
21695  *
21696  * TabBox
21697  * 
21698  */
21699 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21700
21701 /**
21702  * @class Roo.bootstrap.dash.TabBox
21703  * @extends Roo.bootstrap.Component
21704  * Bootstrap TabBox class
21705  * @cfg {String} title Title of the TabBox
21706  * @cfg {String} icon Icon of the TabBox
21707  * @cfg {Boolean} showtabs (true|false) show the tabs default true
21708  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
21709  * 
21710  * @constructor
21711  * Create a new TabBox
21712  * @param {Object} config The config object
21713  */
21714
21715
21716 Roo.bootstrap.dash.TabBox = function(config){
21717     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
21718     this.addEvents({
21719         // raw events
21720         /**
21721          * @event addpane
21722          * When a pane is added
21723          * @param {Roo.bootstrap.dash.TabPane} pane
21724          */
21725         "addpane" : true,
21726         /**
21727          * @event activatepane
21728          * When a pane is activated
21729          * @param {Roo.bootstrap.dash.TabPane} pane
21730          */
21731         "activatepane" : true
21732         
21733          
21734     });
21735     
21736     this.panes = [];
21737 };
21738
21739 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
21740
21741     title : '',
21742     icon : false,
21743     showtabs : true,
21744     tabScrollable : false,
21745     
21746     getChildContainer : function()
21747     {
21748         return this.el.select('.tab-content', true).first();
21749     },
21750     
21751     getAutoCreate : function(){
21752         
21753         var header = {
21754             tag: 'li',
21755             cls: 'pull-left header',
21756             html: this.title,
21757             cn : []
21758         };
21759         
21760         if(this.icon){
21761             header.cn.push({
21762                 tag: 'i',
21763                 cls: 'fa ' + this.icon
21764             });
21765         }
21766         
21767         var h = {
21768             tag: 'ul',
21769             cls: 'nav nav-tabs pull-right',
21770             cn: [
21771                 header
21772             ]
21773         };
21774         
21775         if(this.tabScrollable){
21776             h = {
21777                 tag: 'div',
21778                 cls: 'tab-header',
21779                 cn: [
21780                     {
21781                         tag: 'ul',
21782                         cls: 'nav nav-tabs pull-right',
21783                         cn: [
21784                             header
21785                         ]
21786                     }
21787                 ]
21788             }
21789         }
21790         
21791         var cfg = {
21792             tag: 'div',
21793             cls: 'nav-tabs-custom',
21794             cn: [
21795                 h,
21796                 {
21797                     tag: 'div',
21798                     cls: 'tab-content no-padding',
21799                     cn: []
21800                 }
21801             ]
21802         }
21803
21804         return  cfg;
21805     },
21806     initEvents : function()
21807     {
21808         //Roo.log('add add pane handler');
21809         this.on('addpane', this.onAddPane, this);
21810     },
21811      /**
21812      * Updates the box title
21813      * @param {String} html to set the title to.
21814      */
21815     setTitle : function(value)
21816     {
21817         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
21818     },
21819     onAddPane : function(pane)
21820     {
21821         this.panes.push(pane);
21822         //Roo.log('addpane');
21823         //Roo.log(pane);
21824         // tabs are rendere left to right..
21825         if(!this.showtabs){
21826             return;
21827         }
21828         
21829         var ctr = this.el.select('.nav-tabs', true).first();
21830          
21831          
21832         var existing = ctr.select('.nav-tab',true);
21833         var qty = existing.getCount();;
21834         
21835         
21836         var tab = ctr.createChild({
21837             tag : 'li',
21838             cls : 'nav-tab' + (qty ? '' : ' active'),
21839             cn : [
21840                 {
21841                     tag : 'a',
21842                     href:'#',
21843                     html : pane.title
21844                 }
21845             ]
21846         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
21847         pane.tab = tab;
21848         
21849         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
21850         if (!qty) {
21851             pane.el.addClass('active');
21852         }
21853         
21854                 
21855     },
21856     onTabClick : function(ev,un,ob,pane)
21857     {
21858         //Roo.log('tab - prev default');
21859         ev.preventDefault();
21860         
21861         
21862         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
21863         pane.tab.addClass('active');
21864         //Roo.log(pane.title);
21865         this.getChildContainer().select('.tab-pane',true).removeClass('active');
21866         // technically we should have a deactivate event.. but maybe add later.
21867         // and it should not de-activate the selected tab...
21868         this.fireEvent('activatepane', pane);
21869         pane.el.addClass('active');
21870         pane.fireEvent('activate');
21871         
21872         
21873     },
21874     
21875     getActivePane : function()
21876     {
21877         var r = false;
21878         Roo.each(this.panes, function(p) {
21879             if(p.el.hasClass('active')){
21880                 r = p;
21881                 return false;
21882             }
21883             
21884             return;
21885         });
21886         
21887         return r;
21888     }
21889     
21890     
21891 });
21892
21893  
21894 /*
21895  * - LGPL
21896  *
21897  * Tab pane
21898  * 
21899  */
21900 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21901 /**
21902  * @class Roo.bootstrap.TabPane
21903  * @extends Roo.bootstrap.Component
21904  * Bootstrap TabPane class
21905  * @cfg {Boolean} active (false | true) Default false
21906  * @cfg {String} title title of panel
21907
21908  * 
21909  * @constructor
21910  * Create a new TabPane
21911  * @param {Object} config The config object
21912  */
21913
21914 Roo.bootstrap.dash.TabPane = function(config){
21915     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
21916     
21917     this.addEvents({
21918         // raw events
21919         /**
21920          * @event activate
21921          * When a pane is activated
21922          * @param {Roo.bootstrap.dash.TabPane} pane
21923          */
21924         "activate" : true
21925          
21926     });
21927 };
21928
21929 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
21930     
21931     active : false,
21932     title : '',
21933     
21934     // the tabBox that this is attached to.
21935     tab : false,
21936      
21937     getAutoCreate : function() 
21938     {
21939         var cfg = {
21940             tag: 'div',
21941             cls: 'tab-pane'
21942         }
21943         
21944         if(this.active){
21945             cfg.cls += ' active';
21946         }
21947         
21948         return cfg;
21949     },
21950     initEvents  : function()
21951     {
21952         //Roo.log('trigger add pane handler');
21953         this.parent().fireEvent('addpane', this)
21954     },
21955     
21956      /**
21957      * Updates the tab title 
21958      * @param {String} html to set the title to.
21959      */
21960     setTitle: function(str)
21961     {
21962         if (!this.tab) {
21963             return;
21964         }
21965         this.title = str;
21966         this.tab.select('a', true).first().dom.innerHTML = str;
21967         
21968     }
21969     
21970     
21971     
21972 });
21973
21974  
21975
21976
21977  /*
21978  * - LGPL
21979  *
21980  * menu
21981  * 
21982  */
21983 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
21984
21985 /**
21986  * @class Roo.bootstrap.menu.Menu
21987  * @extends Roo.bootstrap.Component
21988  * Bootstrap Menu class - container for Menu
21989  * @cfg {String} html Text of the menu
21990  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
21991  * @cfg {String} icon Font awesome icon
21992  * @cfg {String} pos Menu align to (top | bottom) default bottom
21993  * 
21994  * 
21995  * @constructor
21996  * Create a new Menu
21997  * @param {Object} config The config object
21998  */
21999
22000
22001 Roo.bootstrap.menu.Menu = function(config){
22002     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
22003     
22004     this.addEvents({
22005         /**
22006          * @event beforeshow
22007          * Fires before this menu is displayed
22008          * @param {Roo.bootstrap.menu.Menu} this
22009          */
22010         beforeshow : true,
22011         /**
22012          * @event beforehide
22013          * Fires before this menu is hidden
22014          * @param {Roo.bootstrap.menu.Menu} this
22015          */
22016         beforehide : true,
22017         /**
22018          * @event show
22019          * Fires after this menu is displayed
22020          * @param {Roo.bootstrap.menu.Menu} this
22021          */
22022         show : true,
22023         /**
22024          * @event hide
22025          * Fires after this menu is hidden
22026          * @param {Roo.bootstrap.menu.Menu} this
22027          */
22028         hide : true,
22029         /**
22030          * @event click
22031          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
22032          * @param {Roo.bootstrap.menu.Menu} this
22033          * @param {Roo.EventObject} e
22034          */
22035         click : true
22036     });
22037     
22038 };
22039
22040 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
22041     
22042     submenu : false,
22043     html : '',
22044     weight : 'default',
22045     icon : false,
22046     pos : 'bottom',
22047     
22048     
22049     getChildContainer : function() {
22050         if(this.isSubMenu){
22051             return this.el;
22052         }
22053         
22054         return this.el.select('ul.dropdown-menu', true).first();  
22055     },
22056     
22057     getAutoCreate : function()
22058     {
22059         var text = [
22060             {
22061                 tag : 'span',
22062                 cls : 'roo-menu-text',
22063                 html : this.html
22064             }
22065         ];
22066         
22067         if(this.icon){
22068             text.unshift({
22069                 tag : 'i',
22070                 cls : 'fa ' + this.icon
22071             })
22072         }
22073         
22074         
22075         var cfg = {
22076             tag : 'div',
22077             cls : 'btn-group',
22078             cn : [
22079                 {
22080                     tag : 'button',
22081                     cls : 'dropdown-button btn btn-' + this.weight,
22082                     cn : text
22083                 },
22084                 {
22085                     tag : 'button',
22086                     cls : 'dropdown-toggle btn btn-' + this.weight,
22087                     cn : [
22088                         {
22089                             tag : 'span',
22090                             cls : 'caret'
22091                         }
22092                     ]
22093                 },
22094                 {
22095                     tag : 'ul',
22096                     cls : 'dropdown-menu'
22097                 }
22098             ]
22099             
22100         };
22101         
22102         if(this.pos == 'top'){
22103             cfg.cls += ' dropup';
22104         }
22105         
22106         if(this.isSubMenu){
22107             cfg = {
22108                 tag : 'ul',
22109                 cls : 'dropdown-menu'
22110             }
22111         }
22112         
22113         return cfg;
22114     },
22115     
22116     onRender : function(ct, position)
22117     {
22118         this.isSubMenu = ct.hasClass('dropdown-submenu');
22119         
22120         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
22121     },
22122     
22123     initEvents : function() 
22124     {
22125         if(this.isSubMenu){
22126             return;
22127         }
22128         
22129         this.hidden = true;
22130         
22131         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
22132         this.triggerEl.on('click', this.onTriggerPress, this);
22133         
22134         this.buttonEl = this.el.select('button.dropdown-button', true).first();
22135         this.buttonEl.on('click', this.onClick, this);
22136         
22137     },
22138     
22139     list : function()
22140     {
22141         if(this.isSubMenu){
22142             return this.el;
22143         }
22144         
22145         return this.el.select('ul.dropdown-menu', true).first();
22146     },
22147     
22148     onClick : function(e)
22149     {
22150         this.fireEvent("click", this, e);
22151     },
22152     
22153     onTriggerPress  : function(e)
22154     {   
22155         if (this.isVisible()) {
22156             this.hide();
22157         } else {
22158             this.show();
22159         }
22160     },
22161     
22162     isVisible : function(){
22163         return !this.hidden;
22164     },
22165     
22166     show : function()
22167     {
22168         this.fireEvent("beforeshow", this);
22169         
22170         this.hidden = false;
22171         this.el.addClass('open');
22172         
22173         Roo.get(document).on("mouseup", this.onMouseUp, this);
22174         
22175         this.fireEvent("show", this);
22176         
22177         
22178     },
22179     
22180     hide : function()
22181     {
22182         this.fireEvent("beforehide", this);
22183         
22184         this.hidden = true;
22185         this.el.removeClass('open');
22186         
22187         Roo.get(document).un("mouseup", this.onMouseUp);
22188         
22189         this.fireEvent("hide", this);
22190     },
22191     
22192     onMouseUp : function()
22193     {
22194         this.hide();
22195     }
22196     
22197 });
22198
22199  
22200  /*
22201  * - LGPL
22202  *
22203  * menu item
22204  * 
22205  */
22206 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22207
22208 /**
22209  * @class Roo.bootstrap.menu.Item
22210  * @extends Roo.bootstrap.Component
22211  * Bootstrap MenuItem class
22212  * @cfg {Boolean} submenu (true | false) default false
22213  * @cfg {String} html text of the item
22214  * @cfg {String} href the link
22215  * @cfg {Boolean} disable (true | false) default false
22216  * @cfg {Boolean} preventDefault (true | false) default true
22217  * @cfg {String} icon Font awesome icon
22218  * @cfg {String} pos Submenu align to (left | right) default right 
22219  * 
22220  * 
22221  * @constructor
22222  * Create a new Item
22223  * @param {Object} config The config object
22224  */
22225
22226
22227 Roo.bootstrap.menu.Item = function(config){
22228     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
22229     this.addEvents({
22230         /**
22231          * @event mouseover
22232          * Fires when the mouse is hovering over this menu
22233          * @param {Roo.bootstrap.menu.Item} this
22234          * @param {Roo.EventObject} e
22235          */
22236         mouseover : true,
22237         /**
22238          * @event mouseout
22239          * Fires when the mouse exits this menu
22240          * @param {Roo.bootstrap.menu.Item} this
22241          * @param {Roo.EventObject} e
22242          */
22243         mouseout : true,
22244         // raw events
22245         /**
22246          * @event click
22247          * The raw click event for the entire grid.
22248          * @param {Roo.EventObject} e
22249          */
22250         click : true
22251     });
22252 };
22253
22254 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
22255     
22256     submenu : false,
22257     href : '',
22258     html : '',
22259     preventDefault: true,
22260     disable : false,
22261     icon : false,
22262     pos : 'right',
22263     
22264     getAutoCreate : function()
22265     {
22266         var text = [
22267             {
22268                 tag : 'span',
22269                 cls : 'roo-menu-item-text',
22270                 html : this.html
22271             }
22272         ];
22273         
22274         if(this.icon){
22275             text.unshift({
22276                 tag : 'i',
22277                 cls : 'fa ' + this.icon
22278             })
22279         }
22280         
22281         var cfg = {
22282             tag : 'li',
22283             cn : [
22284                 {
22285                     tag : 'a',
22286                     href : this.href || '#',
22287                     cn : text
22288                 }
22289             ]
22290         };
22291         
22292         if(this.disable){
22293             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
22294         }
22295         
22296         if(this.submenu){
22297             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
22298             
22299             if(this.pos == 'left'){
22300                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
22301             }
22302         }
22303         
22304         return cfg;
22305     },
22306     
22307     initEvents : function() 
22308     {
22309         this.el.on('mouseover', this.onMouseOver, this);
22310         this.el.on('mouseout', this.onMouseOut, this);
22311         
22312         this.el.select('a', true).first().on('click', this.onClick, this);
22313         
22314     },
22315     
22316     onClick : function(e)
22317     {
22318         if(this.preventDefault){
22319             e.preventDefault();
22320         }
22321         
22322         this.fireEvent("click", this, e);
22323     },
22324     
22325     onMouseOver : function(e)
22326     {
22327         if(this.submenu && this.pos == 'left'){
22328             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
22329         }
22330         
22331         this.fireEvent("mouseover", this, e);
22332     },
22333     
22334     onMouseOut : function(e)
22335     {
22336         this.fireEvent("mouseout", this, e);
22337     }
22338 });
22339
22340  
22341
22342  /*
22343  * - LGPL
22344  *
22345  * menu separator
22346  * 
22347  */
22348 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22349
22350 /**
22351  * @class Roo.bootstrap.menu.Separator
22352  * @extends Roo.bootstrap.Component
22353  * Bootstrap Separator class
22354  * 
22355  * @constructor
22356  * Create a new Separator
22357  * @param {Object} config The config object
22358  */
22359
22360
22361 Roo.bootstrap.menu.Separator = function(config){
22362     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
22363 };
22364
22365 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
22366     
22367     getAutoCreate : function(){
22368         var cfg = {
22369             tag : 'li',
22370             cls: 'divider'
22371         };
22372         
22373         return cfg;
22374     }
22375    
22376 });
22377
22378  
22379
22380  /*
22381  * - LGPL
22382  *
22383  * Tooltip
22384  * 
22385  */
22386
22387 /**
22388  * @class Roo.bootstrap.Tooltip
22389  * Bootstrap Tooltip class
22390  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
22391  * to determine which dom element triggers the tooltip.
22392  * 
22393  * It needs to add support for additional attributes like tooltip-position
22394  * 
22395  * @constructor
22396  * Create a new Toolti
22397  * @param {Object} config The config object
22398  */
22399
22400 Roo.bootstrap.Tooltip = function(config){
22401     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
22402 };
22403
22404 Roo.apply(Roo.bootstrap.Tooltip, {
22405     /**
22406      * @function init initialize tooltip monitoring.
22407      * @static
22408      */
22409     currentEl : false,
22410     currentTip : false,
22411     currentRegion : false,
22412     
22413     //  init : delay?
22414     
22415     init : function()
22416     {
22417         Roo.get(document).on('mouseover', this.enter ,this);
22418         Roo.get(document).on('mouseout', this.leave, this);
22419          
22420         
22421         this.currentTip = new Roo.bootstrap.Tooltip();
22422     },
22423     
22424     enter : function(ev)
22425     {
22426         var dom = ev.getTarget();
22427         
22428         //Roo.log(['enter',dom]);
22429         var el = Roo.fly(dom);
22430         if (this.currentEl) {
22431             //Roo.log(dom);
22432             //Roo.log(this.currentEl);
22433             //Roo.log(this.currentEl.contains(dom));
22434             if (this.currentEl == el) {
22435                 return;
22436             }
22437             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
22438                 return;
22439             }
22440
22441         }
22442         
22443         
22444         
22445         if (this.currentTip.el) {
22446             this.currentTip.el.hide(); // force hiding...
22447         }    
22448         //Roo.log(ev);
22449         var bindEl = el;
22450         
22451         // you can not look for children, as if el is the body.. then everythign is the child..
22452         if (!el.attr('tooltip')) { //
22453             if (!el.select("[tooltip]").elements.length) {
22454                 return;
22455             }
22456             // is the mouse over this child...?
22457             bindEl = el.select("[tooltip]").first();
22458             var xy = ev.getXY();
22459             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
22460                 //Roo.log("not in region.");
22461                 return;
22462             }
22463             //Roo.log("child element over..");
22464             
22465         }
22466         this.currentEl = bindEl;
22467         this.currentTip.bind(bindEl);
22468         this.currentRegion = Roo.lib.Region.getRegion(dom);
22469         this.currentTip.enter();
22470         
22471     },
22472     leave : function(ev)
22473     {
22474         var dom = ev.getTarget();
22475         //Roo.log(['leave',dom]);
22476         if (!this.currentEl) {
22477             return;
22478         }
22479         
22480         
22481         if (dom != this.currentEl.dom) {
22482             return;
22483         }
22484         var xy = ev.getXY();
22485         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
22486             return;
22487         }
22488         // only activate leave if mouse cursor is outside... bounding box..
22489         
22490         
22491         
22492         
22493         if (this.currentTip) {
22494             this.currentTip.leave();
22495         }
22496         //Roo.log('clear currentEl');
22497         this.currentEl = false;
22498         
22499         
22500     },
22501     alignment : {
22502         'left' : ['r-l', [-2,0], 'right'],
22503         'right' : ['l-r', [2,0], 'left'],
22504         'bottom' : ['t-b', [0,2], 'top'],
22505         'top' : [ 'b-t', [0,-2], 'bottom']
22506     }
22507     
22508 });
22509
22510
22511 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
22512     
22513     
22514     bindEl : false,
22515     
22516     delay : null, // can be { show : 300 , hide: 500}
22517     
22518     timeout : null,
22519     
22520     hoverState : null, //???
22521     
22522     placement : 'bottom', 
22523     
22524     getAutoCreate : function(){
22525     
22526         var cfg = {
22527            cls : 'tooltip',
22528            role : 'tooltip',
22529            cn : [
22530                 {
22531                     cls : 'tooltip-arrow'
22532                 },
22533                 {
22534                     cls : 'tooltip-inner'
22535                 }
22536            ]
22537         };
22538         
22539         return cfg;
22540     },
22541     bind : function(el)
22542     {
22543         this.bindEl = el;
22544     },
22545       
22546     
22547     enter : function () {
22548        
22549         if (this.timeout != null) {
22550             clearTimeout(this.timeout);
22551         }
22552         
22553         this.hoverState = 'in';
22554          //Roo.log("enter - show");
22555         if (!this.delay || !this.delay.show) {
22556             this.show();
22557             return;
22558         }
22559         var _t = this;
22560         this.timeout = setTimeout(function () {
22561             if (_t.hoverState == 'in') {
22562                 _t.show();
22563             }
22564         }, this.delay.show);
22565     },
22566     leave : function()
22567     {
22568         clearTimeout(this.timeout);
22569     
22570         this.hoverState = 'out';
22571          if (!this.delay || !this.delay.hide) {
22572             this.hide();
22573             return;
22574         }
22575        
22576         var _t = this;
22577         this.timeout = setTimeout(function () {
22578             //Roo.log("leave - timeout");
22579             
22580             if (_t.hoverState == 'out') {
22581                 _t.hide();
22582                 Roo.bootstrap.Tooltip.currentEl = false;
22583             }
22584         }, delay);
22585     },
22586     
22587     show : function ()
22588     {
22589         if (!this.el) {
22590             this.render(document.body);
22591         }
22592         // set content.
22593         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
22594         
22595         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
22596         
22597         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
22598         
22599         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
22600         
22601         var placement = typeof this.placement == 'function' ?
22602             this.placement.call(this, this.el, on_el) :
22603             this.placement;
22604             
22605         var autoToken = /\s?auto?\s?/i;
22606         var autoPlace = autoToken.test(placement);
22607         if (autoPlace) {
22608             placement = placement.replace(autoToken, '') || 'top';
22609         }
22610         
22611         //this.el.detach()
22612         //this.el.setXY([0,0]);
22613         this.el.show();
22614         //this.el.dom.style.display='block';
22615         this.el.addClass(placement);
22616         
22617         //this.el.appendTo(on_el);
22618         
22619         var p = this.getPosition();
22620         var box = this.el.getBox();
22621         
22622         if (autoPlace) {
22623             // fixme..
22624         }
22625         var align = Roo.bootstrap.Tooltip.alignment[placement];
22626         this.el.alignTo(this.bindEl, align[0],align[1]);
22627         //var arrow = this.el.select('.arrow',true).first();
22628         //arrow.set(align[2], 
22629         
22630         this.el.addClass('in fade');
22631         this.hoverState = null;
22632         
22633         if (this.el.hasClass('fade')) {
22634             // fade it?
22635         }
22636         
22637     },
22638     hide : function()
22639     {
22640          
22641         if (!this.el) {
22642             return;
22643         }
22644         //this.el.setXY([0,0]);
22645         this.el.removeClass('in');
22646         //this.el.hide();
22647         
22648     }
22649     
22650 });
22651  
22652
22653  /*
22654  * - LGPL
22655  *
22656  * Location Picker
22657  * 
22658  */
22659
22660 /**
22661  * @class Roo.bootstrap.LocationPicker
22662  * @extends Roo.bootstrap.Component
22663  * Bootstrap LocationPicker class
22664  * @cfg {Number} latitude Position when init default 0
22665  * @cfg {Number} longitude Position when init default 0
22666  * @cfg {Number} zoom default 15
22667  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
22668  * @cfg {Boolean} mapTypeControl default false
22669  * @cfg {Boolean} disableDoubleClickZoom default false
22670  * @cfg {Boolean} scrollwheel default true
22671  * @cfg {Boolean} streetViewControl default false
22672  * @cfg {Number} radius default 0
22673  * @cfg {String} locationName
22674  * @cfg {Boolean} draggable default true
22675  * @cfg {Boolean} enableAutocomplete default false
22676  * @cfg {Boolean} enableReverseGeocode default true
22677  * @cfg {String} markerTitle
22678  * 
22679  * @constructor
22680  * Create a new LocationPicker
22681  * @param {Object} config The config object
22682  */
22683
22684
22685 Roo.bootstrap.LocationPicker = function(config){
22686     
22687     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
22688     
22689     this.addEvents({
22690         /**
22691          * @event initial
22692          * Fires when the picker initialized.
22693          * @param {Roo.bootstrap.LocationPicker} this
22694          * @param {Google Location} location
22695          */
22696         initial : true,
22697         /**
22698          * @event positionchanged
22699          * Fires when the picker position changed.
22700          * @param {Roo.bootstrap.LocationPicker} this
22701          * @param {Google Location} location
22702          */
22703         positionchanged : true,
22704         /**
22705          * @event resize
22706          * Fires when the map resize.
22707          * @param {Roo.bootstrap.LocationPicker} this
22708          */
22709         resize : true,
22710         /**
22711          * @event show
22712          * Fires when the map show.
22713          * @param {Roo.bootstrap.LocationPicker} this
22714          */
22715         show : true,
22716         /**
22717          * @event hide
22718          * Fires when the map hide.
22719          * @param {Roo.bootstrap.LocationPicker} this
22720          */
22721         hide : true,
22722         /**
22723          * @event mapClick
22724          * Fires when click the map.
22725          * @param {Roo.bootstrap.LocationPicker} this
22726          * @param {Map event} e
22727          */
22728         mapClick : true,
22729         /**
22730          * @event mapRightClick
22731          * Fires when right click the map.
22732          * @param {Roo.bootstrap.LocationPicker} this
22733          * @param {Map event} e
22734          */
22735         mapRightClick : true,
22736         /**
22737          * @event markerClick
22738          * Fires when click the marker.
22739          * @param {Roo.bootstrap.LocationPicker} this
22740          * @param {Map event} e
22741          */
22742         markerClick : true,
22743         /**
22744          * @event markerRightClick
22745          * Fires when right click the marker.
22746          * @param {Roo.bootstrap.LocationPicker} this
22747          * @param {Map event} e
22748          */
22749         markerRightClick : true,
22750         /**
22751          * @event OverlayViewDraw
22752          * Fires when OverlayView Draw
22753          * @param {Roo.bootstrap.LocationPicker} this
22754          */
22755         OverlayViewDraw : true,
22756         /**
22757          * @event OverlayViewOnAdd
22758          * Fires when OverlayView Draw
22759          * @param {Roo.bootstrap.LocationPicker} this
22760          */
22761         OverlayViewOnAdd : true,
22762         /**
22763          * @event OverlayViewOnRemove
22764          * Fires when OverlayView Draw
22765          * @param {Roo.bootstrap.LocationPicker} this
22766          */
22767         OverlayViewOnRemove : true,
22768         /**
22769          * @event OverlayViewShow
22770          * Fires when OverlayView Draw
22771          * @param {Roo.bootstrap.LocationPicker} this
22772          * @param {Pixel} cpx
22773          */
22774         OverlayViewShow : true,
22775         /**
22776          * @event OverlayViewHide
22777          * Fires when OverlayView Draw
22778          * @param {Roo.bootstrap.LocationPicker} this
22779          */
22780         OverlayViewHide : true
22781     });
22782         
22783 };
22784
22785 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
22786     
22787     gMapContext: false,
22788     
22789     latitude: 0,
22790     longitude: 0,
22791     zoom: 15,
22792     mapTypeId: false,
22793     mapTypeControl: false,
22794     disableDoubleClickZoom: false,
22795     scrollwheel: true,
22796     streetViewControl: false,
22797     radius: 0,
22798     locationName: '',
22799     draggable: true,
22800     enableAutocomplete: false,
22801     enableReverseGeocode: true,
22802     markerTitle: '',
22803     
22804     getAutoCreate: function()
22805     {
22806
22807         var cfg = {
22808             tag: 'div',
22809             cls: 'roo-location-picker'
22810         };
22811         
22812         return cfg
22813     },
22814     
22815     initEvents: function(ct, position)
22816     {       
22817         if(!this.el.getWidth() || this.isApplied()){
22818             return;
22819         }
22820         
22821         this.el.setVisibilityMode(Roo.Element.DISPLAY);
22822         
22823         this.initial();
22824     },
22825     
22826     initial: function()
22827     {
22828         if(!this.mapTypeId){
22829             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
22830         }
22831         
22832         this.gMapContext = this.GMapContext();
22833         
22834         this.initOverlayView();
22835         
22836         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
22837         
22838         var _this = this;
22839                 
22840         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
22841             _this.setPosition(_this.gMapContext.marker.position);
22842         });
22843         
22844         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
22845             _this.fireEvent('mapClick', this, event);
22846             
22847         });
22848
22849         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
22850             _this.fireEvent('mapRightClick', this, event);
22851             
22852         });
22853         
22854         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
22855             _this.fireEvent('markerClick', this, event);
22856             
22857         });
22858
22859         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
22860             _this.fireEvent('markerRightClick', this, event);
22861             
22862         });
22863         
22864         this.setPosition(this.gMapContext.location);
22865         
22866         this.fireEvent('initial', this, this.gMapContext.location);
22867     },
22868     
22869     initOverlayView: function()
22870     {
22871         var _this = this;
22872         
22873         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
22874             
22875             draw: function()
22876             {
22877                 _this.fireEvent('OverlayViewDraw', _this);
22878             },
22879             
22880             onAdd: function()
22881             {
22882                 _this.fireEvent('OverlayViewOnAdd', _this);
22883             },
22884             
22885             onRemove: function()
22886             {
22887                 _this.fireEvent('OverlayViewOnRemove', _this);
22888             },
22889             
22890             show: function(cpx)
22891             {
22892                 _this.fireEvent('OverlayViewShow', _this, cpx);
22893             },
22894             
22895             hide: function()
22896             {
22897                 _this.fireEvent('OverlayViewHide', _this);
22898             }
22899             
22900         });
22901     },
22902     
22903     fromLatLngToContainerPixel: function(event)
22904     {
22905         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
22906     },
22907     
22908     isApplied: function() 
22909     {
22910         return this.getGmapContext() == false ? false : true;
22911     },
22912     
22913     getGmapContext: function() 
22914     {
22915         return this.gMapContext
22916     },
22917     
22918     GMapContext: function() 
22919     {
22920         var position = new google.maps.LatLng(this.latitude, this.longitude);
22921         
22922         var _map = new google.maps.Map(this.el.dom, {
22923             center: position,
22924             zoom: this.zoom,
22925             mapTypeId: this.mapTypeId,
22926             mapTypeControl: this.mapTypeControl,
22927             disableDoubleClickZoom: this.disableDoubleClickZoom,
22928             scrollwheel: this.scrollwheel,
22929             streetViewControl: this.streetViewControl,
22930             locationName: this.locationName,
22931             draggable: this.draggable,
22932             enableAutocomplete: this.enableAutocomplete,
22933             enableReverseGeocode: this.enableReverseGeocode
22934         });
22935         
22936         var _marker = new google.maps.Marker({
22937             position: position,
22938             map: _map,
22939             title: this.markerTitle,
22940             draggable: this.draggable
22941         });
22942         
22943         return {
22944             map: _map,
22945             marker: _marker,
22946             circle: null,
22947             location: position,
22948             radius: this.radius,
22949             locationName: this.locationName,
22950             addressComponents: {
22951                 formatted_address: null,
22952                 addressLine1: null,
22953                 addressLine2: null,
22954                 streetName: null,
22955                 streetNumber: null,
22956                 city: null,
22957                 district: null,
22958                 state: null,
22959                 stateOrProvince: null
22960             },
22961             settings: this,
22962             domContainer: this.el.dom,
22963             geodecoder: new google.maps.Geocoder()
22964         };
22965     },
22966     
22967     drawCircle: function(center, radius, options) 
22968     {
22969         if (this.gMapContext.circle != null) {
22970             this.gMapContext.circle.setMap(null);
22971         }
22972         if (radius > 0) {
22973             radius *= 1;
22974             options = Roo.apply({}, options, {
22975                 strokeColor: "#0000FF",
22976                 strokeOpacity: .35,
22977                 strokeWeight: 2,
22978                 fillColor: "#0000FF",
22979                 fillOpacity: .2
22980             });
22981             
22982             options.map = this.gMapContext.map;
22983             options.radius = radius;
22984             options.center = center;
22985             this.gMapContext.circle = new google.maps.Circle(options);
22986             return this.gMapContext.circle;
22987         }
22988         
22989         return null;
22990     },
22991     
22992     setPosition: function(location) 
22993     {
22994         this.gMapContext.location = location;
22995         this.gMapContext.marker.setPosition(location);
22996         this.gMapContext.map.panTo(location);
22997         this.drawCircle(location, this.gMapContext.radius, {});
22998         
22999         var _this = this;
23000         
23001         if (this.gMapContext.settings.enableReverseGeocode) {
23002             this.gMapContext.geodecoder.geocode({
23003                 latLng: this.gMapContext.location
23004             }, function(results, status) {
23005                 
23006                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
23007                     _this.gMapContext.locationName = results[0].formatted_address;
23008                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
23009                     
23010                     _this.fireEvent('positionchanged', this, location);
23011                 }
23012             });
23013             
23014             return;
23015         }
23016         
23017         this.fireEvent('positionchanged', this, location);
23018     },
23019     
23020     resize: function()
23021     {
23022         google.maps.event.trigger(this.gMapContext.map, "resize");
23023         
23024         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
23025         
23026         this.fireEvent('resize', this);
23027     },
23028     
23029     setPositionByLatLng: function(latitude, longitude)
23030     {
23031         this.setPosition(new google.maps.LatLng(latitude, longitude));
23032     },
23033     
23034     getCurrentPosition: function() 
23035     {
23036         return {
23037             latitude: this.gMapContext.location.lat(),
23038             longitude: this.gMapContext.location.lng()
23039         };
23040     },
23041     
23042     getAddressName: function() 
23043     {
23044         return this.gMapContext.locationName;
23045     },
23046     
23047     getAddressComponents: function() 
23048     {
23049         return this.gMapContext.addressComponents;
23050     },
23051     
23052     address_component_from_google_geocode: function(address_components) 
23053     {
23054         var result = {};
23055         
23056         for (var i = 0; i < address_components.length; i++) {
23057             var component = address_components[i];
23058             if (component.types.indexOf("postal_code") >= 0) {
23059                 result.postalCode = component.short_name;
23060             } else if (component.types.indexOf("street_number") >= 0) {
23061                 result.streetNumber = component.short_name;
23062             } else if (component.types.indexOf("route") >= 0) {
23063                 result.streetName = component.short_name;
23064             } else if (component.types.indexOf("neighborhood") >= 0) {
23065                 result.city = component.short_name;
23066             } else if (component.types.indexOf("locality") >= 0) {
23067                 result.city = component.short_name;
23068             } else if (component.types.indexOf("sublocality") >= 0) {
23069                 result.district = component.short_name;
23070             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
23071                 result.stateOrProvince = component.short_name;
23072             } else if (component.types.indexOf("country") >= 0) {
23073                 result.country = component.short_name;
23074             }
23075         }
23076         
23077         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
23078         result.addressLine2 = "";
23079         return result;
23080     },
23081     
23082     setZoomLevel: function(zoom)
23083     {
23084         this.gMapContext.map.setZoom(zoom);
23085     },
23086     
23087     show: function()
23088     {
23089         if(!this.el){
23090             return;
23091         }
23092         
23093         this.el.show();
23094         
23095         this.resize();
23096         
23097         this.fireEvent('show', this);
23098     },
23099     
23100     hide: function()
23101     {
23102         if(!this.el){
23103             return;
23104         }
23105         
23106         this.el.hide();
23107         
23108         this.fireEvent('hide', this);
23109     }
23110     
23111 });
23112
23113 Roo.apply(Roo.bootstrap.LocationPicker, {
23114     
23115     OverlayView : function(map, options)
23116     {
23117         options = options || {};
23118         
23119         this.setMap(map);
23120     }
23121     
23122     
23123 });/*
23124  * - LGPL
23125  *
23126  * Alert
23127  * 
23128  */
23129
23130 /**
23131  * @class Roo.bootstrap.Alert
23132  * @extends Roo.bootstrap.Component
23133  * Bootstrap Alert class
23134  * @cfg {String} title The title of alert
23135  * @cfg {String} html The content of alert
23136  * @cfg {String} weight (  success | info | warning | danger )
23137  * @cfg {String} faicon font-awesomeicon
23138  * 
23139  * @constructor
23140  * Create a new alert
23141  * @param {Object} config The config object
23142  */
23143
23144
23145 Roo.bootstrap.Alert = function(config){
23146     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
23147     
23148 };
23149
23150 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
23151     
23152     title: '',
23153     html: '',
23154     weight: false,
23155     faicon: false,
23156     
23157     getAutoCreate : function()
23158     {
23159         
23160         var cfg = {
23161             tag : 'div',
23162             cls : 'alert',
23163             cn : [
23164                 {
23165                     tag : 'i',
23166                     cls : 'roo-alert-icon'
23167                     
23168                 },
23169                 {
23170                     tag : 'b',
23171                     cls : 'roo-alert-title',
23172                     html : this.title
23173                 },
23174                 {
23175                     tag : 'span',
23176                     cls : 'roo-alert-text',
23177                     html : this.html
23178                 }
23179             ]
23180         };
23181         
23182         if(this.faicon){
23183             cfg.cn[0].cls += ' fa ' + this.faicon;
23184         }
23185         
23186         if(this.weight){
23187             cfg.cls += ' alert-' + this.weight;
23188         }
23189         
23190         return cfg;
23191     },
23192     
23193     initEvents: function() 
23194     {
23195         this.el.setVisibilityMode(Roo.Element.DISPLAY);
23196     },
23197     
23198     setTitle : function(str)
23199     {
23200         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
23201     },
23202     
23203     setText : function(str)
23204     {
23205         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
23206     },
23207     
23208     setWeight : function(weight)
23209     {
23210         if(this.weight){
23211             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
23212         }
23213         
23214         this.weight = weight;
23215         
23216         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
23217     },
23218     
23219     setIcon : function(icon)
23220     {
23221         if(this.faicon){
23222             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
23223         }
23224         
23225         this.faicon = icon
23226         
23227         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
23228     },
23229     
23230     hide: function() 
23231     {
23232         this.el.hide();   
23233     },
23234     
23235     show: function() 
23236     {  
23237         this.el.show();   
23238     }
23239     
23240 });
23241
23242